Thanks for reading!
I have made a small app to illustrate the problem I am having. I have a JTextArea which I have an UndoManager managing any edits made. But, I get an ArrayIndexOutOfBoundsException - sometimes, but then not other times.
At the end of this post:-
UndoManager.java - compiles to illustrate problem
Stack Trace - for ArrayIndexOutOfBoundsException
Compile and press the '
Test Me' button first, then 'Undo' and this causes the exception. If you change the code in the actionPerformed method, you will see that there is not always an exception.
I have traced the error, and it seems to occur if the first edit to the JTextArea ends in a newline char, and also, if the first edit consists of more than one line and the first line is shorter than any of the others eg.
String s = "a\nab\n"; // this CAUSES an exception
If the first line length >= the second, it is fine eg.
String s = "ab\nab\n"; // this does NOT cause an exception
My question is: what am I doing wrong? Must be something simple?
PS. I have tried with various different methods, eg dsd.insertString(...) etc and regardless, I get the same error every time.
Can you tell me what I'm doing wrong???
Much appreciated!
**************************************************************************
Here is the Code:
**************************************************************************
/* Small app to test UndoManager
*
* Works fine for straight editing in the JTextArea,
* but if you apply a few methods on the Text Area, using
* the class API, it starts to throw ArrayIndexOutOfBoundsExceptions.
*
* My question is this: What do I need to do to make it possible to undo
* textArea.setText(...) or textArea.append(...) methods from code. I have
* tried different methods, eg dsd.insertText(...) etc, but always the same
* result.
*
* I have set this example up with some data. If you execute the code, hit
* the test button FIRST before typing in anything, then undo, and you get the
* exception. This exception only occurs if the first line of the append is
* shorter than the others, and the whole append all ends in a newline char!
* Other strange things happen too!
*
* Have a look at the actionPerformed method. Play around too. Strange things.
*
* I know I must be doing something simple wrong!!!
*/
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.undo.*;
import javax.swing.text.*;
public class UndoManagerTest extends JFrame implements ActionListener
{
private JPanel contentPane;
private JTextArea textArea;
private JButton undoButton, redoButton, testButton;
private UndoManager undo;
private UndoAction undoAction;
private RedoAction redoAction;
private DefaultStyledDocument dsd;
//--------------------------------------------------------------------------
public UndoManagerTest()
{
contentPane = new JPanel();
dsd = new DefaultStyledDocument();
textArea = new JTextArea( dsd, "", 20, 30 );
JScrollPane scrollPane = new JScrollPane( textArea );
dsd.addUndoableEditListener( new MyUndoableEditListener() );
undoAction = new UndoAction ( "Undo" );
redoAction = new RedoAction ( "Redo" );
undoButton = new JButton( undoAction );
redoButton = new JButton( redoAction );
testButton = new JButton( "Test Me" );
testButton.addActionListener( this );
contentPane.add( scrollPane );
contentPane.add( undoButton );
contentPane.add( redoButton );
contentPane.add( testButton );
setContentPane( contentPane );
// create a new UndoManager
undo = new UndoManager();
}
//--------------------------------------------------------------------------
public void actionPerformed( ActionEvent e )
{
if( e.getSource() == testButton )
{
String s = "a\nab\n"; // this CAUSES an exception
//String s = "ab\nab\n"; // this does NOT cause an exception
//String s = "a\na"; // this does NOT cause an exception
textArea.append( s );
}
}
//--------------------------------------------------------------------------
private class UndoAction extends AbstractAction
{
public UndoAction( String text )
{
super( text );
setEnabled( false );
}
public void actionPerformed( ActionEvent e )
{
try {
undo.undo();
}
catch( CannotUndoException ex )
{
System.out.println( "Unable to undo: " + ex);
ex.printStackTrace();
}
catch( Exception ec )
{
System.err.println( "UndoAction General Exception: Why this error???" );
ec.printStackTrace();
}
updateUndoState();
redoAction.updateRedoState();
}
protected void updateUndoState()
{
if( undo.canUndo() )
{
setEnabled( true );
}
else
{
setEnabled( false );
}
}
}
//--------------------------------------------------------------------------
private class RedoAction extends AbstractAction
{
public RedoAction( String text )
{
super( text );
setEnabled( false );
}
public void actionPerformed( ActionEvent e )
{
try {
undo.redo();
}
catch ( CannotRedoException ex )
{
System.out.println("Unable to redo: " + ex);
ex.printStackTrace();
}
catch( Exception ec )
{
System.err.println( Action.NAME + " Why this error???" );
ec.printStackTrace();
}
updateRedoState();
undoAction.updateUndoState();
}
protected void updateRedoState()
{
if ( undo.canRedo() )
{
setEnabled( true );
}
else
{
setEnabled( false );
}
}
}
//--------------------------------------------------------------------------
// Listener for undo manager
protected class MyUndoableEditListener implements UndoableEditListener
{
public void undoableEditHappened( UndoableEditEvent e )
{
//Remember the edit and update the menus
undo.addEdit( e.getEdit() );
undoAction.updateUndoState();
redoAction.updateRedoState();
}
}
//--------------------------------------------------------------------------
public static void main( String[] args )
{
UndoManagerTest mainFrame = new UndoManagerTest();
mainFrame.setSize( 400, 400 );
mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
mainFrame.setTitle( "UndoManager Test" );
mainFrame.setVisible( true );
}
//--------------------------------------------------------------------------
}
***************************************************************************
Here is the Stack Trace:
***************************************************************************
java.lang.ArrayIndexOutOfBoundsException: -1
at javax.swing.text.AbstractDocument$BranchElement.getEndOffset(AbstractDocument.java:2333)
at javax.swing.text.PlainView.getLineWidth(PlainView.java:631)
at javax.swing.text.PlainView.updateDamage(PlainView.java:507)
at javax.swing.text.PlainView.removeUpdate(PlainView.java:434)
at javax.swing.plaf.basic.BasicTextUI$RootView.removeUpdate(BasicTextUI.java:1501)
at javax.swing.plaf.basic.BasicTextUI$UpdateHandler.removeUpdate(BasicTextUI.java:1741)
at javax.swing.text.AbstractDocument.fireRemoveUpdate(AbstractDocument.java:242)
at javax.swing.text.AbstractDocument$DefaultDocumentEvent.undo(AbstractDocument.java:2799)
at javax.swing.undo.UndoManager.undoTo(UndoManager.java:210)
at javax.swing.undo.UndoManager.undo(UndoManager.java:275)
at UndoManagerTest$UndoAction.actionPerformed(UndoManagerTest.java:107)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1786)
at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1839)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:245)
at java.awt.Component.processMouseEvent(Component.java:5100)
at java.awt.Component.processEvent(Component.java:4897)
at java.awt.Container.processEvent(Container.java:1569)
at java.awt.Component.dispatchEventImpl(Component.java:3615)
at java.awt.Container.dispatchEventImpl(Container.java:1627)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3198)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
at java.awt.Container.dispatchEventImpl(Container.java:1613)
at java.awt.Window.dispatchEventImpl(Window.java:1606)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:456)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)