# HG changeset patch # User malenkov # Date 1377696745 -14400 # Node ID f640c22218a3d22425e08cd1fe1a98cfc1f55339 # Parent 30b8aaa80ae2bd11d7ff2fe3dd2adaa19099c2d6 6968363: ClassCastException while entering HINDI characters with CustomDocument Reviewed-by: alexsch diff -r 30b8aaa80ae2 -r f640c22218a3 jdk/src/share/classes/javax/swing/text/AbstractDocument.java --- a/jdk/src/share/classes/javax/swing/text/AbstractDocument.java Wed Aug 28 17:25:35 2013 +0400 +++ b/jdk/src/share/classes/javax/swing/text/AbstractDocument.java Wed Aug 28 17:32:25 2013 +0400 @@ -934,16 +934,18 @@ * Returns true if the text in the range p0 to * p1 is left to right. */ - boolean isLeftToRight(int p0, int p1) { - if(!getProperty(I18NProperty).equals(Boolean.TRUE)) { - return true; - } - Element bidiRoot = getBidiRootElement(); - int index = bidiRoot.getElementIndex(p0); - Element bidiElem = bidiRoot.getElement(index); - if(bidiElem.getEndOffset() >= p1) { - AttributeSet bidiAttrs = bidiElem.getAttributes(); - return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0); + static boolean isLeftToRight(Document doc, int p0, int p1) { + if (Boolean.TRUE.equals(doc.getProperty(I18NProperty))) { + if (doc instanceof AbstractDocument) { + AbstractDocument adoc = (AbstractDocument) doc; + Element bidiRoot = adoc.getBidiRootElement(); + int index = bidiRoot.getElementIndex(p0); + Element bidiElem = bidiRoot.getElement(index); + if (bidiElem.getEndOffset() >= p1) { + AttributeSet bidiAttrs = bidiElem.getAttributes(); + return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0); + } + } } return true; } diff -r 30b8aaa80ae2 -r f640c22218a3 jdk/src/share/classes/javax/swing/text/DefaultCaret.java --- a/jdk/src/share/classes/javax/swing/text/DefaultCaret.java Wed Aug 28 17:25:35 2013 +0400 +++ b/jdk/src/share/classes/javax/swing/text/DefaultCaret.java Wed Aug 28 17:32:25 2013 +0400 @@ -1211,12 +1211,9 @@ boolean isPositionLTR(int position, Position.Bias bias) { Document doc = component.getDocument(); - if(doc instanceof AbstractDocument ) { - if(bias == Position.Bias.Backward && --position < 0) - position = 0; - return ((AbstractDocument)doc).isLeftToRight(position, position); - } - return true; + if(bias == Position.Bias.Backward && --position < 0) + position = 0; + return AbstractDocument.isLeftToRight(doc, position, position); } Position.Bias guessBiasForOffset(int offset, Position.Bias lastBias, diff -r 30b8aaa80ae2 -r f640c22218a3 jdk/src/share/classes/javax/swing/text/GlyphPainter2.java --- a/jdk/src/share/classes/javax/swing/text/GlyphPainter2.java Wed Aug 28 17:25:35 2013 +0400 +++ b/jdk/src/share/classes/javax/swing/text/GlyphPainter2.java Wed Aug 28 17:32:25 2013 +0400 @@ -239,10 +239,10 @@ Position.Bias[] biasRet) throws BadLocationException { + Document doc = v.getDocument(); int startOffset = v.getStartOffset(); int endOffset = v.getEndOffset(); Segment text; - AbstractDocument doc; boolean viewIsLeftToRight; TextHitInfo currentHit, nextHit; @@ -252,8 +252,7 @@ case View.SOUTH: break; case View.EAST: - doc = (AbstractDocument)v.getDocument(); - viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset); + viewIsLeftToRight = AbstractDocument.isLeftToRight(doc, startOffset, endOffset); if(startOffset == doc.getLength()) { if(pos == -1) { @@ -313,8 +312,7 @@ } return pos; case View.WEST: - doc = (AbstractDocument)v.getDocument(); - viewIsLeftToRight = doc.isLeftToRight(startOffset, endOffset); + viewIsLeftToRight = AbstractDocument.isLeftToRight(doc, startOffset, endOffset); if(startOffset == doc.getLength()) { if(pos == -1) { diff -r 30b8aaa80ae2 -r f640c22218a3 jdk/src/share/classes/javax/swing/text/ParagraphView.java --- a/jdk/src/share/classes/javax/swing/text/ParagraphView.java Wed Aug 28 17:25:35 2013 +0400 +++ b/jdk/src/share/classes/javax/swing/text/ParagraphView.java Wed Aug 28 17:32:25 2013 +0400 @@ -267,8 +267,6 @@ throws BadLocationException { JTextComponent text = (JTextComponent)getContainer(); Document doc = getDocument(); - AbstractDocument aDoc = (doc instanceof AbstractDocument) ? - (AbstractDocument)doc : null; View row = getView(rowIndex); int lastPos = -1; // This could be made better to check backward positions too. @@ -276,8 +274,7 @@ for(int vc = 0, numViews = row.getViewCount(); vc < numViews; vc++) { View v = row.getView(vc); int start = v.getStartOffset(); - boolean ltr = (aDoc != null) ? aDoc.isLeftToRight - (start, start + 1) : true; + boolean ltr = AbstractDocument.isLeftToRight(doc, start, start + 1); if(ltr) { lastPos = start; for(int end = v.getEndOffset(); lastPos < end; lastPos++) { @@ -338,12 +335,8 @@ protected boolean flipEastAndWestAtEnds(int position, Position.Bias bias) { Document doc = getDocument(); - if(doc instanceof AbstractDocument && - !((AbstractDocument)doc).isLeftToRight(getStartOffset(), - getStartOffset() + 1)) { - return true; - } - return false; + position = getStartOffset(); + return !AbstractDocument.isLeftToRight(doc, position, position + 1); } // --- FlowView methods --------------------------------------------- diff -r 30b8aaa80ae2 -r f640c22218a3 jdk/test/javax/swing/text/AbstractDocument/6968363/Test6968363.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/text/AbstractDocument/6968363/Test6968363.java Wed Aug 28 17:32:25 2013 +0400 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import sun.awt.SunToolkit; + +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.event.DocumentListener; +import javax.swing.event.UndoableEditListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.PlainDocument; +import javax.swing.text.Position; +import javax.swing.text.Segment; + +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_LEFT; +import static javax.swing.SwingUtilities.invokeAndWait; + +/* + * @test + * @bug 6968363 + * @summary Ensures that a custom document may not extend AbstractDocument + * @author Sergey Malenkov + */ +public class Test6968363 implements Runnable, Thread.UncaughtExceptionHandler { + private JFrame frame; + + public static void main(String[] args) throws Exception { + SunToolkit toolkit = (SunToolkit) getDefaultToolkit(); + Runnable task = new Test6968363(); + invokeAndWait(task); + toolkit.realSync(100); + new Robot().keyPress(VK_LEFT); + toolkit.realSync(100); + invokeAndWait(task); + } + + @Override + public void uncaughtException(Thread thread, Throwable throwable) { + throwable.printStackTrace(); + System.exit(1); + } + + @Override + public void run() { + if (this.frame == null) { + Thread.setDefaultUncaughtExceptionHandler(this); + this.frame = new JFrame(getClass().getSimpleName()); + this.frame.add(NORTH, new JLabel("Copy Paste a HINDI text into the field below")); + this.frame.add(SOUTH, new JTextField(new MyDocument(), "\u0938", 10)); + this.frame.pack(); + this.frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + this.frame.setLocationRelativeTo(null); + this.frame.setVisible(true); + } else { + this.frame.dispose(); + this.frame = null; + } + } + + private static class MyDocument implements Document { + private final Document document = new PlainDocument(); + + @Override + public int getLength() { + return this.document.getLength(); + } + + @Override + public void addDocumentListener(DocumentListener listener) { + this.document.addDocumentListener(listener); + } + + @Override + public void removeDocumentListener(DocumentListener listener) { + this.document.removeDocumentListener(listener); + } + + @Override + public void addUndoableEditListener(UndoableEditListener listener) { + this.document.addUndoableEditListener(listener); + } + + @Override + public void removeUndoableEditListener(UndoableEditListener listener) { + this.document.removeUndoableEditListener(listener); + } + + @Override + public Object getProperty(Object key) { + return this.document.getProperty(key); + } + + @Override + public void putProperty(Object key, Object value) { + this.document.putProperty(key, value); + } + + @Override + public void remove(int offset, int length) throws BadLocationException { + this.document.remove(offset, length); + } + + @Override + public void insertString(int offset, String string, AttributeSet set) throws BadLocationException { + for (int i = 0; i < string.length(); i++) { + System.out.println("i: " + i + "; ch: " + Integer.toHexString(string.charAt(i))); + } + this.document.insertString(offset, string, set); + } + + @Override + public String getText(int offset, int length) throws BadLocationException { + return this.document.getText(offset, length); + } + + @Override + public void getText(int offset, int length, Segment segment) throws BadLocationException { + this.document.getText(offset, length, segment); + } + + @Override + public Position getStartPosition() { + return this.document.getStartPosition(); + } + + @Override + public Position getEndPosition() { + return this.document.getEndPosition(); + } + + @Override + public Position createPosition(int offset) throws BadLocationException { + return this.document.createPosition(offset); + } + + @Override + public Element[] getRootElements() { + Element[] elements = this.document.getRootElements(); + Element[] wrappers = new Element[elements.length]; + for (int i = 0; i < elements.length; i++) { + wrappers[i] = new MyElement(elements[i]); + } + return wrappers; + } + + @Override + public Element getDefaultRootElement() { + return new MyElement(this.document.getDefaultRootElement()); + } + + @Override + public void render(Runnable task) { + this.document.render(task); + } + + private class MyElement implements Element { + private final Element element; + + private MyElement(Element element) { + this.element = element; + } + + @Override + public Document getDocument() { + return MyDocument.this; + } + + @Override + public Element getParentElement() { + return new MyElement(this.element.getParentElement()); + } + + @Override + public String getName() { + return this.element.getName(); + } + + @Override + public AttributeSet getAttributes() { + return this.element.getAttributes(); + } + + @Override + public int getStartOffset() { + return this.element.getStartOffset(); + } + + @Override + public int getEndOffset() { + return this.element.getEndOffset(); + } + + @Override + public int getElementIndex(int offset) { + return this.element.getElementIndex(offset); + } + + @Override + public int getElementCount() { + return this.element.getElementCount(); + } + + @Override + public Element getElement(int index) { + return new MyElement(this.element.getElement(index)); + } + + @Override + public boolean isLeaf() { + return this.element.isLeaf(); + } + } + } +}