Index: src/org/openide/text/DocumentLine.java
===================================================================
RCS file: /cvs/openide/src/org/openide/text/DocumentLine.java,v
retrieving revision 1.54
diff -u -r1.54 DocumentLine.java
--- src/org/openide/text/DocumentLine.java 20 May 2004 15:58:15 -0000 1.54
+++ src/org/openide/text/DocumentLine.java 1 Jun 2004 16:21:02 -0000
@@ -781,8 +781,8 @@
* NetBeans conventions.
*/
public static abstract class Set extends Line.Set {
- /** listener on document changes */
- private final LineListener listener;
+ /** listener on document changes, accessed from LazyLines */
+ final LineListener listener;
/** all lines in the set or null */
private java.util.List list;
@@ -859,14 +859,9 @@
*
* @return list of Line objects
*/
- public java.util.List getLines () {
+ public synchronized java.util.List getLines () {
if (list == null) {
- int cnt = listener.getOriginalLineCount ();
- java.util.List l = new java.util.LinkedList ();
- for (int i = 0; i < cnt; i++) {
- l.add (getOriginal (i));
- }
- list = l;
+ list = new LazyLines (this);
}
return list;
}
@@ -885,6 +880,16 @@
return safelyRegisterLine(createLine(offset));
}
+
+ public int getOriginal (Line line) {
+ Line find = findLine (line);
+ if (find != null) {
+ return listener.getOld (find.getLineNumber ());
+ } else {
+ return -1;
+ }
+ }
+
/* Creates current line.
*
* @param line is a number of the line (text line) we want to acquire
Index: src/org/openide/text/LazyLines.java
===================================================================
RCS file: src/org/openide/text/LazyLines.java
diff -N src/org/openide/text/LazyLines.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/openide/text/LazyLines.java 1 Jun 2004 16:21:02 -0000
@@ -0,0 +1,160 @@
+/*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+
+package org.openide.text;
+
+/** Lazy List that delegates to another instance of itself.
+ */
+final class LazyLines extends Object implements java.util.List {
+ private java.util.List delegate;
+ private DocumentLine.Set set;
+
+ public LazyLines (DocumentLine.Set set) {
+ this.set = set;
+ }
+
+ /** Override this to create the delegate
+ */
+ private java.util.List createDelegate () {
+ int cnt = set.listener.getOriginalLineCount ();
+ java.util.List l = new java.util.ArrayList (cnt);
+ for (int i = 0; i < cnt; i++) {
+ l.add (set.getOriginal (i));
+ }
+ return l;
+ }
+
+
+ private synchronized java.util.List getDelegate () {
+ if (delegate == null) {
+ delegate = createDelegate ();
+ }
+ return delegate;
+ }
+
+ public int indexOf (Object o) {
+ if (o instanceof DocumentLine) {
+ Line find = set.findLine ((DocumentLine)o);
+ if (find != null) {
+ int indx = set.listener.getOld (find.getLineNumber ());
+ if (set.getOriginal (indx).equals (o)) {
+ // just to verify that the index really exists
+ return indx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public int lastIndexOf (Object o) {
+ return indexOf (o);
+ }
+
+
+ //
+ // Pure delegate methods
+ //
+
+ public int hashCode () {
+ return getDelegate ().hashCode ();
+ }
+
+ public boolean addAll (java.util.Collection c) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public boolean removeAll (java.util.Collection c) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public java.util.ListIterator listIterator () {
+ return getDelegate ().listIterator ();
+ }
+
+ public Object[] toArray () {
+ return getDelegate ().toArray ();
+ }
+
+ public Object[] toArray (Object[] a) {
+ return getDelegate ().toArray (a);
+ }
+
+ public java.util.ListIterator listIterator (int index) {
+ return getDelegate ().listIterator (index);
+ }
+
+ public boolean remove (Object o) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public boolean equals (Object obj) {
+ return getDelegate ().equals (obj);
+ }
+
+ public boolean contains (Object o) {
+ return getDelegate ().contains (o);
+ }
+
+ public void add (int index, Object element) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public void clear () {
+ getDelegate ().clear ();
+ }
+
+ public Object set (int index, Object element) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public int size () {
+ return getDelegate ().size ();
+ }
+
+ public Object get (int index) {
+ return getDelegate ().get (index);
+ }
+
+ public boolean containsAll (java.util.Collection c) {
+ return getDelegate ().containsAll (c);
+ }
+
+ public boolean add (Object o) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public boolean isEmpty () {
+ return getDelegate ().isEmpty ();
+ }
+
+ public boolean retainAll (java.util.Collection c) {
+ throw new UnsupportedOperationException ();
+ }
+
+ public java.util.List subList (int fromIndex, int toIndex) {
+ return getDelegate ().subList (fromIndex, toIndex);
+ }
+
+ public Object remove (int index) {
+ return getDelegate ().remove (index);
+ }
+
+ public java.util.Iterator iterator () {
+ return getDelegate ().iterator ();
+ }
+
+ public boolean addAll (int index, java.util.Collection c) {
+ throw new UnsupportedOperationException ();
+ }
+
+}
Index: src/org/openide/text/Line.java
===================================================================
RCS file: /cvs/openide/src/org/openide/text/Line.java,v
retrieving revision 1.27
diff -u -r1.27 Line.java
--- src/org/openide/text/Line.java 25 Mar 2004 16:04:54 -0000 1.27
+++ src/org/openide/text/Line.java 1 Jun 2004 16:21:02 -0000
@@ -325,13 +325,23 @@
* @exception IndexOutOfBoundsException if line
is an invalid index for the original set of lines
*/
public abstract Line getCurrent (int line) throws IndexOutOfBoundsException;
-
+
+ /** Finds an original line number for given line in this line set.
+ * @param line the line to look for
+ * @return the number that best matches the line number of the line or -1
+ * if the line does seem to be produced by this line set
+ * @since JST-PENDING
+ */
+ public int getOriginal (Line line) {
+ return computeOriginal (this, line);
+ }
+
/** Registers the line to this Line.Set
.
* @param line Line
to register
* @return registered Line
. Note: the retruned
* Line
could be different (identityHashCode not equal)
* from the one passed in */
- Line registerLine(Line line) {
+ final Line registerLine(Line line) {
synchronized(lines) {
Reference r = (Reference)lines.get(line);
Line in = (r != null ? (Line)r.get() : null);
@@ -347,6 +357,106 @@
return in;
}
+ }
+
+ /** Finds whether a line equal to provided is already registered.
+ * @param line the line to register
+ * @return the registered line equal to line or null
+ */
+ final Line findLine (Line line) {
+ synchronized (lines) {
+ Reference r = (Reference)lines.get(line);
+ Line in = (r != null ? (Line)r.get() : null);
+ return in;
+ }
+ }
+
+ /** A method that for a given Line.Set and a line computes the best
+ * original line number based on the querying the set. This is called
+ * in default implementation of getOriginal (Line) to provide
+ * inefficient (but better then most people would write) way to
+ * compute the number. It is static so it can be tested from
+ * tests working on DocumentLine objects that override the
+ * getOriginal (Line) method.
+ *
+ * @param set the set to search in
+ * @param line the line to look for
+ * @return closest possible line number for given line
+ */
+ static int computeOriginal (Line.Set set, Line line) {
+ int n = line.getLineNumber ();
+ Line current = null;
+ try {
+ current = set.getOriginal (n);
+ if (line.equals (current)) {
+ return n;
+ }
+ } catch (IndexOutOfBoundsException ex) {
+ // ok, few lines have been added and this one is now
+ // bellow the end of the document
+ }
+
+ if (current == null) {
+ return binarySearch (set, n, 0, findMaxLine (set));
+ }
+
+ if (n < current.getLineNumber ()) {
+ return binarySearch (set, n, 0, current.getLineNumber ());
+ } else {
+ return binarySearch (set, n, current.getLineNumber (), findMaxLine (set));
+ }
+ }
+
+ /** Does a search for a given line number in a given Line.Set.
+ */
+ private static int binarySearch (Line.Set set, int number, int from, int to) {
+ while (from < to) {
+ int middle = (from + to) / 2;
+
+ Line l = set.getOriginal (middle);
+ if (l.getLineNumber () < number) {
+ // try after the middle
+ from = middle + 1;
+ } else {
+ // try before the middle
+ to = middle - 1;
+ }
+ }
+
+ return from;
+ }
+
+ private static int findMaxLine (Line.Set set) {
+ int from = 0;
+ int to = 32000;
+
+ // probably larger than any existing document
+ for (;;) {
+ try {
+ set.getOriginal (to);
+ // if the line exists, double the max number, but keep
+ // for reference that it exists
+ from = to;
+ to *= 2;
+ } catch (IndexOutOfBoundsException ex) {
+ break;
+ }
+ }
+
+ while (from < to) {
+ int middle = (from + to + 1) / 2;
+
+ try {
+ set.getOriginal (middle);
+ // line exists
+ from = middle;
+ } catch (IndexOutOfBoundsException ex) {
+ // line does not exists, we have to search lower
+ to = middle - 1;
+ }
+ }
+
+ return from;
}
} // End of class Line.Set.
Index: src/org/openide/text/LineListener.java
===================================================================
RCS file: /cvs/openide/src/org/openide/text/LineListener.java,v
retrieving revision 1.11
diff -u -r1.11 LineListener.java
--- src/org/openide/text/LineListener.java 12 Aug 2003 09:38:11 -0000 1.11
+++ src/org/openide/text/LineListener.java 1 Jun 2004 16:21:02 -0000
@@ -55,7 +55,11 @@
/** Convertor between old and new line sets */
public int getLine (int i) {
- return struct.originalToCurrent (i);
+ return struct.convert (i, true/*originalToCurrent*/);
+ }
+ /** Convertor between old and new line sets */
+ public int getOld (int i) {
+ return struct.convert (i, false/*currentToOriginal*/);
}
public void removeUpdate(javax.swing.event.DocumentEvent p0) {
Index: src/org/openide/text/LineStruct.java
===================================================================
RCS file: /cvs/openide/src/org/openide/text/LineStruct.java,v
retrieving revision 1.12
diff -u -r1.12 LineStruct.java
--- src/org/openide/text/LineStruct.java 14 Apr 2004 15:05:55 -0000 1.12
+++ src/org/openide/text/LineStruct.java 1 Jun 2004 16:21:02 -0000
@@ -279,7 +279,7 @@
* @param line the line number in the original
* @return line number in the new numbering
*/
- public int originalToCurrent (int line) {
+ public int convert (int line, final boolean currentToOriginal) {
// class to compute in the request processor thread
class Compute extends Object implements Runnable {
public int result;
@@ -289,7 +289,11 @@
}
public void run () {
- result = originalToCurrentImpl (result);
+ if (currentToOriginal) {
+ result = originalToCurrentImpl (result);
+ } else {
+ result = currentToOriginalImpl (result);
+ }
}
}
@@ -343,6 +347,24 @@
}
cur += i.current;
line -= i.original;
+ }
+ }
+
+ /** Converts the current numbering to original
+ * @param line the line number now
+ * @return line number in the original numbering
+ */
+ private int currentToOriginalImpl (int line) {
+ Iterator it = list.iterator ();
+ int cur = 0;
+ for (;;) {
+ Info i = (Info)it.next ();
+ if (i.current > line) {
+ // ok we found the segment that contained this line
+ return line > i.original ? cur + i.original : cur + line;
+ }
+ cur += i.original;
+ line -= i.current;
}
}
Index: test/unit/src/org/openide/text/LineSetTest.java
===================================================================
RCS file: /cvs/openide/test/unit/src/org/openide/text/LineSetTest.java,v
retrieving revision 1.1
diff -u -r1.1 LineSetTest.java
--- test/unit/src/org/openide/text/LineSetTest.java 20 May 2004 15:58:15 -0000 1.1
+++ test/unit/src/org/openide/text/LineSetTest.java 1 Jun 2004 16:21:02 -0000
@@ -50,6 +50,9 @@
}
public static void main(java.lang.String[] args) {
+ if (args.length == 1) {
+ junit.textui.TestRunner.run (new LineSetTest (args[0]));
+ }
junit.textui.TestRunner.run(suite());
}
@@ -63,6 +66,7 @@
protected void setUp () {
ic = new InstanceContent ();
support = new CES (this, new AbstractLookup (ic));
+ ic.add (this);
}
public void testLineSetIsEmpty () throws Exception {
@@ -90,6 +94,7 @@
Line line = set.getCurrent (1);
assertEquals ("Line number is one", 1, line.getLineNumber ());
+ assertGetOriginal ("Original number is of course 1", set, line, 1);
doc.insertString (0, "New line\n", null);
@@ -107,6 +112,7 @@
Line currentLineTwo = support.getLineSet ().getOriginal (2);
assertEquals ("This is our original line", line, currentLineTwo);
+ assertGetOriginal ("Original number of the line was 1", set, line, 1);
assertEquals ("Original set still has two lines", 2, set.getLines ().size ());
assertEquals ("Index of current line 1 is 0 in old set", 1, set.getLines ().indexOf (line));
@@ -165,6 +171,211 @@
assertEquals ("They will not part", one, two);
assertEquals ("Line number is 0", 0, one.getLineNumber ());
+ }
+
+ public void testGetLinesIndexOfDoesNotCreateAllLines () throws Exception {
+ content = "0\n1\n2\n3\n4\n";
+ javax.swing.text.Document doc = support.openDocument ();
+
+ Line.Set set = support.getLineSet ();
+ Line two = set.getOriginal (2);
+
+ assertEquals ("Line index is two", 2, set.getLines ().indexOf (two));
+ assertNumberOfLines (1, set);
+
+ assertEquals ("Really two", 2, new java.util.ArrayList (set.getLines ()).indexOf (two));
+ }
+
+ public void testLinesAreNonMutable () throws Exception {
+ content = "0\n1\n2\n3\n4\n";
+ javax.swing.text.Document doc = support.openDocument ();
+
+ assertNonmutable (support.getLineSet ().getLines ());
+ }
+
+ public void testGetLinesIndexWorksAfterModifications () throws Exception {
+ content = "0\n1\n2\n3\n4\n";
+ javax.swing.text.Document doc = support.openDocument ();
+
+ Line.Set set = support.getLineSet ();
+
+ int offset = 4;
+ assertEquals ("2 is on the second line", "2", doc.getText (4, 1));
+ doc.insertString (offset, "x\n", null);
+ assertEquals ("x\n is on the second line", "x\n", doc.getText (4, 2));
+
+
+ Line two = set.getOriginal (2);
+ assertEquals ("2\n is the line text", "2\n", two.getText ());
+
+ assertEquals ("Line index is two", 2, set.getLines ().indexOf (two));
+ assertNumberOfLines (1, set);
+
+ assertEquals ("Really two", 2, new java.util.ArrayList (set.getLines ()).indexOf (two));
+ }
+
+ public void testWhatHappensWhenAskingForLineOutOfBounds () throws Exception {
+ content = "0";
+ javax.swing.text.Document doc = support.openDocument ();
+
+ Line.Set set = support.getLineSet ();
+
+ try {
+ Line l = set.getCurrent (1);
+ fail ("Should thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+ // ok
+ }
+
+ try {
+ Line n = set.getOriginal (1);
+ fail ("Should thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+ // ok
+ }
+ try {
+ Line l = set.getCurrent (-1);
+ fail ("Should thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+ // ok
+ }
+
+ try {
+ Line n = set.getOriginal (-1);
+ fail ("Should thrown IndexOutOfBoundsException");
+ } catch (IndexOutOfBoundsException ex) {
+ // ok
+ }
+
+ Line l = set.getCurrent (0);
+ Line n = set.getOriginal (0);
+
+ assertNotNull (l);
+ assertNotNull (n);
+
+ assertEquals ("Lines are the same", l, n);
+ assertEquals ("Text is", "0", l.getText ());
+ }
+
+
+ public void testGetLinesIndexWorksForNewlyAddedLines () throws Exception {
+ content = "0\n1\n2\n3\n4\n";
+ javax.swing.text.Document doc = support.openDocument ();
+
+ Line.Set set = support.getLineSet ();
+
+ int offset = 4;
+ assertEquals ("2 is on the second line", "2", doc.getText (4, 1));
+ doc.insertString (offset, "x\n", null);
+ assertEquals ("x\n is on the second line", "x\n", doc.getText (4, 2));
+
+
+ Line two = set.getCurrent (2);
+ assertEquals ("x\n is the line text", "x\n", two.getText ());
+
+ assertEquals ("Line index is -1 as it is not present", -1, set.getLines ().indexOf (two));
+ // two lines created as we need to verify that the current two line is not
+ // in the original set
+ // of course that it can be one if somebody implements this query
+ // in better way
+ assertNumberOfLines (2, set);
+ assertEquals ("Query on set works and does not produce new lines", 2, set.getOriginal (two));
+ assertNumberOfLines (2, set);
+
+ // now few additinal checks
+ assertGetOriginal ("However if one asks the line set it works", set, two, 2);
+ assertEquals ("Really missing from the list", -1, new java.util.ArrayList (set.getLines ()).indexOf (two));
+ }
+
+ private void assertNumberOfLines (int cnt, Line.Set set) throws Exception {
+ class MF implements org.netbeans.junit.MemoryFilter {
+ private java.util.HashSet counted = new java.util.HashSet ();
+ public int cnt;
+ public boolean reject(Object obj) {
+ if (obj instanceof Line) {
+ Line l = (Line)obj;
+ if (counted.add (obj)) {
+ if (l.getLookup ().lookup (LineSetTest.class) == LineSetTest.this) {
+ cnt++;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ MF mf = new MF ();
+
+ // just travel thru the memory
+ assertSize (
+ "Just one line",
+ java.util.Collections.singleton (set),
+ Integer.MAX_VALUE,
+ mf
+ );
+
+ if (mf.cnt > cnt) {
+ fail ("Only given number of instance of line created (" + cnt + ") but was: " + mf.cnt);
+ }
+ }
+
+ private static void assertGetOriginal (String s, Line.Set set, Line line, int expected) {
+ assertEquals (s + " - Overriden DocumentLine.Set.getOriginal as well", expected, set.getOriginal (line));
+ assertEquals (s + " - The default Line.Set.computeOriginal method works", expected, Line.Set.computeOriginal (set, line));
+ }
+
+ private static void assertNonmutable (java.util.List l) throws Exception {
+ try {
+ l.add (new Object ());
+ fail ("add should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+ try {
+ l.add (0, new Object ());
+ fail ("add should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+
+ try {
+ l.remove (new Object ());
+ fail ("remove should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+
+ try {
+ l.addAll (java.util.Collections.EMPTY_LIST);
+ fail ("addAll should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+ try {
+ l.addAll (0, java.util.Collections.EMPTY_LIST);
+ fail ("addAll should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+ try {
+ l.removeAll (java.util.Collections.EMPTY_LIST);
+ fail ("removeAll should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+
+ try {
+ l.retainAll (java.util.Collections.EMPTY_LIST);
+ fail ("retainAll should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
+ try {
+ l.set (0, null);
+ fail ("set should fail");
+ } catch (java.lang.UnsupportedOperationException ex) {
+ // ok
+ }
}
//