0N/A/*
3855N/A * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * Redistribution and use in source and binary forms, with or without
0N/A * modification, are permitted provided that the following conditions
0N/A * are met:
0N/A *
0N/A * - Redistributions of source code must retain the above copyright
0N/A * notice, this list of conditions and the following disclaimer.
0N/A *
0N/A * - Redistributions in binary form must reproduce the above copyright
0N/A * notice, this list of conditions and the following disclaimer in the
0N/A * documentation and/or other materials provided with the distribution.
0N/A *
2362N/A * - Neither the name of Oracle nor the names of its
0N/A * contributors may be used to endorse or promote products derived
0N/A * from this software without specific prior written permission.
0N/A *
0N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
0N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0N/A */
4378N/A
4378N/A/*
4378N/A * This source code is provided to illustrate the usage of a given feature
4378N/A * or technique and has been deliberately simplified. Additional steps
4378N/A * required for a production-quality application, such as security checks,
4378N/A * input validation and proper error handling, might not be present in
4378N/A * this sample code.
4378N/A */
4378N/A
3855N/Apackage com.sun.inputmethods.internal.codepointim;
0N/A
0N/A
0N/Aimport java.awt.AWTEvent;
0N/Aimport java.awt.Toolkit;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.event.InputMethodEvent;
0N/Aimport java.awt.event.KeyEvent;
0N/Aimport java.awt.font.TextAttribute;
0N/Aimport java.awt.font.TextHitInfo;
0N/Aimport java.awt.im.InputMethodHighlight;
0N/Aimport java.awt.im.spi.InputMethod;
0N/Aimport java.awt.im.spi.InputMethodContext;
0N/Aimport java.io.IOException;
0N/Aimport java.text.AttributedString;
0N/Aimport java.util.Locale;
0N/A
3855N/A
0N/A/**
0N/A * The Code Point Input Method is a simple input method that allows Unicode
0N/A * characters to be entered using their code point or code unit values. See the
0N/A * accompanying file README.txt for more information.
0N/A *
0N/A * @author Brian Beck
0N/A */
0N/Apublic class CodePointInputMethod implements InputMethod {
0N/A
3855N/A private static final int UNSET = 0;
3855N/A private static final int ESCAPE = 1; // \u0000 - \uFFFF
3855N/A private static final int SPECIAL_ESCAPE = 2; // \U000000 - \U10FFFF
3855N/A private static final int SURROGATE_PAIR = 3; // \uD800\uDC00 - \uDBFF\uDFFF
0N/A private InputMethodContext context;
0N/A private Locale locale;
0N/A private StringBuffer buffer;
0N/A private int insertionPoint;
0N/A private int format = UNSET;
0N/A
0N/A public CodePointInputMethod() throws IOException {
0N/A }
0N/A
0N/A /**
0N/A * This is the input method's main routine. The composed text is stored
0N/A * in buffer.
0N/A */
0N/A public void dispatchEvent(AWTEvent event) {
0N/A // This input method handles KeyEvent only.
0N/A if (!(event instanceof KeyEvent)) {
0N/A return;
0N/A }
0N/A
0N/A KeyEvent e = (KeyEvent) event;
0N/A int eventID = event.getID();
0N/A boolean notInCompositionMode = buffer.length() == 0;
0N/A
0N/A if (eventID == KeyEvent.KEY_PRESSED) {
0N/A // If we are not in composition mode, pass through
3855N/A if (notInCompositionMode) {
0N/A return;
0N/A }
0N/A
0N/A switch (e.getKeyCode()) {
0N/A case KeyEvent.VK_LEFT:
0N/A moveCaretLeft();
0N/A break;
0N/A case KeyEvent.VK_RIGHT:
0N/A moveCaretRight();
0N/A break;
0N/A }
0N/A } else if (eventID == KeyEvent.KEY_TYPED) {
0N/A char c = e.getKeyChar();
0N/A
0N/A // If we are not in composition mode, wait a back slash
3855N/A if (notInCompositionMode) {
0N/A // If the type character is not a back slash, pass through
0N/A if (c != '\\') {
0N/A return;
0N/A }
0N/A
0N/A startComposition(); // Enter to composition mode
0N/A } else {
0N/A switch (c) {
3855N/A case ' ': // Exit from composition mode
3855N/A finishComposition();
3855N/A break;
3855N/A case '\u007f': // Delete
3855N/A deleteCharacter();
3855N/A break;
3855N/A case '\b': // BackSpace
3855N/A deletePreviousCharacter();
3855N/A break;
3855N/A case '\u001b': // Escape
3855N/A cancelComposition();
3855N/A break;
3855N/A case '\n': // Return
3855N/A case '\t': // Tab
3855N/A sendCommittedText();
3855N/A break;
3855N/A default:
3855N/A composeUnicodeEscape(c);
3855N/A break;
0N/A }
0N/A }
0N/A } else { // KeyEvent.KEY_RELEASED
0N/A // If we are not in composition mode, pass through
3855N/A if (notInCompositionMode) {
0N/A return;
0N/A }
0N/A }
0N/A
0N/A e.consume();
0N/A }
0N/A
0N/A private void composeUnicodeEscape(char c) {
0N/A switch (buffer.length()) {
3855N/A case 1: // \\
0N/A waitEscapeCharacter(c);
0N/A break;
0N/A case 2: // \\u or \\U
0N/A case 3: // \\ux or \\Ux
0N/A case 4: // \\uxx or \\Uxx
0N/A waitDigit(c);
0N/A break;
0N/A case 5: // \\uxxx or \\Uxxx
0N/A if (format == SPECIAL_ESCAPE) {
0N/A waitDigit(c);
0N/A } else {
0N/A waitDigit2(c);
0N/A }
0N/A break;
0N/A case 6: // \\uxxxx or \\Uxxxx
0N/A if (format == SPECIAL_ESCAPE) {
0N/A waitDigit(c);
0N/A } else if (format == SURROGATE_PAIR) {
0N/A waitBackSlashOrLowSurrogate(c);
0N/A } else {
0N/A beep();
0N/A }
0N/A break;
0N/A case 7: // \\Uxxxxx
0N/A // Only SPECIAL_ESCAPE format uses this state.
0N/A // Since the second "\\u" of SURROGATE_PAIR format is inserted
0N/A // automatically, users don't have to type these keys.
0N/A waitDigit(c);
0N/A break;
0N/A case 8: // \\uxxxx\\u
0N/A case 9: // \\uxxxx\\ux
0N/A case 10: // \\uxxxx\\uxx
0N/A case 11: // \\uxxxx\\uxxx
0N/A if (format == SURROGATE_PAIR) {
0N/A waitDigit(c);
0N/A } else {
0N/A beep();
0N/A }
0N/A break;
0N/A default:
0N/A beep();
0N/A break;
0N/A }
0N/A }
0N/A
0N/A private void waitEscapeCharacter(char c) {
0N/A if (c == 'u' || c == 'U') {
0N/A buffer.append(c);
0N/A insertionPoint++;
0N/A sendComposedText();
0N/A format = (c == 'u') ? ESCAPE : SPECIAL_ESCAPE;
0N/A } else {
0N/A if (c != '\\') {
0N/A buffer.append(c);
0N/A insertionPoint++;
0N/A }
0N/A sendCommittedText();
0N/A }
0N/A }
0N/A
0N/A private void waitDigit(char c) {
0N/A if (Character.digit(c, 16) != -1) {
0N/A buffer.insert(insertionPoint++, c);
0N/A sendComposedText();
0N/A } else {
0N/A beep();
0N/A }
0N/A }
0N/A
0N/A private void waitDigit2(char c) {
0N/A if (Character.digit(c, 16) != -1) {
0N/A buffer.insert(insertionPoint++, c);
3855N/A char codePoint = (char) getCodePoint(buffer, 2, 5);
0N/A if (Character.isHighSurrogate(codePoint)) {
0N/A format = SURROGATE_PAIR;
0N/A buffer.append("\\u");
0N/A insertionPoint = 8;
0N/A } else {
0N/A format = ESCAPE;
0N/A }
0N/A sendComposedText();
0N/A } else {
0N/A beep();
0N/A }
0N/A }
0N/A
0N/A private void waitBackSlashOrLowSurrogate(char c) {
0N/A if (insertionPoint == 6) {
0N/A if (c == '\\') {
0N/A buffer.append(c);
0N/A buffer.append('u');
0N/A insertionPoint = 8;
0N/A sendComposedText();
0N/A } else if (Character.digit(c, 16) != -1) {
0N/A buffer.append("\\u");
0N/A buffer.append(c);
0N/A insertionPoint = 9;
0N/A sendComposedText();
0N/A } else {
0N/A beep();
0N/A }
0N/A } else {
0N/A beep();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Send the composed text to the client.
0N/A */
0N/A private void sendComposedText() {
0N/A AttributedString as = new AttributedString(buffer.toString());
0N/A as.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
3855N/A InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT);
0N/A context.dispatchInputMethodEvent(
3855N/A InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
3855N/A as.getIterator(), 0,
3855N/A TextHitInfo.leading(insertionPoint), null);
0N/A }
0N/A
0N/A /**
0N/A * Send the committed text to the client.
0N/A */
0N/A private void sendCommittedText() {
0N/A AttributedString as = new AttributedString(buffer.toString());
0N/A context.dispatchInputMethodEvent(
3855N/A InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
3855N/A as.getIterator(), buffer.length(),
3855N/A TextHitInfo.leading(insertionPoint), null);
0N/A
0N/A buffer.setLength(0);
0N/A insertionPoint = 0;
0N/A format = UNSET;
0N/A }
0N/A
0N/A /**
0N/A * Move the insertion point one position to the left in the composed text.
0N/A * Do not let the caret move to the left of the "\\u" or "\\U".
0N/A */
0N/A private void moveCaretLeft() {
0N/A int len = buffer.length();
0N/A if (--insertionPoint < 2) {
0N/A insertionPoint++;
0N/A beep();
0N/A } else if (format == SURROGATE_PAIR && insertionPoint == 7) {
0N/A insertionPoint = 8;
0N/A beep();
0N/A }
0N/A
0N/A context.dispatchInputMethodEvent(
3855N/A InputMethodEvent.CARET_POSITION_CHANGED,
3855N/A null, 0,
3855N/A TextHitInfo.leading(insertionPoint), null);
0N/A }
0N/A
0N/A /**
0N/A * Move the insertion point one position to the right in the composed text.
0N/A */
0N/A private void moveCaretRight() {
0N/A int len = buffer.length();
0N/A if (++insertionPoint > len) {
0N/A insertionPoint = len;
0N/A beep();
0N/A }
0N/A
0N/A context.dispatchInputMethodEvent(
3855N/A InputMethodEvent.CARET_POSITION_CHANGED,
3855N/A null, 0,
3855N/A TextHitInfo.leading(insertionPoint), null);
0N/A }
0N/A
0N/A /**
0N/A * Delete the character preceding the insertion point in the composed text.
0N/A * If the insertion point is not at the end of the composed text and the
0N/A * preceding text is "\\u" or "\\U", ring the bell.
0N/A */
0N/A private void deletePreviousCharacter() {
0N/A if (insertionPoint == 2) {
0N/A if (buffer.length() == 2) {
0N/A cancelComposition();
0N/A } else {
0N/A // Do not allow deletion of the leading "\\u" or "\\U" if there
0N/A // are other digits in the composed text.
0N/A beep();
0N/A }
0N/A } else if (insertionPoint == 8) {
0N/A if (buffer.length() == 8) {
0N/A if (format == SURROGATE_PAIR) {
0N/A buffer.deleteCharAt(--insertionPoint);
0N/A }
0N/A buffer.deleteCharAt(--insertionPoint);
0N/A sendComposedText();
0N/A } else {
0N/A // Do not allow deletion of the second "\\u" if there are other
0N/A // digits in the composed text.
0N/A beep();
0N/A }
0N/A } else {
0N/A buffer.deleteCharAt(--insertionPoint);
0N/A if (buffer.length() == 0) {
0N/A sendCommittedText();
0N/A } else {
0N/A sendComposedText();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Delete the character following the insertion point in the composed text.
0N/A * If the insertion point is at the end of the composed text, ring the bell.
0N/A */
0N/A private void deleteCharacter() {
0N/A if (insertionPoint < buffer.length()) {
0N/A buffer.deleteCharAt(insertionPoint);
0N/A sendComposedText();
0N/A } else {
0N/A beep();
0N/A }
0N/A }
0N/A
0N/A private void startComposition() {
0N/A buffer.append('\\');
0N/A insertionPoint = 1;
0N/A sendComposedText();
0N/A }
0N/A
0N/A private void cancelComposition() {
0N/A buffer.setLength(0);
0N/A insertionPoint = 0;
0N/A sendCommittedText();
0N/A }
0N/A
0N/A private void finishComposition() {
0N/A int len = buffer.length();
0N/A if (len == 6 && format != SPECIAL_ESCAPE) {
3855N/A char codePoint = (char) getCodePoint(buffer, 2, 5);
0N/A if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) {
0N/A buffer.setLength(0);
0N/A buffer.append(codePoint);
0N/A sendCommittedText();
0N/A return;
0N/A }
0N/A } else if (len == 8 && format == SPECIAL_ESCAPE) {
0N/A int codePoint = getCodePoint(buffer, 2, 7);
0N/A if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) {
0N/A buffer.setLength(0);
0N/A buffer.appendCodePoint(codePoint);
0N/A sendCommittedText();
0N/A return;
0N/A }
0N/A } else if (len == 12 && format == SURROGATE_PAIR) {
0N/A char[] codePoint = {
3855N/A (char) getCodePoint(buffer, 2, 5),
3855N/A (char) getCodePoint(buffer, 8, 11)
0N/A };
3855N/A if (Character.isHighSurrogate(codePoint[0]) && Character.
3855N/A isLowSurrogate(codePoint[1])) {
0N/A buffer.setLength(0);
0N/A buffer.append(codePoint);
0N/A sendCommittedText();
0N/A return;
0N/A }
0N/A }
0N/A
0N/A beep();
0N/A }
0N/A
0N/A private int getCodePoint(StringBuffer sb, int from, int to) {
0N/A int value = 0;
0N/A for (int i = from; i <= to; i++) {
3855N/A value = (value << 4) + Character.digit(sb.charAt(i), 16);
0N/A }
0N/A return value;
0N/A }
0N/A
0N/A private static void beep() {
0N/A Toolkit.getDefaultToolkit().beep();
0N/A }
0N/A
0N/A public void activate() {
0N/A if (buffer == null) {
0N/A buffer = new StringBuffer(12);
0N/A insertionPoint = 0;
0N/A }
0N/A }
0N/A
0N/A public void deactivate(boolean isTemporary) {
0N/A if (!isTemporary) {
0N/A buffer = null;
0N/A }
0N/A }
0N/A
0N/A public void dispose() {
0N/A }
0N/A
0N/A public Object getControlObject() {
0N/A return null;
0N/A }
0N/A
0N/A public void endComposition() {
0N/A sendCommittedText();
0N/A }
0N/A
0N/A public Locale getLocale() {
0N/A return locale;
0N/A }
0N/A
0N/A public void hideWindows() {
0N/A }
0N/A
0N/A public boolean isCompositionEnabled() {
0N/A // always enabled
0N/A return true;
0N/A }
0N/A
0N/A public void notifyClientWindowChange(Rectangle location) {
0N/A }
0N/A
0N/A public void reconvert() {
0N/A // not supported yet
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A
0N/A public void removeNotify() {
0N/A }
0N/A
0N/A public void setCharacterSubsets(Character.Subset[] subsets) {
0N/A }
0N/A
0N/A public void setCompositionEnabled(boolean enable) {
0N/A // not supported yet
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A
0N/A public void setInputMethodContext(InputMethodContext context) {
0N/A this.context = context;
0N/A }
0N/A
0N/A /*
0N/A * The Code Point Input Method supports all locales.
0N/A */
0N/A public boolean setLocale(Locale locale) {
0N/A this.locale = locale;
0N/A return true;
0N/A }
0N/A}