001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.cnd.test.base;
042:
043: import javax.swing.event.ChangeEvent;
044: import javax.swing.event.ChangeListener;
045: import javax.swing.event.DocumentEvent;
046: import javax.swing.event.DocumentListener;
047: import javax.swing.event.EventListenerList;
048: import javax.swing.text.BadLocationException;
049: import javax.swing.text.Caret;
050: import javax.swing.text.Document;
051: import javax.swing.text.EditorKit;
052: import org.netbeans.editor.BaseDocument;
053: import org.netbeans.modules.cnd.test.BaseTestCase;
054:
055: /**
056: * Vladimir Voskresensky copied this class to prevent dependency on editor tests
057: *
058: * Support for creation of unit tests working with document.
059: *
060: * @author Miloslav Metelka
061: */
062: public abstract class BaseDocumentUnitTestCase extends BaseTestCase {
063:
064: private EditorKit editorKit;
065:
066: private String loadDocumentText;
067:
068: private BaseDocument doc;
069:
070: private Caret caret;
071:
072: private int loadCaretOffset = -1;
073:
074: public BaseDocumentUnitTestCase(String testMethodName) {
075: super (testMethodName);
076:
077: }
078:
079: /**
080: * Set the text that the document obtained by {@link #getDocument()}
081: * would be loaded with.
082: *
083: * <p>
084: * The text is parsed and the first occurrence of "|" sets
085: * the caret offset which is available by {@link #getLoadCaretOffset()}.
086: * <br>
087: * The "|" itself is removed from the document text and subsequent
088: * calls to {@link #getLoadDocumentText()} do not contain it.
089: */
090: protected void setLoadDocumentText(String loadDocumentText) {
091: // [TODO] a more elaborated support could be done e.g. "||" to a real "|" etc.
092: loadCaretOffset = loadDocumentText.indexOf('|');
093: if (loadCaretOffset != -1) {
094: loadDocumentText = loadDocumentText.substring(0,
095: loadCaretOffset)
096: + loadDocumentText.substring(loadCaretOffset + 1);
097: }
098:
099: this .loadDocumentText = loadDocumentText;
100: }
101:
102: /**
103: * Get the text that the document obtained by {@link #getDocument()}
104: * would be loaded with.
105: *
106: * @return text to be loaded into the document or null if nothing
107: * should be loaded.
108: */
109: protected final String getLoadDocumentText() {
110: return loadDocumentText;
111: }
112:
113: /**
114: * Return caret offset based on the scanning of the text passed
115: * to {@link #setLoadDocumentText(String)}.
116: *
117: * @return valid caret offset or -1 if no caret offset was determined
118: * in the document text.
119: */
120: protected final int getLoadCaretOffset() {
121: return loadCaretOffset;
122: }
123:
124: /**
125: * Return the caret instance that can be used for testing.
126: * <br>
127: * The caret listens on document changes and its initial
128: * position is set to {@link #getLoadCaretOffset()}.
129: *
130: * @return caret instance.
131: */
132: protected synchronized final Caret getCaret() {
133: if (caret == null) {
134: caret = new CaretImpl(getDocument(), loadCaretOffset);
135: }
136: return caret;
137: }
138:
139: /**
140: * Get the offset of where the caret resides in the document.
141: */
142: protected final int getCaretOffset() {
143: return getCaret().getDot();
144: }
145:
146: /**
147: * Get the document that the test should work with.
148: * <br>
149: * If the document does not exist yet it will be created
150: * and loaded with the text from {@link #getLoadDocumentText()}.
151: */
152: protected synchronized BaseDocument getDocument() {
153: if (doc == null) {
154: doc = createAndLoadDocument();
155: }
156: return doc;
157: }
158:
159: /**
160: * Return text of the whole document.
161: * <br>
162: * The document is retrieved by {@link #getDocument()}.
163: */
164: protected String getDocumentText() {
165: try {
166: Document d = getDocument();
167: return d.getText(0, d.getLength());
168: } catch (BadLocationException ex) {
169: ex.printStackTrace(getLog());
170: fail(ex.getMessage());
171: return null; // should never be reached
172: }
173: }
174:
175: /**
176: * Assert whether the document available through {@link #getDocument()}
177: * has a content equal to <code>expectedText</code>.
178: */
179: protected void assertDocumentText(String msg, String expectedText) {
180: String docText = getDocumentText();
181: if (!docText.equals(expectedText)) {
182: StringBuffer sb = new StringBuffer();
183: sb.append(msg);
184: sb.append("\n----- expected text: -----\n");
185: appendDebugText(sb, expectedText);
186: sb.append("\n----- document text: -----\n");
187: appendDebugText(sb, docText);
188: sb.append("\n-----\n");
189: int startLine = 1;
190: for (int i = 0; i < docText.length()
191: && i < expectedText.length(); i++) {
192: if (expectedText.charAt(i) == '\n') {
193: startLine++;
194: }
195: if (expectedText.charAt(i) != docText.charAt(i)) {
196: sb
197: .append("Diff starts in line " + startLine
198: + "\n");
199: String context = expectedText.substring(i);
200: if (context.length() > 40) {
201: context = context.substring(0, 40);
202: }
203: sb.append("Expected " + context + "\n");
204: context = docText.substring(i);
205: if (context.length() > 40) {
206: context = context.substring(0, 40);
207: }
208: sb.append("Found " + context + "\n");
209: break;
210: }
211: }
212: fail(sb.toString());
213: }
214: }
215:
216: protected final void appendDebugChar(StringBuffer sb, char ch) {
217: switch (ch) {
218: case '\n':
219: sb.append("\\n\n");
220: break;
221: case '\t':
222: sb.append("\\t");
223: break;
224:
225: default:
226: sb.append(ch);
227: break;
228: }
229: }
230:
231: protected final void appendDebugText(StringBuffer sb, String text) {
232: for (int i = 0; i < text.length(); i++) {
233: appendDebugChar(sb, text.charAt(i));
234: }
235: }
236:
237: protected final String debugText(String text) {
238: StringBuffer sb = new StringBuffer();
239: appendDebugText(sb, text);
240: return sb.toString();
241: }
242:
243: /**
244: * Assert whether the document available through {@link #getDocument()}
245: * has a content equal to <code>expectedText</code> and whether the caret
246: * position {@link #getCaretOffset()}
247: * indicated by "|" in the passed text is at the right place.
248: */
249: protected void assertDocumentTextAndCaret(String msg,
250: String expectedTextAndCaretPipe) {
251: // [TODO] a more elaborated support could be done e.g. "||" to a real "|" etc.
252: int expectedCaretOffset = expectedTextAndCaretPipe.indexOf('|');
253: if (expectedCaretOffset == -1) { // caret position not indicated
254: fail("Caret position not indicated in '"
255: + expectedTextAndCaretPipe + "'");
256: }
257:
258: String expectedDocumentText = expectedTextAndCaretPipe
259: .substring(0, expectedCaretOffset)
260: + expectedTextAndCaretPipe
261: .substring(expectedCaretOffset + 1);
262:
263: assertDocumentText(msg, expectedDocumentText);
264: if (expectedCaretOffset != getCaretOffset()) {
265: fail("caretOffset=" + getCaretOffset()
266: + " but expectedCaretOffset=" + expectedCaretOffset
267: + " in '" + expectedTextAndCaretPipe + "'");
268: }
269: }
270:
271: /**
272: * Create editor kit instance to be returned
273: * by {@link #getEditorKit()}.
274: * <br>
275: * The returned editor kit should return
276: * <code>BaseDocument</code> instances
277: * from its {@link javax.swing.text.EditorKit.createDefaultDocument()}.
278: */
279: abstract protected EditorKit createEditorKit();
280:
281: /**
282: * Get the kit that should be used
283: * when creating the <code>BaseDocument</code>
284: * instance.
285: * <br>
286: * The editor kit instance is created in {@link #createEditorKit()}.
287: *
288: * @return editor kit instance.
289: */
290: public final EditorKit getEditorKit() {
291: if (editorKit == null) {
292: editorKit = createEditorKit();
293: }
294: return editorKit;
295: }
296:
297: private BaseDocument createAndLoadDocument() {
298: BaseDocument bd = (BaseDocument) getEditorKit()
299: .createDefaultDocument();
300:
301: if (loadDocumentText != null) {
302: try {
303: bd.insertString(0, loadDocumentText, null);
304: } catch (BadLocationException e) {
305: e.printStackTrace(getLog());
306: fail();
307: }
308: }
309: return bd;
310: }
311:
312: class CaretImpl implements Caret, DocumentListener {
313:
314: private Document doc;
315:
316: private int dot;
317:
318: private int mark;
319:
320: private boolean visible = true;
321:
322: private boolean selectionVisible;
323:
324: private int blinkRate = 300;
325:
326: private EventListenerList listenerList = new EventListenerList();
327:
328: private ChangeEvent changeEvent;
329:
330: CaretImpl(Document doc, int dot) {
331: this .doc = doc;
332: doc.addDocumentListener(this );
333: setDot(dot);
334: }
335:
336: public void deinstall(javax.swing.text.JTextComponent c) {
337: fail("Not yet implemented");
338: }
339:
340: public void install(javax.swing.text.JTextComponent c) {
341: fail("Not yet implemented");
342: }
343:
344: public java.awt.Point getMagicCaretPosition() {
345: fail("Not yet implemented");
346: return null;
347: }
348:
349: public void setMagicCaretPosition(java.awt.Point p) {
350: fail("Not yet implemented");
351: }
352:
353: public int getDot() {
354: return dot;
355: }
356:
357: public int getMark() {
358: return mark;
359: }
360:
361: public void setDot(int dot) {
362: this .mark = this .dot;
363: changeCaretPosition(dot);
364: }
365:
366: public void moveDot(int dot) {
367: changeCaretPosition(dot);
368: }
369:
370: public int getBlinkRate() {
371: return blinkRate;
372: }
373:
374: public void setBlinkRate(int rate) {
375: this .blinkRate = blinkRate;
376: }
377:
378: public boolean isVisible() {
379: return visible;
380: }
381:
382: public void setVisible(boolean v) {
383: this .visible = visible;
384: }
385:
386: public boolean isSelectionVisible() {
387: return selectionVisible;
388: }
389:
390: public void setSelectionVisible(boolean v) {
391: this .selectionVisible = v;
392: }
393:
394: public void addChangeListener(ChangeListener l) {
395: listenerList.add(ChangeListener.class, l);
396: }
397:
398: public void removeChangeListener(ChangeListener l) {
399: listenerList.remove(ChangeListener.class, l);
400: }
401:
402: public void fireChangeListener() {
403: // Lazily create the event
404: if (changeEvent == null) {
405: changeEvent = new ChangeEvent(this );
406: }
407:
408: Object[] listeners = listenerList.getListenerList();
409: for (int i = 0; i < listeners.length; i++) {
410: ((ChangeListener) listeners[i + 1])
411: .stateChanged(changeEvent);
412: }
413: }
414:
415: public void paint(java.awt.Graphics g) {
416: }
417:
418: public void insertUpdate(DocumentEvent e) {
419: int offset = e.getOffset();
420: int length = e.getLength();
421: int newDot = dot;
422: short changed = 0;
423: if (newDot >= offset) {
424: newDot += length;
425: changed |= 1;
426: }
427: int newMark = mark;
428: if (newMark >= offset) {
429: newMark += length;
430: changed |= 2;
431: }
432:
433: if (changed != 0) {
434: if (newMark == newDot) {
435: setDot(newDot);
436: ensureValidPosition();
437: } else {
438: setDot(newMark);
439: if (getDot() == newMark) {
440: moveDot(newDot);
441: }
442: ensureValidPosition();
443: }
444:
445: }
446: }
447:
448: public void removeUpdate(DocumentEvent e) {
449: int offs0 = e.getOffset();
450: int offs1 = offs0 + e.getLength();
451: int newDot = dot;
452: if (newDot >= offs1) {
453: newDot -= (offs1 - offs0);
454: } else if (newDot >= offs0) {
455: newDot = offs0;
456: }
457: int newMark = mark;
458: if (newMark >= offs1) {
459: newMark -= (offs1 - offs0);
460: } else if (newMark >= offs0) {
461: newMark = offs0;
462: }
463: if (newMark == newDot) {
464: setDot(newDot);
465: ensureValidPosition();
466: } else {
467: setDot(newMark);
468: if (getDot() == newMark) {
469: moveDot(newDot);
470: }
471: ensureValidPosition();
472: }
473: }
474:
475: public void changedUpdate(DocumentEvent e) {
476:
477: }
478:
479: private void changeCaretPosition(int dot) {
480: if (this .dot != dot) {
481: this .dot = dot;
482: fireChangeListener();
483: }
484: }
485:
486: private void ensureValidPosition() {
487: int length = doc.getLength();
488: if (dot > length || mark > length) {
489: setDot(length);
490: }
491: }
492:
493: }
494:
495: }
|