Странная упаковка текста со стилизованным текстом в JTextPane с Java 7


У меня есть два разных редактора, использующих JTextPane со странными ошибками в Java 7, которые не произошли с предыдущими версиями JVM. Это происходит с длинными строками, содержащими стилизованный текст или компоненты.

Вот пример, демонстрирующий эту ошибку. В этом примере стиль по умолчанию применяется для всего текста при каждой вставке символа. Я протестировал его с JDK 1.7.0_04.

import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class BugWrapJava7 extends JFrame {

    JTextPane jtp;
    StyledDocument doc;

    public BugWrapJava7() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        jtp = new JTextPane();
        add(jtp, BorderLayout.CENTER);
        jtp.setText("ntype some text in the above empty line and check the wrapping behavior");
        doc = jtp.getStyledDocument();
        doc.addDocumentListener(new DocumentListener() {
            public void insertUpdate(DocumentEvent e) {
                insert();
            }
            public void removeUpdate(DocumentEvent e) {
            }
            public void changedUpdate(DocumentEvent e) {
            }
        });
        setSize(200, 200);
        setVisible(true);
    }
    public void insert() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE);
                doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false);
            }
        });
    }
    public static void main(String[] args) {
        new BugWrapJava7();
    }
}

Мой вопрос: есть ли что-то неправильное в моем коде, или это действительно новая ошибка, введенная в Java 7 ? И если это новая ошибка JVM, есть ли обходной путь ?

Это может быть связано с вопросом 8666727, но проблема здесь заключается в неправильном обертывании, а не в появлении полосы прокрутки.

2 14

2 ответа:

Для читателей фьючерсов, ошибка все еще присутствует в JDK 1.7.0_04.,

Сравнивая Java7 и стабильный Java6,

Введите описание изображения здесьВведите описание изображения здесь

Введите описание изображения здесьВведите описание изображения здесь

Введите описание изображения здесьВведите описание изображения здесь

Введите описание изображения здесьВведите описание изображения здесь

Из кода

import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class BugWrapJava7 {

    private JFrame frame = new JFrame();
    private JTextPane jtp;
    private StyledDocument doc;

    public BugWrapJava7() {
        jtp = new JTextPane();
        jtp.setText("\ntype some text in the above empty line and check the wrapping behavior");
        doc = jtp.getStyledDocument();
        doc.addDocumentListener(new DocumentListener() {

            public void insertUpdate(DocumentEvent e) {
                insert();
            }

            public void removeUpdate(DocumentEvent e) {
                insert();
            }

            public void changedUpdate(DocumentEvent e) {
                insert();
            }

            public void insert() {
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE);
                        doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false);
                    }
                });
            }
        });
        JScrollPane scroll = new JScrollPane(jtp);
        scroll.setPreferredSize(new Dimension(200, 200));
        frame.add(scroll);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                BugWrapJava7 bugWrapJava7 = new BugWrapJava7();
            }
        });
    }
}

Исследовал это. Причина-кэширование точек останова. Похоже, что LabelView хранит их и не пересчитывает смещения при редактировании текста previos. Если я сброшу их вручную, ошибки не произойдет.

Обходной путь (очень грязный из-за частных полей точек останова) следующий

import java.awt.Dimension;
import java.lang.reflect.Field;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class BugWrapJava7 {

    private JFrame frame = new JFrame();
    private JTextPane jtp;
    private StyledDocument doc;

    public BugWrapJava7() {
        jtp = new JTextPane();
        jtp.setEditorKit(new MyStyledEditorKit());
        jtp.setText("\ntype some text in the above empty line and check the wrapping behavior");
        doc = jtp.getStyledDocument();
        doc.addDocumentListener(new DocumentListener() {

            public void insertUpdate(DocumentEvent e) {
                insert();
            }

            public void removeUpdate(DocumentEvent e) {
                insert();
            }

            public void changedUpdate(DocumentEvent e) {
                insert();
            }

            public void insert() {
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE);
                        doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false);
                    }
                });
            }
        });
        JScrollPane scroll = new JScrollPane(jtp);
        scroll.setPreferredSize(new Dimension(200, 200));
        frame.add(scroll);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                BugWrapJava7 bugWrapJava7 = new BugWrapJava7();
            }
        });
    }
}

class MyStyledEditorKit extends StyledEditorKit {
    private MyFactory factory;

    public ViewFactory getViewFactory() {
        if (factory == null) {
            factory = new MyFactory();
        }
        return factory;
    }
}

class MyFactory implements ViewFactory {
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new MyLabelView(elem);
            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                return new ParagraphView(elem);
            } else if (kind.equals(AbstractDocument.SectionElementName)) {
                return new BoxView(elem, View.Y_AXIS);
            } else if (kind.equals(StyleConstants.ComponentElementName)) {
                return new ComponentView(elem);
            } else if (kind.equals(StyleConstants.IconElementName)) {
                return new IconView(elem);
            }
        }

        // default to text display
        return new LabelView(elem);
    }
}

class MyLabelView extends LabelView {
    public MyLabelView(Element elem) {
        super(elem);
    }
    public View breakView(int axis, int p0, float pos, float len) {
        if (axis == View.X_AXIS) {
            resetBreakSpots();
        }
        return super.breakView(axis, p0, pos, len);
    }

    private void resetBreakSpots() {
        try {
            // HACK the breakSpots private fields
            Field f=GlyphView.class.getDeclaredField("breakSpots");
            f.setAccessible(true);
            f.set(this, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Меньше рубить без отражения. Основан на обычном сбросе точек пробоя при смене модели.

class MyLabelView extends LabelView {

    boolean isResetBreakSpots=false;

    public MyLabelView(Element elem) {
        super(elem);
    }
    public View breakView(int axis, int p0, float pos, float len) {
        if (axis == View.X_AXIS) {
            resetBreakSpots();
        }
        return super.breakView(axis, p0, pos, len);
    }

    private void resetBreakSpots() {
        isResetBreakSpots=true;
        removeUpdate(null, null, null);
        isResetBreakSpots=false;

//        try {
//            Field f=GlyphView.class.getDeclaredField("breakSpots");
//            f.setAccessible(true);
//            f.set(this, null);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
        super.removeUpdate(e, a, f);
    }

    public void preferenceChanged(View child, boolean width, boolean height) {
        if (!isResetBreakSpots) {
            super.preferenceChanged(child, width, height);
        }
    }
}

Обновление: это исправляет TextSamplerDemo, а также. Я сбрасываю все пятна для всех меток Просмотры.

class MyStyledEditorKit extends StyledEditorKit {
    private MyFactory factory;

    public ViewFactory getViewFactory() {
        if (factory == null) {
            factory = new MyFactory();
        }
        return factory;
    }
}

class MyFactory implements ViewFactory {
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new MyLabelView(elem);
            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                return new MyParagraphView(elem);
            } else if (kind.equals(AbstractDocument.SectionElementName)) {
                return new BoxView(elem, View.Y_AXIS);
            } else if (kind.equals(StyleConstants.ComponentElementName)) {
                return new ComponentView(elem);
            } else if (kind.equals(StyleConstants.IconElementName)) {
                return new IconView(elem);
            }
        }

        // default to text display
        return new LabelView(elem);
    }
}

class MyParagraphView extends ParagraphView {

    public MyParagraphView(Element elem) {
        super(elem);
    }
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    super.removeUpdate(e, a, f);
    resetBreakSpots();
}
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
    super.insertUpdate(e, a, f);
    resetBreakSpots();
}

private void resetBreakSpots() {
    for (int i=0; i<layoutPool.getViewCount(); i++) {
        View v=layoutPool.getView(i);
        if (v instanceof MyLabelView) {
            ((MyLabelView)v).resetBreakSpots();
        }
    }
}

}

class MyLabelView extends LabelView {

    boolean isResetBreakSpots=false;

    public MyLabelView(Element elem) {
        super(elem);
    }
    public View breakView(int axis, int p0, float pos, float len) {
        if (axis == View.X_AXIS) {
            resetBreakSpots();
        }
        return super.breakView(axis, p0, pos, len);
    }

    public void resetBreakSpots() {
        isResetBreakSpots=true;
        removeUpdate(null, null, null);
        isResetBreakSpots=false;
   }

    public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
        super.removeUpdate(e, a, f);
    }

    public void preferenceChanged(View child, boolean width, boolean height) {
        if (!isResetBreakSpots) {
            super.preferenceChanged(child, width, height);
        }
    }
}