A couple of fixes for javax.swing.text that have been lying around on my HD for too long now...
2006-06-21 Roman Kennke <[EMAIL PROTECTED]> * javax/swing/text/AbstractDocument.java (BranchElement.numChildren): New field. (BranchElement.BranchElement): Initialize children array with one element (that's the least number of elements that makes sense). Initialize numChildren. (BranchElement.children): Use numChildren as boundary. (BranchElement.getElement): Use numChildren as boundary. (BranchElement.getElementCount): Use numChildren as boundary. (BranchElement.getElementIndex): Use numChildren as boundary. (BranchElement.getEndOffset): Use numChildren as boundary. (BranchElement.getStartOffset): Use numChildren as boundary. (BranchElement.positionToElement): Use numChildren as boundary. (BranchElement.replace): Handle the children array more efficiently by growing in blocks > 1, and reusing space from removed elements. (LeafElement.startDelta): Removed. (LeafElement.endDelta): Removed. (LeafElement.LeafElement): Removed handling of deltas. (LeafElement.getEndOffset): Likewise. (LeafElement.getStartOffset): Likewise. * javax/swing/text/JTextComponent.java (setDocument): Added locking of the old document to avoid dangling notification beeing delivered while the document is beeing disconnected. (getScrollableTracksViewportWidth): Fixed condition. * javax/swing/text/PlainDocument.java (createDefaultRoot): Create elements without AttributeSet. * javax/swing/text/rtf/RTFParser.java (parseFile): Handle slightly incorrect RTF gracefully. * javax/swing/text/rtf/RTFScanner.java (lastToken): New field. (readTokenImpl): New method. (peekToken): New method. (readToken): Changed to call readTokenImpl or return the lastToken if there's one present. -- “Improvement makes straight roads, but the crooked roads, without Improvement, are roads of Genius.” - William Blake
Index: javax/swing/text/AbstractDocument.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/AbstractDocument.java,v retrieving revision 1.57 diff -u -1 -0 -r1.57 AbstractDocument.java --- javax/swing/text/AbstractDocument.java 13 May 2006 23:11:13 -0000 1.57 +++ javax/swing/text/AbstractDocument.java 21 Jun 2006 17:02:57 -0000 @@ -1667,22 +1667,29 @@ /** * An implementation of [EMAIL PROTECTED] Element} to represent composite * <code>Element</code>s that contain other <code>Element</code>s. */ public class BranchElement extends AbstractElement { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = -6037216547466333183L; - /** The child elements of this BranchElement. */ - private Element[] children = new Element[0]; + /** + * The child elements of this BranchElement. + */ + private Element[] children;; + + /** + * The number of children in the branch element. + */ + private int numChildren; /** * The cached startOffset value. This is used in the case when a * BranchElement (temporarily) has no child elements. */ private int startOffset; /** * The cached endOffset value. This is used in the case when a * BranchElement (temporarily) has no child elements. @@ -1693,38 +1700,40 @@ * Creates a new <code>BranchElement</code> with the specified * parent and attributes. * * @param parent the parent element of this <code>BranchElement</code> * @param attributes the attributes to set on this * <code>BranchElement</code> */ public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); + children = new Element[1]; + numChildren = 0; startOffset = -1; endOffset = -1; } /** * Returns the children of this <code>BranchElement</code>. * * @return the children of this <code>BranchElement</code> */ public Enumeration children() { if (children.length == 0) return null; Vector tmp = new Vector(); - for (int index = 0; index < children.length; ++index) - tmp.add(children[index]); + for (int index = 0; index < numChildren; ++index) + tmp.add(children[index]); return tmp.elements(); } /** * Returns <code>true</code> since <code>BranchElements</code> allow * child elements. * * @return <code>true</code> since <code>BranchElements</code> allow * child elements @@ -1736,55 +1745,55 @@ /** * Returns the child element at the specified <code>index</code>. * * @param index the index of the requested child element * * @return the requested element */ public Element getElement(int index) { - if (index < 0 || index >= children.length) - return null; + if (index < 0 || index >= numChildren) + return null; return children[index]; } /** * Returns the number of child elements of this element. * * @return the number of child elements of this element */ public int getElementCount() { - return children.length; + return numChildren; } /** * Returns the index of the child element that spans the specified * offset in the document model. * * @param offset the offset for which the responsible element is searched * * @return the index of the child element that spans the specified * offset in the document model */ public int getElementIndex(int offset) { // If offset is less than the start offset of our first child, // return 0 if (offset < getStartOffset()) return 0; // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length - 1; ++index) + for (int index = 0; index < numChildren - 1; ++index) { Element elem = children[index]; if ((elem.getStartOffset() <= offset) && (offset < elem.getEndOffset())) return index; // If the next element's start offset is greater than offset // then we have to return the closest Element, since no Elements // will contain the offset if (children[index + 1].getStartOffset() > offset) @@ -1807,27 +1816,27 @@ * This is the end offset of the last child element. If this element * has no children, this method throws a <code>NullPointerException</code>. * * @return the offset inside the document model that is after the last * character of this element * * @throws NullPointerException if this branch element has no children */ public int getEndOffset() { - if (children.length == 0) + if (numChildren == 0) { if (endOffset == -1) throw new NullPointerException("BranchElement has no children."); } else - endOffset = children[children.length - 1].getEndOffset(); + endOffset = children[numChildren - 1].getEndOffset(); return endOffset; } /** * Returns the name of this element. This is [EMAIL PROTECTED] #ParagraphElementName} * in this case. * * @return the name of this element */ @@ -1841,21 +1850,21 @@ * This is the start offset of the first child element. If this element * has no children, this method throws a <code>NullPointerException</code>. * * @return the start offset of this element inside the document model * * @throws NullPointerException if this branch element has no children and * no startOffset value has been cached */ public int getStartOffset() { - if (children.length == 0) + if (numChildren == 0) { if (startOffset == -1) throw new NullPointerException("BranchElement has no children."); } else startOffset = children[0].getStartOffset(); return startOffset; } @@ -1877,49 +1886,62 @@ * * @return the <code>Element</code> at the specified <code>Document</code> * offset * * @see #getElementIndex(int) */ public Element positionToElement(int position) { // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length; ++index) + for (int index = 0; index < numChildren; ++index) { Element elem = children[index]; if ((elem.getStartOffset() <= position) && (position < elem.getEndOffset())) return elem; } return null; } /** * Replaces a set of child elements with a new set of child elemens. * * @param offset the start index of the elements to be removed * @param length the number of elements to be removed * @param elements the new elements to be inserted */ public void replace(int offset, int length, Element[] elements) { - Element[] target = new Element[children.length - length - + elements.length]; - System.arraycopy(children, 0, target, 0, offset); - System.arraycopy(elements, 0, target, offset, elements.length); - System.arraycopy(children, offset + length, target, - offset + elements.length, - children.length - offset - length); - children = target; + if (numChildren + elements.length - length > children.length) + { + // Gotta grow the array. + int newSize = Math.max(2 * children.length, + numChildren + elements.length - length); + Element[] target = new Element[newSize]; + System.arraycopy(children, 0, target, 0, offset); + System.arraycopy(elements, 0, target, offset, elements.length); + System.arraycopy(children, offset + length, target, + offset + elements.length, + numChildren - offset - length); + children = target; + } + else + { + System.arraycopy(children, offset + length, children, + offset + elements.length, + numChildren - offset - length); + System.arraycopy(elements, 0, children, offset, elements.length); + } + numChildren += elements.length - length; } /** * Returns a string representation of this element. * * @return a string representation of this element */ public String toString() { return ("BranchElement(" + getName() + ") " @@ -2158,65 +2180,46 @@ * Manages the start offset of this element. */ private Position startPos; /** * Manages the end offset of this element. */ private Position endPos; /** - * This gets possible added to the startOffset when a startOffset - * outside the document range is requested. - */ - private int startDelta; - - /** - * This gets possible added to the endOffset when a endOffset - * outside the document range is requested. - */ - private int endDelta; - - /** * Creates a new <code>LeafElement</code>. * * @param parent the parent of this <code>LeafElement</code> * @param attributes the attributes to be set * @param start the start index of this element inside the document model * @param end the end index of this element inside the document model */ public LeafElement(Element parent, AttributeSet attributes, int start, int end) { super(parent, attributes); - int len = content.length(); - startDelta = 0; - if (start > len) - startDelta = start - len; - endDelta = 0; - if (end > len) - endDelta = end - len; try - { - startPos = createPosition(start - startDelta); - endPos = createPosition(end - endDelta); - } - catch (BadLocationException ex) - { - AssertionError as; - as = new AssertionError("BadLocationException thrown " - + "here. start=" + start - + ", end=" + end - + ", length=" + getLength()); - as.initCause(ex); - throw as; - } + { + startPos = createPosition(start); + endPos = createPosition(end); + } + catch (BadLocationException ex) + { + AssertionError as; + as = new AssertionError("BadLocationException thrown " + + "here. start=" + start + + ", end=" + end + + ", length=" + getLength()); + as.initCause(ex); + throw as; + } } /** * Returns <code>null</code> since <code>LeafElement</code>s cannot have * children. * * @return <code>null</code> since <code>LeafElement</code>s cannot have * children */ public Enumeration children() @@ -2274,21 +2277,21 @@ /** * Returns the end offset of this <code>Element</code> inside the * document. * * @return the end offset of this <code>Element</code> inside the * document */ public int getEndOffset() { - return endPos.getOffset() + endDelta; + return endPos.getOffset(); } /** * Returns the name of this <code>Element</code>. This is * [EMAIL PROTECTED] #ContentElementName} in this case. * * @return the name of this <code>Element</code> */ public String getName() { @@ -2300,21 +2303,21 @@ /** * Returns the start offset of this <code>Element</code> inside the * document. * * @return the start offset of this <code>Element</code> inside the * document */ public int getStartOffset() { - return startPos.getOffset() + startDelta; + return startPos.getOffset(); } /** * Returns <code>true</code>. * * @return <code>true</code> */ public boolean isLeaf() { return true; Index: javax/swing/text/JTextComponent.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/JTextComponent.java,v retrieving revision 1.59 diff -u -1 -0 -r1.59 JTextComponent.java --- javax/swing/text/JTextComponent.java 20 Jun 2006 18:24:14 -0000 1.59 +++ javax/swing/text/JTextComponent.java 21 Jun 2006 17:02:58 -0000 @@ -35,20 +35,21 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package javax.swing.text; import gnu.classpath.NotImplementedException; import java.awt.AWTEvent; import java.awt.Color; +import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; @@ -1172,22 +1173,33 @@ setFocusable(true); setEditable(true); enableEvents(AWTEvent.KEY_EVENT_MASK); setOpaque(true); updateUI(); } public void setDocument(Document newDoc) { Document oldDoc = doc; - doc = newDoc; - firePropertyChange("document", oldDoc, newDoc); + try + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readLock(); + + doc = newDoc; + firePropertyChange("document", oldDoc, newDoc); + } + finally + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readUnlock(); + } revalidate(); repaint(); } public Document getDocument() { return doc; } /** @@ -1650,24 +1662,26 @@ public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) return getParent().getHeight() > getPreferredSize().height; return false; } public boolean getScrollableTracksViewportWidth() { - if (getParent() instanceof JViewport) - return getParent().getWidth() > getPreferredSize().width; + boolean res = false;; + Container c = getParent(); + if (c instanceof JViewport) + res = ((JViewport) c).getExtentSize().width > getPreferredSize().width; - return false; + return res; } /** * Adds a <code>CaretListener</code> object to this text component. * * @param listener the listener to add */ public void addCaretListener(CaretListener listener) { listenerList.add(CaretListener.class, listener); Index: javax/swing/text/PlainDocument.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/PlainDocument.java,v retrieving revision 1.21 diff -u -1 -0 -r1.21 PlainDocument.java --- javax/swing/text/PlainDocument.java 6 Mar 2006 10:51:19 -0000 1.21 +++ javax/swing/text/PlainDocument.java 21 Jun 2006 17:02:58 -0000 @@ -98,24 +98,24 @@ lines = new Element[1]; lines[0] = createLeafElement(rootElement, SimpleAttributeSet.EMPTY, 0, 1); } ((BranchElement) rootElement).replace(0, rootElement.getElementCount(), lines); } protected AbstractDocument.AbstractElement createDefaultRoot() { BranchElement root = - (BranchElement) createBranchElement(null, SimpleAttributeSet.EMPTY); + (BranchElement) createBranchElement(null, null); Element[] array = new Element[1]; - array[0] = createLeafElement(root, SimpleAttributeSet.EMPTY, 0, 1); + array[0] = createLeafElement(root, null, 0, 1); root.replace(0, 0, array); return root; } protected void insertUpdate(DefaultDocumentEvent event, AttributeSet attributes) { int offset = event.getOffset(); int eventLength = event.getLength(); Index: javax/swing/text/rtf/RTFParser.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/rtf/RTFParser.java,v retrieving revision 1.3 diff -u -1 -0 -r1.3 RTFParser.java --- javax/swing/text/rtf/RTFParser.java 2 Jul 2005 20:32:51 -0000 1.3 +++ javax/swing/text/rtf/RTFParser.java 21 Jun 2006 17:02:58 -0000 @@ -133,23 +133,31 @@ private void parseFile() throws IOException, BadLocationException { Token t1 = scanner.readToken(); if (t1.type != Token.LCURLY) throw new RTFParseException("expected left curly braces"); parseHeader(); parseDocument(); - Token t2 = scanner.readToken(); - if (t2.type != Token.RCURLY) - throw new RTFParseException("expected right curly braces"); + Token t2 = scanner.peekToken(); + if (t2.type == Token.RCURLY) + { + // Eat the token. + scanner.readToken(); + } + else + { + // Ignore this for maximum robustness when file is broken. + System.err.println("RTF warning: expected right curly braces"); + } } /** * The parse rules for <header>. * * TODO: implement this properly */ private void parseHeader() //throws IOException, BadLocationException Index: javax/swing/text/rtf/RTFScanner.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/rtf/RTFScanner.java,v retrieving revision 1.3 diff -u -1 -0 -r1.3 RTFScanner.java --- javax/swing/text/rtf/RTFScanner.java 2 Jul 2005 20:32:51 -0000 1.3 +++ javax/swing/text/rtf/RTFScanner.java 21 Jun 2006 17:02:58 -0000 @@ -64,20 +64,25 @@ * The reader from which we read the RTF data. */ private Reader in; /** * This is used to constuct strings from the read in chars. */ private StringBuffer buffer; /** + * Lookahead token. + */ + private Token lastToken; + + /** * Constructs a new RTFScanner without initializing the [EMAIL PROTECTED] Reader}. */ private RTFScanner() { buffer = new StringBuffer(); } /** * Constructs a new RTFScanner for the given [EMAIL PROTECTED] InputStream}. * The stream is wrapped into an [EMAIL PROTECTED] InputStreamReader} and if it's @@ -113,21 +118,21 @@ } } /** * Reads in the next [EMAIL PROTECTED] Token} from the stream. * * @return the read [EMAIL PROTECTED] Token} * * @throws IOException if the underlying stream has problems */ - public Token readToken() + private Token readTokenImpl() throws IOException { Token token = null; int c = in.read(); switch(c) { case -1: token = new Token(Token.EOF); break; @@ -149,20 +154,41 @@ default: buffer.delete(0, buffer.length()); buffer.append((char) c); token = readText(); break; } return token; } + Token peekToken() + throws IOException + { + lastToken = readTokenImpl(); + return lastToken; + } + + Token readToken() + throws IOException + { + Token token; + if (lastToken != null) + { + token = lastToken; + lastToken = null; + } + else + token = readTokenImpl(); + return token; + } + /** * Reads in a control word and optional parameter. * * @return the read in control word as [EMAIL PROTECTED] ControlWordToken} * * @throws IOException if the underlying stream has problems */ private Token readControlWord() throws IOException {
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil