8176072: READING attributes are not available on TSF
authorssreedharan
Fri, 24 Nov 2017 13:18:36 +0530
changeset 47974 9ec74010cadf
parent 47973 d5774c36c605
child 47975 5e86806f57f9
8176072: READING attributes are not available on TSF Reviewed-by: ssadetsky
src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java
src/java.desktop/windows/native/libawt/windows/awt_InputTextInfor.cpp
test/jdk/javax/swing/JTextField/JapaneseReadingAttributes/JapaneseReadingAttributes.java
--- a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java	Thu Nov 23 23:05:52 2017 -0800
+++ b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java	Fri Nov 24 13:18:36 2017 +0530
@@ -498,7 +498,7 @@
             // set Clause and Reading Information
             if (clauseBoundary!=null && clauseReading!=null &&
                 clauseReading.length!=0 && clauseBoundary.length==clauseReading.length+1 &&
-                clauseBoundary[0]==0 && clauseBoundary[clauseReading.length]==text.length() )
+                clauseBoundary[0]==0 && clauseBoundary[clauseReading.length]<=text.length() )
             {
                 for (int i=0; i<clauseBoundary.length-1; i++) {
                     attrStr.addAttribute(Attribute.INPUT_METHOD_SEGMENT,
--- a/src/java.desktop/windows/native/libawt/windows/awt_InputTextInfor.cpp	Thu Nov 23 23:05:52 2017 -0800
+++ b/src/java.desktop/windows/native/libawt/windows/awt_InputTextInfor.cpp	Fri Nov 24 13:18:36 2017 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -202,9 +202,15 @@
          m_lpClauseW == NULL || m_lpReadClauseW == NULL ||
          m_lpClauseW[0] != 0 || m_lpClauseW[m_cClauseW] != (DWORD)m_cStrW ||
          m_lpReadClauseW[0] != 0 || m_lpReadClauseW[m_cReadClauseW] != (DWORD)m_cReadStrW) {
-        lpBndClauseW = NULL;
-        lpReadingClauseW = NULL;
-        return 0;
+        // For cases where IMM sends WM_IME_COMPOSITION with both GCS_COMPSTR and GCS_RESULTSTR
+        // The GCS_RESULTSTR part may have Caluse and Reading information which should not be ignored
+        if (NULL == m_pResultTextInfor) {
+            lpBndClauseW = NULL;
+            lpReadingClauseW = NULL;
+            return 0;
+        } else {
+            return m_pResultTextInfor->GetClauseInfor(lpBndClauseW, lpReadingClauseW);
+        }
     }
 
     int*    bndClauseW = NULL;
@@ -346,10 +352,14 @@
 //
 int AwtInputTextInfor::GetAttributeInfor(int*& lpBndAttrW, BYTE*& lpValAttrW) {
     if (m_cStrW == 0 || m_cAttrW != m_cStrW) {
-        lpBndAttrW = NULL;
-        lpValAttrW = NULL;
+        if (NULL == m_pResultTextInfor) {
+            lpBndAttrW = NULL;
+            lpValAttrW = NULL;
 
-        return 0;
+            return 0;
+        } else {
+            return m_pResultTextInfor->GetAttributeInfor(lpBndAttrW, lpValAttrW);
+        }
     }
 
     int* bndAttrW = NULL;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/JTextField/JapaneseReadingAttributes/JapaneseReadingAttributes.java	Fri Nov 24 13:18:36 2017 +0530
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+/*
+ * @test
+ * @key headful
+ * @bug 8176072
+ * @summary Checks whether reading attributes are obtained for Japanese IME
+ * @requires (os.family == "windows")
+ * @run main/manual JapaneseReadingAttributes
+ */
+
+/**
+ * This test requires a manual intervention as the keyboard layout has to be
+ * changed to Japanese IME. Once the keyboard layout has been selected, click on
+ * Start Test to start the automated tests. Will run two passes, first with an
+ * enter key in between to generate the yomigana for the first block of
+ * characters. The second without the intermediate enter key. Without the fix,
+ * there will be a mismatch in the reading attributes obtained.
+ */
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Robot;
+import java.awt.event.InputMethodEvent;
+import java.awt.event.InputMethodListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+import javax.swing.WindowConstants;
+import java.awt.event.KeyEvent;
+import java.text.AttributedCharacterIterator;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+
+public class JapaneseReadingAttributes {
+    private static boolean testPassed = false;
+    private static boolean startTest = false;
+
+    private static JFrame frame = null;
+    private static JLabel lblTestStatus = null;
+    private static JTextField textFieldMain = null;
+    private static JTextField textFieldReading = null;
+    private static String testResult;
+    private static String readingPass1;
+    private static String readingPass2;
+
+    private static final CountDownLatch testStartLatch = new CountDownLatch(1);
+
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(() -> {
+            setupUI();
+        });
+
+        testStartLatch.await();
+
+        if (startTest) {
+            glyphTest();
+
+            frame.dispose();
+
+            if (testPassed) {
+                System.out.println(testResult);
+            } else {
+                throw new RuntimeException(testResult);
+            }
+        } else {
+            throw new RuntimeException("User has not executed the test");
+        }
+    }
+
+    private static void setupUI() {
+        String description = " 1. Go to \"Language Preferences -> Add a Language"
+                            + "\" and add \"Japanese\"\n"
+                            + " 2. Set current IM to \"Japanese\" \n"
+                            + " 3. Try typing in the text field to ensure"
+                            + " that Japanese IME has been successfully"
+                            + " selected \n"
+                            + " 4. Now click on \"Start Test\" button \n";
+        String title = "Reading Attributes test Japanese IME (Windows)";
+
+        frame = new JFrame(title);
+
+        JPanel mainPanel = new JPanel(new BorderLayout());
+
+        JPanel textEditPanel = new JPanel(new FlowLayout());
+
+        textFieldMain = new JTextField(20);
+
+        textFieldReading = new JTextField(20);
+        textFieldReading.setEditable(false);
+
+        textEditPanel.add(textFieldMain);
+        textEditPanel.add(textFieldReading);
+
+        mainPanel.add(textEditPanel, BorderLayout.CENTER);
+
+        JTextArea textArea = new JTextArea(description);
+        textArea.setEditable(false);
+        final JButton btnStartTest = new JButton("Start Test");
+        final JButton btnCancelTest = new JButton("Cancel Test");
+
+        btnStartTest.addActionListener((e) -> {
+            btnStartTest.setEnabled(false);
+            btnCancelTest.setEnabled(false);
+            startTest = true;
+            testStartLatch.countDown();
+        });
+
+        btnCancelTest.addActionListener((e) -> {
+            frame.dispose();
+            testStartLatch.countDown();
+        });
+        mainPanel.add(textArea, BorderLayout.NORTH);
+
+        JPanel buttonPanel = new JPanel(new FlowLayout());
+        buttonPanel.add(btnStartTest);
+        buttonPanel.add(btnCancelTest);
+        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+        lblTestStatus = new JLabel("");
+        lblTestStatus.setMinimumSize(new Dimension(250, 20));
+        lblTestStatus.setPreferredSize(new Dimension(250, 20));
+        lblTestStatus.setVisible(true);
+        textEditPanel.add(lblTestStatus);
+
+        frame.add(mainPanel);
+        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+        frame.pack();
+        frame.setLocationRelativeTo(null);
+
+        frame.addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowClosing(WindowEvent e) {
+                testStartLatch.countDown();
+            }
+            @Override
+            public void windowOpened( WindowEvent e ){
+                textFieldMain.requestFocusInWindow();
+            }
+        });
+
+        textFieldMain.addInputMethodListener(new InputMethodListener() {
+            @Override
+            public void caretPositionChanged(InputMethodEvent event) {
+            }
+
+            @Override
+            public void inputMethodTextChanged(InputMethodEvent event) {
+                AttributedCharacterIterator itr = event.getText();
+                if (itr != null) {
+                    int toCopy = event.getCommittedCharacterCount();
+                    if (toCopy > 0) {
+                        itr.first();
+                        StringBuilder yomigana = new StringBuilder(
+                                textFieldReading.getText());
+                        while (toCopy-- > 0) {
+                            if (itr.getIndex() == itr.getRunStart(
+                                    AttributedCharacterIterator.Attribute.READING)) {
+                                java.text.Annotation annotatedText
+                                        = (java.text.Annotation) itr.
+                                                getAttribute(AttributedCharacterIterator.Attribute.READING);
+                                yomigana.append(annotatedText.getValue());
+                            }
+                            itr.next();
+                        }
+                        textFieldReading.setText(yomigana.toString());
+                    }
+                }
+            }
+        });
+
+        frame.setVisible(true);
+    }
+
+    private static void glyphTest() throws Exception {
+        Robot robotKeySimulator = new Robot();
+        performTasks(robotKeySimulator);
+    }
+
+    public static void performTasks(Robot robotForKeyInput) throws Exception {
+        lblTestStatus.setText("Running Tests..");
+        robotForKeyInput.setAutoDelay(500);
+
+        ArrayList<Integer> keyCodesToUse = new ArrayList<Integer>();
+
+        keyCodesToUse.add(KeyEvent.VK_A);
+        keyCodesToUse.add(KeyEvent.VK_B);
+        keyCodesToUse.add(KeyEvent.VK_E);
+        keyCodesToUse.add(KeyEvent.VK_SPACE);
+        keyCodesToUse.add(KeyEvent.VK_SPACE);
+        keyCodesToUse.add(KeyEvent.VK_ENTER);
+        keyCodesToUse.add(KeyEvent.VK_S);
+        keyCodesToUse.add(KeyEvent.VK_I);
+        keyCodesToUse.add(KeyEvent.VK_N);
+        keyCodesToUse.add(KeyEvent.VK_Z);
+        keyCodesToUse.add(KeyEvent.VK_O);
+        keyCodesToUse.add(KeyEvent.VK_U);
+        keyCodesToUse.add(KeyEvent.VK_SPACE);
+        keyCodesToUse.add(KeyEvent.VK_ENTER);
+
+        textFieldMain.requestFocusInWindow();
+
+        robotForKeyInput.waitForIdle();
+
+        enterInput(robotForKeyInput, keyCodesToUse);
+
+        SwingUtilities.invokeAndWait(() -> {
+            readingPass1 = textFieldReading.getText();
+        });
+
+        if (setTaskStatus(readingPass1, 1)) {
+            keyCodesToUse.remove((Integer) KeyEvent.VK_ENTER);
+
+            enterInput(robotForKeyInput, keyCodesToUse);
+
+            SwingUtilities.invokeAndWait(() -> {
+                readingPass2 = textFieldReading.getText();
+            });
+
+            if (setTaskStatus(readingPass2, 2)) {
+                if (readingPass1.equals(readingPass2)) {
+                    testPassed = true;
+                    testResult = "Test Passed : Same reading attribute "
+                            + "obtained from both passes ";
+                    lblTestStatus.setText(testResult);
+                } else {
+                    testResult = "Test Failed : Reading attribute from Pass 1 <"
+                            + readingPass1 + "> != Reading attribute "
+                            + "from Pass 2 <" + readingPass2 + ">";
+                }
+            }
+        }
+    }
+
+    private static void enterInput(Robot robotKeyInput,
+            ArrayList<Integer> keyInputs) {
+        textFieldReading.setText("");
+        textFieldMain.setText("");
+
+        String strKeyInput = "KeyPress=>";
+        int nOfKeyInputs = keyInputs.size();
+        for (int i = 0; i < nOfKeyInputs; i++) {
+            int keyToUse = keyInputs.get(i);
+            robotKeyInput.keyPress(keyToUse);
+            robotKeyInput.keyRelease(keyToUse);
+            strKeyInput += (Integer.toHexString(keyToUse)) + ":";
+        }
+
+        System.out.println(strKeyInput);
+    }
+
+    public static boolean setTaskStatus(String readingValue, int passCount) {
+        boolean status = false;
+
+        if (!readingValue.isEmpty()) {
+            testResult = "Attribute : " + readingValue
+                    + "read from pass " + Integer.toString(passCount);
+            status = true;
+        } else {
+            testResult = "Failed to read Reading attribute from pass  "
+                    + Integer.toString(passCount);
+        }
+
+        lblTestStatus.setText(testResult);
+
+        return status;
+    }
+}