Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

NullPointerException on redo with JEditorPane/HTMLEditorKit

1 view
Skip to first unread message

Chris Smith

unread,
Nov 11, 2002, 11:23:10 AM11/11/02
to
Just ran across this quirk with JEditorPane. Anyone have a solution?

I can reproducably get JEditorPane to throw an exception on some systems
with a sequence of deleting text, undoing the delete, and then redoing
the delete. This only occurs with some text; not all. It seems to be
related to the presence of a paragraph element with a tag of
HTML.Tag.IMPLIED. In any case, I've been unable to reproduce it when
consistently using HTML.Tag.P

This has been reported to Bug Parade. I'm now looking for a work-around
for use in the interim.

To reproduce the problem:

1. Compile and run the following source code.
2. Select the entire contents of the JEditorPane.
3. Press the delete key (other actions, like cut, which
also delete the text are acceptable.)
4. Press the undo button.
5. Press the redo button.

The application should then crash with:

java.lang.NullPointerException
at javax.swing.text.CompositeView.replace(CompositeView.java:170)
at javax.swing.text.View.updateChildren(View.java:1117)
at javax.swing.text.View.removeUpdate(View.java:742)
at javax.swing.text.FlowView.removeUpdate(FlowView.java:249)
at javax.swing.text.View.forwardUpdateToView(View.java:1210)
at javax.swing.text.View.forwardUpdate(View.java:1183)
at javax.swing.text.BoxView.forwardUpdate(BoxView.java:222)
at javax.swing.text.View.removeUpdate(View.java:748)
at javax.swing.text.View.forwardUpdateToView(View.java:1210)
at javax.swing.text.View.forwardUpdate(View.java:1183)
at javax.swing.text.BoxView.forwardUpdate(BoxView.java:222)
at javax.swing.text.View.removeUpdate(View.java:748)
at javax.swing.plaf.basic.BasicTextUI$RootView.removeUpdate
(BasicTextUI.java:1430)
at javax.swing.plaf.basic.BasicTextUI$UpdateHandler.removeUpdate
(BasicTextUI.java:1665)
at javax.swing.text.AbstractDocument.fireRemoveUpdate
(AbstractDocument.java:242)
at javax.swing.text.AbstractDocument$DefaultDocumentEvent.redo
(AbstractDocument.java:2782)
at javax.swing.undo.UndoManager.redoTo(UndoManager.java:222)
at javax.swing.undo.UndoManager.redo(UndoManager.java:311)
at zom.mindiq.JEPaneTester.redo(JEPaneTester.java:98)
at zom.mindiq.JEPaneTester.access$1(JEPaneTester.java:91)
at zom.mindiq.JEPaneTester$2.actionPerformed(JEPaneTester.java:55)
at javax.swing.AbstractButton.fireActionPerformed
(AbstractButton.java:1767)
at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed
(AbstractButton.java:1820)
at javax.swing.DefaultButtonModel.fireActionPerformed
(DefaultButtonModel.java:419)
at javax.swing.DefaultButtonModel.setPressed
(DefaultButtonModel.java:257)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased
(BasicButtonListener.java:258)
at java.awt.Component.processMouseEvent(Component.java:5021)
at java.awt.Component.processEvent(Component.java:4818)
at java.awt.Container.processEvent(Container.java:1525)
at java.awt.Component.dispatchEventImpl(Component.java:3526)
at java.awt.Container.dispatchEventImpl(Container.java:1582)
at java.awt.Component.dispatchEvent(Component.java:3367)
at java.awt.LightweightDispatcher.retargetMouseEvent
(Container.java:3359)
at java.awt.LightweightDispatcher.processMouseEvent
(Container.java:3074)
at java.awt.LightweightDispatcher.dispatchEvent
(Container.java:3004)
at java.awt.Container.dispatchEventImpl(Container.java:1568)
at java.awt.Window.dispatchEventImpl(Window.java:1581)
at java.awt.Component.dispatchEvent(Component.java:3367)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:445)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy
(EventDispatchThread.java:191)
at java.awt.EventDispatchThread.pumpEventsForHierarchy
(EventDispatchThread.java:144)
at java.awt.EventDispatchThread.pumpEvents
(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.pumpEvents
(EventDispatchThread.java:130)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:98)

The source code to reproduce the problem is:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.StringReader;

import javax.swing.*;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.undo.*;

public class JEPaneTester extends JFrame
{
private static final String HTML_CONTENTS
= "<HTML>\n<BODY>\na\n<p>b</p>\n</BODY>\n</HTML>\n";

final UndoManager undo = new UndoManager();

public JEPaneTester() throws Exception
{
JEditorPane jep = new JEditorPane();

HTMLEditorKit kit = (HTMLEditorKit)
jep.getEditorKitForContentType("text/html");
HTMLDocument doc = (HTMLDocument)
kit.createDefaultDocument();
kit.read(new StringReader(HTML_CONTENTS), doc, 0);
jep.setEditorKit(kit);
jep.setDocument(doc);

jep.getDocument().addUndoableEditListener(undo) ;
jep.setEditable(true);

/*
* button pane panel
*/
JPanel bp = new JPanel();
JButton undoButton = new JButton("undo");
undoButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
undo();
}
});

JButton redoButton = new JButton("redo");
redoButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
redo();
}
});

bp.add(undoButton);
bp.add(redoButton);

/*
* whole frame
*/
getContentPane().add(jep, BorderLayout.CENTER);
getContentPane().add(bp, BorderLayout.SOUTH);

setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(450, 450);
}

private void undo()
{
try
{
if (undo.canUndo())
{
undo.undo();
}
else
{
JOptionPane.showMessageDialog(this, "Cannot redo.");
}
}
catch (CannotUndoException e)
{
}
}

private void redo()
{
try
{
if (undo.canRedo())
{
undo.redo();
}
else
{
JOptionPane.showMessageDialog(this, "Cannot redo.");
}
}
catch (CannotRedoException e)
{
}
}

public static void main(String args[]) throws Exception
{
JEPaneTester jept = new JEPaneTester();
jept.pack();
jept.show();
}
}

I'm doing this with the J2SDK version 1.4.0_01 and 1.4.0_02. I cannot
use 1.4.1 at this point, because our development systems have ATI video
cards, and reliably crash the whole system when we run the 1.4.1 JRE.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Christian Kaufhold

unread,
Nov 12, 2002, 9:27:57 AM11/12/02
to
Hello!

Chris Smith <cds...@twu.net> wrote:

> Just ran across this quirk with JEditorPane. Anyone have a solution?


Put this (or similar) into a a HTMLDocument subclass:

private transient boolean dontFire;


protected void fireInsertUpdate(DocumentEvent e)
{
if (!dontFire)
super.fireInsertUpdate(e);
}

protected void fireRemoveUpdate(DocumentEvent e)
{
if (!dontFire)
super.fireRemoveUpdate(e);
}

protected void fireChangedUpdate(DocumentEvent e)
{
if (!dontFire)
super.fireChangedUpdate(e);
}


protected void fireUndoableEditUpdate(UndoableEditEvent e)
{
super.fireUndoableEditUpdate(new UndoableEditEvent(this, new Wrapper(e.getEdit())));
}

private class Wrapper
implements DocumentEvent, UndoableEdit
{
private DefaultDocumentEvent edit;

private boolean done;


public Wrapper(UndoableEdit e)
{
edit = (DefaultDocumentEvent)e;

done = true;
}


public boolean addEdit(UndoableEdit e)
{
return false;
}

public boolean replaceEdit(UndoableEdit e)
{
return false;
}

public boolean canUndo()
{
return edit.canUndo();
}

public boolean canRedo()
{
return edit.canRedo();
}

public String getPresentationName()
{
return edit.getPresentationName();
}

public String getRedoPresentationName()
{
return edit.getPresentationName();
}

public String getUndoPresentationName()
{
return edit.getPresentationName();
}

public boolean isSignificant()
{
return edit.isSignificant();
}

public void undo()
throws CannotUndoException
{
// writeLock();

try
{
dontFire = true;

edit.undo();

dontFire = false;

done = false;

DocumentEvent.EventType type = edit.getType();

if (type == DocumentEvent.EventType.REMOVE)
fireInsertUpdate(this);
else if (type == DocumentEvent.EventType.INSERT)
fireRemoveUpdate(this);
else
fireChangedUpdate(this);
}
finally
{
dontFire = false;

// writeUnlock();
}
}

public void redo()
throws CannotUndoException
{
// writeLock();

try
{
dontFire = true;

edit.redo();

dontFire = false;

done = true;

DocumentEvent.EventType type = edit.getType();

if (type == DocumentEvent.EventType.INSERT)
fireInsertUpdate(this);
else if (type == DocumentEvent.EventType.REMOVE)
fireRemoveUpdate(this);
else
fireChangedUpdate(this);
}
finally
{
dontFire = false;

// writeUnlock();
}
}

public void die()
{
edit.die();
}


public Document getDocument()
{
return edit.getDocument();
}


public int getOffset()
{
return edit.getOffset();
}

public int getLength()
{
return edit.getLength();
}

public DocumentEvent.ElementChange getChange(Element e)
{
return edit.getChange(e);
}

public DocumentEvent.EventType getType()
{
DocumentEvent.EventType result = edit.getType();

if (!done)
if (result == DocumentEvent.EventType.REMOVE)
result = DocumentEvent.EventType.INSERT;
else if (result == DocumentEvent.EventType.INSERT)
result = DocumentEvent.EventType.REMOVE;

return result;
}

public String toString()
{
return edit.toString();
}
}

(I suppose with 1.4 non-beta, the write(Un)locks needs not be commented
out.)

The bug is that DocumentEvent.getType() is wrong during undo(), and
it is actually used in View.forwardUpdate (usually is not necessary,
since insert- or removeUpdate will be called and then the type *should*
be corresponding, but in View the different listener methods are
merged into forwardUpdate etc.), so already undo() damages the View's
(remove-all - undo - remove-all also threw for me).

I suppose it does not have to do with HTML.Tag.IMPLIED vs. P, but
whether the removed part (?) is heterogenous: the same happens
with H1 vs. H2, for example. There must be some "softening" "accident"
otherwise (I do not really understand the details).


Christian

Chris Smith

unread,
Nov 12, 2002, 2:11:15 PM11/12/02
to
Christian Kaufhold wrote ...

> Hello!
>
> Chris Smith <cds...@twu.net> wrote:
>
> > Just ran across this quirk with JEditorPane. Anyone have a solution?
>
>
> Put this (or similar) into a a HTMLDocument subclass:

Cool! I owe you big. The JDC bug is #4777579, which isn't showing up
in the Bug Parade interface yet; mind if I copy your comments on the
problem here into a comment on the bug report when it shows up?

(Incidentally, I'm impressed with the response time that this bug
received... I reported it yesterday, and it was accepted today. That's
very different from the multiple-month turnaround I've seen in the
past!)

Christian Kaufhold

unread,
Nov 12, 2002, 4:02:10 PM11/12/02
to
Hello!

Chris Smith <cds...@twu.net> wrote:

> Christian Kaufhold wrote ...

>> Chris Smith <cds...@twu.net> wrote:
>>
>> > Just ran across this quirk with JEditorPane. Anyone have a solution?
>>
>>
>> Put this (or similar) into a a HTMLDocument subclass:
>
> Cool! I owe you big. The JDC bug is #4777579, which isn't showing up
> in the Bug Parade interface yet; mind if I copy your comments on the
> problem here into a comment on the bug report when it shows up?

Actually it should suffice to refer to #4736703 (and maybe add that
View depends on this fixed), which was meanwhile accepted.

I vaguely remember this (incorrectly) being evalutated as "not a bug"
in some other bug report, but I cannot find it anymore.

>
> (Incidentally, I'm impressed with the response time that this bug
> received... I reported it yesterday, and it was accepted today. That's
> very different from the multiple-month turnaround I've seen in the
> past!)

I haven't received an answer yet for a (trivial) bug I submitted more
than four weeks ago.


Christian

Java_Programmer

unread,
Nov 18, 2002, 2:48:37 PM11/18/02
to
Hi,

First of all, thanks for posting the bug. I made sure an undo and
redo's
UndoableEdit type is a correct one. So that's fixed in my code for
sure.


But I still get NullPointerException when I undo and redo
several times.


java.lang.NullPointerException
at javax.swing.text.FlowView$FlowStrategy.createView(FlowView.java:568)
at javax.swing.text.FlowView$FlowStrategy.layoutRow(FlowView.java:441)
at javax.swing.text.FlowView$FlowStrategy.layout(FlowView.java:397)
at javax.swing.text.FlowView.layout(FlowView.java:182)
at javax.swing.text.BoxView.setSize(BoxView.java:379)
at javax.swing.text.BoxView.updateChildSizes(BoxView.java:348)
at javax.swing.text.BoxView.setSpanOnAxis(BoxView.java:316)
at javax.swing.text.BoxView.layout(BoxView.java:683)
at javax.swing.text.BoxView.setSize(BoxView.java:379)
at javax.swing.plaf.basic.BasicTextUI$RootView.setSize(BasicTextUI.java:1528)
at javax.swing.plaf.basic.BasicTextUI.getPreferredSize(BasicTextUI.java:730)
at javax.swing.JComponent.getPreferredSize(JComponent.java:1262)
at javax.swing.JEditorPane.getPreferredSize(JEditorPane.java:1206)
at javax.swing.ScrollPaneLayout.layoutContainer(ScrollPaneLayout.java:767)
at java.awt.Container.layout(Container.java:835)
at java.awt.Container.doLayout(Container.java:825)
at java.awt.Container.validateTree(Container.java:903)
at java.awt.Container.validate(Container.java:878)
at javax.swing.RepaintManager.validateInvalidComponents(RepaintManager.java:347)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:116)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:178)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:443)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:190)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:144)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:130)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:98)


Did you guys get the exception from createView() also?
Do you know why it's doing this?

use...@chka.de (Christian Kaufhold) wrote in message news:<0t3dd169e...@simia.chka.de>...

Java_Programmer

unread,
Nov 18, 2002, 6:12:05 PM11/18/02
to
Hi,

Chris Smith

unread,
Nov 18, 2002, 8:35:04 PM11/18/02
to
Java_Programmer wrote ...

> First of all, thanks for posting the bug. I made sure an undo and
> redo's
> UndoableEdit type is a correct one. So that's fixed in my code for
> sure.
>
>
> But I still get NullPointerException when I undo and redo
> several times.

[stack trace snipped]

> Did you guys get the exception from createView() also?
> Do you know why it's doing this?

As you can see in my original post and on Bug Parade #4777579, the stack
trace I get is not inside any call to createView. Christian's fix here
was rather straight-forward, so if his code doesn't fix your case, it's
likely to be a different bug.

Can you come up with a reproducible test case? It looks as if the View
hierarchy is not being properly updated in response to some Document
change... but I couldn't say where that's happening because I can't
produce your failure in tests that I've tried. If you could give more
explicit instructions and code for how to reproducibly cause a failure,
I'd be happy to look into it (esp. since our own commercial product may
also be affected by the bug).

Java_Programmer

unread,
Nov 20, 2002, 5:17:55 PM11/20/02
to
Hi,

Thanks for your reply.
I figured it out, I think.
First of all, I was only adding INSERT and REMOVE undoableEdits to
undoManager because I only wanted to allow text changes, not style
changes. I ended up with combining INSERT and CHANGE edits as one
edit and undo that.
Also, I was calling some coloring code in a response to some
document change (INSERT or REMOVE).(i.e. in DocumentListsner code)
The coloring code calls setCharacterAttributes(), so it itself fires
DocumentEvent (I think..)
and it seemed to be a bad idea to call that in insertUpdate().
Now it seems to be working.

(Anyway, I was using JTextPane + StyledEditorKit).

Chris Smith <cds...@twu.net> wrote in message news:<MPG.184343354...@news.altopia.com>...

0 new messages