KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > inputmethods > internal > codepointim > CodePointInputMethod


1 /*
2  * @(#)CodePointInputMethod.java 1.6 05/11/17
3  *
4  * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * -Redistribution of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  *
12  * -Redistribution in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17  * be used to endorse or promote products derived from this software without
18  * specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
22  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
24  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
25  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
26  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
27  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
28  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
29  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  *
32  * You acknowledge that this software is not designed, licensed or intended
33  * for use in the design, construction, operation or maintenance of any
34  * nuclear facility.
35  */

36
37 /*
38  * @(#)CodePointInputMethod.java 1.6 05/11/17
39  */

40
41 package com.sun.inputmethods.internal.codepointim;
42 import java.text.AttributedCharacterIterator JavaDoc;
43 import java.util.Map JavaDoc;
44
45 import java.awt.AWTEvent JavaDoc;
46 import java.awt.Toolkit JavaDoc;
47 import java.awt.Rectangle JavaDoc;
48 import java.awt.event.InputMethodEvent JavaDoc;
49 import java.awt.event.KeyEvent JavaDoc;
50 import java.awt.font.TextAttribute JavaDoc;
51 import java.awt.font.TextHitInfo JavaDoc;
52 import java.awt.im.InputMethodHighlight JavaDoc;
53 import java.awt.im.spi.InputMethod JavaDoc;
54 import java.awt.im.spi.InputMethodContext JavaDoc;
55 import java.io.IOException JavaDoc;
56 import java.text.AttributedString JavaDoc;
57 import java.util.Locale JavaDoc;
58
59 /**
60  * The Code Point Input Method is a simple input method that allows Unicode
61  * characters to be entered using their code point or code unit values. See the
62  * accompanying file README.txt for more information.
63  *
64  * @author Brian Beck
65  */

66 public class CodePointInputMethod implements InputMethod JavaDoc {
67
68     private static final int UNSET = 0;
69     private static final int ESCAPE = 1; // \u0000 - \uFFFF
70
private static final int SPECIAL_ESCAPE = 2; // \U000000 - \U10FFFF
71
private static final int SURROGATE_PAIR = 3; // \uD800\uDC00 - \uDBFF\uDFFF
72

73     private InputMethodContext JavaDoc context;
74     private Locale JavaDoc locale;
75     private StringBuffer JavaDoc buffer;
76     private int insertionPoint;
77     private int format = UNSET;
78
79
80     public CodePointInputMethod() throws IOException JavaDoc {
81     }
82
83     /**
84      * This is the input method's main routine. The composed text is stored
85      * in buffer.
86      */

87     public void dispatchEvent(AWTEvent JavaDoc event) {
88         // This input method handles KeyEvent only.
89
if (!(event instanceof KeyEvent JavaDoc)) {
90             return;
91         }
92
93         KeyEvent JavaDoc e = (KeyEvent JavaDoc) event;
94         int eventID = event.getID();
95         boolean notInCompositionMode = buffer.length() == 0;
96
97         if (eventID == KeyEvent.KEY_PRESSED) {
98             // If we are not in composition mode, pass through
99
if (notInCompositionMode) {
100                 return;
101             }
102
103             switch (e.getKeyCode()) {
104                 case KeyEvent.VK_LEFT:
105                     moveCaretLeft();
106                     break;
107                 case KeyEvent.VK_RIGHT:
108                     moveCaretRight();
109                     break;
110             }
111         } else if (eventID == KeyEvent.KEY_TYPED) {
112             char c = e.getKeyChar();
113
114             // If we are not in composition mode, wait a back slash
115
if (notInCompositionMode) {
116                 // If the type character is not a back slash, pass through
117
if (c != '\\') {
118                     return;
119                 }
120
121                 startComposition(); // Enter to composition mode
122
} else {
123                 switch (c) {
124                 case ' ': // Exit from composition mode
125
finishComposition();
126                     break;
127                 case '\u007f': // Delete
128
deleteCharacter();
129                     break;
130                 case '\b': // BackSpace
131
deletePreviousCharacter();
132                     break;
133                 case '\u001b': // Escape
134
cancelComposition();
135                     break;
136                 case '\n': // Return
137
case '\t': // Tab
138
sendCommittedText();
139                     break;
140                 default:
141                     composeUnicodeEscape(c);
142                     break;
143                 }
144             }
145         } else { // KeyEvent.KEY_RELEASED
146
// If we are not in composition mode, pass through
147
if (notInCompositionMode) {
148                 return;
149             }
150         }
151
152         e.consume();
153     }
154
155     private void composeUnicodeEscape(char c) {
156         switch (buffer.length()) {
157             case 1: // \\
158
waitEscapeCharacter(c);
159                 break;
160             case 2: // \\u or \\U
161
case 3: // \\ux or \\Ux
162
case 4: // \\uxx or \\Uxx
163
waitDigit(c);
164                 break;
165             case 5: // \\uxxx or \\Uxxx
166
if (format == SPECIAL_ESCAPE) {
167                     waitDigit(c);
168                 } else {
169                     waitDigit2(c);
170                 }
171                 break;
172             case 6: // \\uxxxx or \\Uxxxx
173
if (format == SPECIAL_ESCAPE) {
174                     waitDigit(c);
175                 } else if (format == SURROGATE_PAIR) {
176                     waitBackSlashOrLowSurrogate(c);
177                 } else {
178                     beep();
179                 }
180                 break;
181             case 7: // \\Uxxxxx
182
// Only SPECIAL_ESCAPE format uses this state.
183
// Since the second "\\u" of SURROGATE_PAIR format is inserted
184
// automatically, users don't have to type these keys.
185
waitDigit(c);
186                 break;
187             case 8: // \\uxxxx\\u
188
case 9: // \\uxxxx\\ux
189
case 10: // \\uxxxx\\uxx
190
case 11: // \\uxxxx\\uxxx
191
if (format == SURROGATE_PAIR) {
192                     waitDigit(c);
193                 } else {
194                     beep();
195                 }
196                 break;
197             default:
198                 beep();
199                 break;
200         }
201     }
202
203     private void waitEscapeCharacter(char c) {
204         if (c == 'u' || c == 'U') {
205             buffer.append(c);
206             insertionPoint++;
207             sendComposedText();
208             format = (c == 'u') ? ESCAPE : SPECIAL_ESCAPE;
209         } else {
210             if (c != '\\') {
211                 buffer.append(c);
212                 insertionPoint++;
213             }
214             sendCommittedText();
215         }
216     }
217
218     private void waitDigit(char c) {
219         if (Character.digit(c, 16) != -1) {
220             buffer.insert(insertionPoint++, c);
221             sendComposedText();
222         } else {
223             beep();
224         }
225     }
226
227     private void waitDigit2(char c) {
228         if (Character.digit(c, 16) != -1) {
229             buffer.insert(insertionPoint++, c);
230             char codePoint = (char)getCodePoint(buffer, 2, 5);
231             if (Character.isHighSurrogate(codePoint)) {
232                 format = SURROGATE_PAIR;
233                 buffer.append("\\u");
234                 insertionPoint = 8;
235             } else {
236                 format = ESCAPE;
237             }
238             sendComposedText();
239         } else {
240             beep();
241         }
242     }
243
244     private void waitBackSlashOrLowSurrogate(char c) {
245         if (insertionPoint == 6) {
246             if (c == '\\') {
247                 buffer.append(c);
248                 buffer.append('u');
249                 insertionPoint = 8;
250                 sendComposedText();
251             } else if (Character.digit(c, 16) != -1) {
252                 buffer.append("\\u");
253                 buffer.append(c);
254                 insertionPoint = 9;
255                 sendComposedText();
256             } else {
257                 beep();
258             }
259         } else {
260             beep();
261         }
262     }
263
264     /**
265      * Send the composed text to the client.
266      */

267     private void sendComposedText() {
268         AttributedString JavaDoc as = new AttributedString JavaDoc(buffer.toString());
269         as.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
270                         InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT);
271         context.dispatchInputMethodEvent(
272                                   InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
273                                   as.getIterator(), 0,
274                                   TextHitInfo.leading(insertionPoint), null);
275     }
276
277     /**
278      * Send the committed text to the client.
279      */

280     private void sendCommittedText() {
281         AttributedString JavaDoc as = new AttributedString JavaDoc(buffer.toString());
282         context.dispatchInputMethodEvent(
283                                   InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
284                                   as.getIterator(), buffer.length(),
285                                   TextHitInfo.leading(insertionPoint), null);
286
287         buffer.setLength(0);
288         insertionPoint = 0;
289         format = UNSET;
290     }
291
292     /**
293      * Move the insertion point one position to the left in the composed text.
294      * Do not let the caret move to the left of the "\\u" or "\\U".
295      */

296     private void moveCaretLeft() {
297         int len = buffer.length();
298         if (--insertionPoint < 2) {
299             insertionPoint++;
300             beep();
301         } else if (format == SURROGATE_PAIR && insertionPoint == 7) {
302             insertionPoint = 8;
303             beep();
304         }
305
306         context.dispatchInputMethodEvent(
307                                   InputMethodEvent.CARET_POSITION_CHANGED,
308                                   null, 0,
309                                   TextHitInfo.leading(insertionPoint), null);
310     }
311
312     /**
313      * Move the insertion point one position to the right in the composed text.
314      */

315     private void moveCaretRight() {
316         int len = buffer.length();
317         if (++insertionPoint > len) {
318             insertionPoint = len;
319             beep();
320         }
321
322         context.dispatchInputMethodEvent(
323                                   InputMethodEvent.CARET_POSITION_CHANGED,
324                                   null, 0,
325                                   TextHitInfo.leading(insertionPoint), null);
326     }
327
328     /**
329      * Delete the character preceding the insertion point in the composed text.
330      * If the insertion point is not at the end of the composed text and the
331      * preceding text is "\\u" or "\\U", ring the bell.
332      */

333     private void deletePreviousCharacter() {
334         if (insertionPoint == 2) {
335             if (buffer.length() == 2) {
336                 cancelComposition();
337             } else {
338                 // Do not allow deletion of the leading "\\u" or "\\U" if there
339
// are other digits in the composed text.
340
beep();
341             }
342         } else if (insertionPoint == 8) {
343             if (buffer.length() == 8) {
344                 if (format == SURROGATE_PAIR) {
345                     buffer.deleteCharAt(--insertionPoint);
346                 }
347                 buffer.deleteCharAt(--insertionPoint);
348                 sendComposedText();
349             } else {
350                 // Do not allow deletion of the second "\\u" if there are other
351
// digits in the composed text.
352
beep();
353             }
354         } else {
355             buffer.deleteCharAt(--insertionPoint);
356             if (buffer.length() == 0) {
357                 sendCommittedText();
358             } else {
359                 sendComposedText();
360             }
361         }
362     }
363
364     /**
365      * Delete the character following the insertion point in the composed text.
366      * If the insertion point is at the end of the composed text, ring the bell.
367      */

368     private void deleteCharacter() {
369         if (insertionPoint < buffer.length()) {
370             buffer.deleteCharAt(insertionPoint);
371             sendComposedText();
372         } else {
373             beep();
374         }
375     }
376
377     private void startComposition() {
378         buffer.append('\\');
379         insertionPoint = 1;
380         sendComposedText();
381     }
382
383     private void cancelComposition() {
384         buffer.setLength(0);
385         insertionPoint = 0;
386         sendCommittedText();
387     }
388
389     private void finishComposition() {
390         int len = buffer.length();
391         if (len == 6 && format != SPECIAL_ESCAPE) {
392             char codePoint = (char)getCodePoint(buffer, 2, 5);
393             if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) {
394                 buffer.setLength(0);
395                 buffer.append(codePoint);
396             sendCommittedText();
397                 return;
398             }
399         } else if (len == 8 && format == SPECIAL_ESCAPE) {
400             int codePoint = getCodePoint(buffer, 2, 7);
401             if (Character.isValidCodePoint(codePoint) && codePoint != 0xFFFF) {
402                 buffer.setLength(0);
403                 buffer.appendCodePoint(codePoint);
404             sendCommittedText();
405                 return;
406             }
407         } else if (len == 12 && format == SURROGATE_PAIR) {
408         char[] codePoint = {
409                 (char)getCodePoint(buffer, 2, 5),
410             (char)getCodePoint(buffer, 8, 11)
411             };
412             if (Character.isHighSurrogate(codePoint[0]) &&
413                 Character.isLowSurrogate(codePoint[1])) {
414             buffer.setLength(0);
415             buffer.append(codePoint);
416             sendCommittedText();
417                 return;
418             }
419         }
420
421         beep();
422     }
423
424     private int getCodePoint(StringBuffer JavaDoc sb, int from, int to) {
425         int value = 0;
426         for (int i = from; i <= to; i++) {
427             value = (value<<4) + Character.digit(sb.charAt(i), 16);
428         }
429         return value;
430     }
431
432     private static void beep() {
433         Toolkit.getDefaultToolkit().beep();
434     }
435
436
437     public void activate() {
438         if (buffer == null) {
439             buffer = new StringBuffer JavaDoc(12);
440             insertionPoint = 0;
441         }
442     }
443
444     public void deactivate(boolean isTemporary) {
445         if (!isTemporary) {
446             buffer = null;
447         }
448     }
449
450     public void dispose() {
451     }
452
453     public Object JavaDoc getControlObject() {
454         return null;
455     }
456
457     public void endComposition() {
458         sendCommittedText();
459     }
460
461     public Locale JavaDoc getLocale() {
462         return locale;
463     }
464
465     public void hideWindows() {
466     }
467
468     public boolean isCompositionEnabled() {
469         // always enabled
470
return true;
471     }
472
473     public void notifyClientWindowChange(Rectangle JavaDoc location) {
474     }
475
476     public void reconvert() {
477         // not supported yet
478
throw new UnsupportedOperationException JavaDoc();
479     }
480
481     public void removeNotify() {
482     }
483
484     public void setCharacterSubsets(Character.Subset JavaDoc[] subsets) {
485     }
486
487     public void setCompositionEnabled(boolean enable) {
488         // not supported yet
489
throw new UnsupportedOperationException JavaDoc();
490     }
491
492     public void setInputMethodContext(InputMethodContext JavaDoc context) {
493         this.context = context;
494     }
495
496     /*
497      * The Code Point Input Method supports all locales.
498      */

499     public boolean setLocale(Locale JavaDoc locale) {
500         this.locale = locale;
501         return true;
502     }
503 }
504
Popular Tags