Странное перенос текста со стилизованным текстом в 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 ответа

Решение

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

сравнивая Java7 и стабильную Java6,

<------ Java7 против Java6 --->

<------ Java7 против Java6 --->

<------ Java7 против Java6 --->

<------ 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();
            }
        });
    }
}

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

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

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);
        }
    }
}

Мой HTML-документ упаковывался по буквам. Я установил решение StanislavL, указанное выше. Никакой радости, все еще обертывание по буквам внутри элементов . Итак, я исправил ошибку в коде StanislavL. Радость; упаковка по слову везде. Потом я обнаружил, что моя программа не использует код StanislavL! Кажется, что мои попытки исправили ту же ошибку в моем собственном коде.

Проблема в том, что существует два разных типа элементов (в javax.swing.text и javax.swing.text.html.parser), и оба имеют имя типа тега со строковым значением "content". Метод Element.getName сбивает это с толку, возвращая String вместо Object.

Итак, где в коде написано ".equals()"

    String kind = elem.getName();
    if (kind != null) {
        if (kind.equals(AbstractDocument.ContentElementName)) {
    ...

он должен сказать "=="

    Object absDocName = attrs.getAttribute(AbstractDocument.ElementNameAttribute)
    if (absDocName != null){
        if (kind == AbstractDocument.ContentElementName) {
    ...
Другие вопросы по тегам