1 /*
2  * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.awt;
27 
28 import java.awt.AWTEvent;
29 import java.awt.AWTException;
30 import java.awt.Component;
31 import java.awt.Container;
32 import java.awt.EventQueue;
33 import java.awt.Window;
34 import java.awt.event.InputMethodEvent;
35 import java.awt.font.TextAttribute;
36 import java.awt.font.TextHitInfo;
37 import java.awt.im.InputMethodHighlight;
38 import java.awt.im.spi.InputMethodContext;
39 import java.awt.peer.ComponentPeer;
40 import java.io.BufferedReader;
41 import java.io.File;
42 import java.io.FileReader;
43 import java.io.IOException;
44 import java.lang.Character.Subset;
45 import java.lang.ref.WeakReference;
46 import java.text.AttributedCharacterIterator;
47 import java.text.AttributedString;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.Locale;
51 import java.util.Map;
52 import java.util.StringTokenizer;
53 import java.util.regex.Pattern;
54 
55 import sun.awt.im.InputMethodAdapter;
56 import sun.util.logging.PlatformLogger;
57 
58 /**
59  * Input Method Adapter for XIM
60  *
61  * @author JavaSoft International
62  */
63 public abstract class X11InputMethodBase extends InputMethodAdapter {
64     protected static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod");
65     /*
66      * The following XIM* values must be the same as those defined in
67      * Xlib.h
68      */
69     private static final int XIMReverse = (1<<0);
70     private static final int XIMUnderline = (1<<1);
71     private static final int XIMHighlight = (1<<2);
72     private static final int XIMPrimary = (1<<5);
73     private static final int XIMSecondary = (1<<6);
74     private static final int XIMTertiary = (1<<7);
75 
76     /*
77      * visible position values
78      */
79     protected static final int XIMVisibleToForward = (1<<8);
80     protected static final int XIMVisibleToBackward = (1<<9);
81     protected static final int XIMVisibleCenter = (1<<10);
82     protected static final int XIMVisibleMask =
83         (XIMVisibleToForward | XIMVisibleToBackward | XIMVisibleCenter);
84 
85     private Locale locale;
86     private static boolean isXIMOpened = false;
87     protected Container clientComponentWindow = null;
88     protected Component awtFocussedComponent = null;
89     protected Component lastXICFocussedComponent = null;
90     protected boolean   isLastXICActive = false;
91     protected boolean   isLastTemporary = false;
92     protected boolean   isActive = false;
93     private static Map<TextAttribute, ?>[] highlightStyles;
94     protected boolean disposed = false;
95 
96     //reset the XIC if necessary
97     protected boolean   needResetXIC = false;
98     private WeakReference<Component> needResetXICClient = new WeakReference<>(null);
99 
100     // The use of compositionEnableSupported is to reduce unnecessary
101     // native calls if set/isCompositionEnabled
102     // throws UnsupportedOperationException.
103     // It is set to false if that exception is thrown first time
104     // either of the two methods are called.
105     protected boolean compositionEnableSupported = true;
106     // The savedCompositionState indicates the composition mode when
107     // endComposition or setCompositionEnabled is called. It doesn't always
108     // reflect the actual composition state because it doesn't get updated
109     // when the user changes the composition state through direct interaction
110     // with the input method. It is used to save the composition mode when
111     // focus is traversed across different client components sharing the
112     // same java input context. Also if set/isCompositionEnabled are not
113     // supported, it remains false.
114     protected boolean savedCompositionState = false;
115 
116     // variables to keep track of preedit context.
117     // these variables need to be accessed within AWT_LOCK/UNLOCK
118     protected String committedText = null;
119     protected StringBuffer composedText = null;
120     protected IntBuffer rawFeedbacks;
121 
122     // private data (X11InputMethodData structure defined in
123     // awt_InputMethod.c) for native methods
124     // this structure needs to be accessed within AWT_LOCK/UNLOCK
125     protected transient long pData = 0; // accessed by native
126 
127     // Initialize highlight mapping table
128     static {
129         @SuppressWarnings({"unchecked", "rawtypes"})
130         Map<TextAttribute, ?> styles[] = new Map[4];
131         HashMap<TextAttribute, Object> map;
132 
133         // UNSELECTED_RAW_TEXT_HIGHLIGHT
134         map = new HashMap<>(1);
map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)135         map.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
136         styles[0] = Collections.unmodifiableMap(map);
137 
138         // SELECTED_RAW_TEXT_HIGHLIGHT
139         map = new HashMap<>(1);
map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON)140         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
141         styles[1] = Collections.unmodifiableMap(map);
142 
143         // UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
144         map = new HashMap<>(1);
map.put(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL)145         map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
146                 TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
147         styles[2] = Collections.unmodifiableMap(map);
148 
149         // SELECTED_CONVERTED_TEXT_HIGHLIGHT
150         map = new HashMap<>(1);
map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON)151         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
152         styles[3] = Collections.unmodifiableMap(map);
153 
154         highlightStyles = styles;
155     }
156 
157     static {
initIDs()158         initIDs();
159     }
160 
161     /**
162      * Constructs an X11InputMethod instance. It initializes the XIM
163      * environment if it's not done yet.
164      *
165      * @exception AWTException if XOpenIM() failed.
166      */
X11InputMethodBase()167     public X11InputMethodBase() throws AWTException {
168         // supports only the locale in which the VM is started
169         locale = X11InputMethodDescriptor.getSupportedLocale();
170         if (initXIM() == false) {
171             throw new AWTException("Cannot open X Input Method");
172         }
173     }
174 
175     @SuppressWarnings("deprecation")
finalize()176     protected void finalize() throws Throwable {
177         dispose();
178         super.finalize();
179     }
180 
181     /**
182      * Invokes openIM() that invokes XOpenIM() if it's not opened yet.
183      * @return  true if openXIM() is successful or it's already been opened.
184      */
initXIM()185     private synchronized boolean initXIM() {
186         if (isXIMOpened == false)
187             isXIMOpened = openXIM();
188         return isXIMOpened;
189     }
190 
openXIM()191     protected abstract boolean openXIM();
192 
isDisposed()193     protected boolean isDisposed() {
194         return disposed;
195     }
196 
setXICFocus(ComponentPeer peer, boolean value, boolean active)197     protected abstract void setXICFocus(ComponentPeer peer,
198                                     boolean value, boolean active);
199 
200     /**
201      * Does nothing - this adapter doesn't use the input method context.
202      *
203      * @see java.awt.im.spi.InputMethod#setInputMethodContext
204      */
setInputMethodContext(InputMethodContext context)205     public void setInputMethodContext(InputMethodContext context) {
206     }
207 
208     /**
209      * Set locale to input. If input method doesn't support specified locale,
210      * false will be returned and its behavior is not changed.
211      *
212      * @param lang locale to input
213      * @return the true is returned when specified locale is supported.
214      */
setLocale(Locale lang)215     public boolean setLocale(Locale lang) {
216         if (lang.equals(locale)) {
217             return true;
218         }
219         // special compatibility rule for Japanese and Korean
220         if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
221                 locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
222             return true;
223         }
224         return false;
225     }
226 
227     /**
228      * Returns current input locale.
229      */
getLocale()230     public Locale getLocale() {
231         return locale;
232     }
233 
234     /**
235      * Does nothing - XIM doesn't let you specify which characters you expect.
236      *
237      * @see java.awt.im.spi.InputMethod#setCharacterSubsets
238      */
setCharacterSubsets(Subset[] subsets)239     public void setCharacterSubsets(Subset[] subsets) {
240     }
241 
242     /**
243      * Dispatch event to input method. InputContext dispatch event with this
244      * method. Input method set consume flag if event is consumed in
245      * input method.
246      *
247      * @param e event
248      */
dispatchEvent(AWTEvent e)249     public void dispatchEvent(AWTEvent e) {
250     }
251 
resetXICifneeded()252     protected final void resetXICifneeded(){
253         /* needResetXIC is used to indicate whether to call
254            resetXIC on the active client. resetXIC will always be
255            called on the passive client when endComposition is called.
256         */
257         if (needResetXIC && haveActiveClient() &&
258             getClientComponent() != needResetXICClient.get()){
259             resetXIC();
260 
261             // needs to reset the last xic focussed component.
262             lastXICFocussedComponent = null;
263             isLastXICActive = false;
264 
265             needResetXICClient.clear();
266             needResetXIC = false;
267         }
268     }
269 
270     /**
271      * Reset the composition state to the current composition state.
272      */
resetCompositionState()273     protected abstract void resetCompositionState();
274 
275     /**
276      * Query and then return the current composition state.
277      * @return the composition state if isCompositionEnabled call
278      * is successful. Otherwise, it returns false.
279      */
getCompositionState()280     protected boolean getCompositionState() {
281         boolean compositionState = false;
282         if (compositionEnableSupported) {
283             try {
284                 compositionState = isCompositionEnabled();
285             } catch (UnsupportedOperationException e) {
286                 compositionEnableSupported = false;
287             }
288         }
289         return compositionState;
290     }
291 
292     /**
293      * Activate input method.
294      */
activate()295     public abstract void activate();
296 
createXIC()297     protected abstract boolean createXIC();
298 
299     /**
300      * Deactivate input method.
301      */
deactivate(boolean isTemporary)302     public abstract void deactivate(boolean isTemporary);
303 
304     /**
305      * Explicitly disable the native IME. Native IME is not disabled when
306      * deactivate is called.
307      */
disableInputMethod()308     public void disableInputMethod() {
309         if (lastXICFocussedComponent != null) {
310             setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive);
311             lastXICFocussedComponent = null;
312             isLastXICActive = false;
313 
314             resetXIC();
315             needResetXICClient.clear();
316             needResetXIC = false;
317         }
318     }
319 
320     // implements java.awt.im.spi.InputMethod.hideWindows
hideWindows()321     public abstract void hideWindows();
322 
323     /**
324      * @see java.awt.Toolkit#mapInputMethodHighlight
325      */
mapInputMethodHighlight(InputMethodHighlight highlight)326     public static Map<TextAttribute, ?> mapInputMethodHighlight(InputMethodHighlight highlight) {
327         int index;
328         int state = highlight.getState();
329         if (state == InputMethodHighlight.RAW_TEXT) {
330             index = 0;
331         } else if (state == InputMethodHighlight.CONVERTED_TEXT) {
332             index = 2;
333         } else {
334             return null;
335         }
336         if (highlight.isSelected()) {
337             index += 1;
338         }
339         return highlightStyles[index];
340     }
341 
342     /**
343      * @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent
344      */
setAWTFocussedComponent(Component component)345     protected void setAWTFocussedComponent(Component component) {
346         if (component == null) {
347             return;
348         }
349         if (isActive) {
350             // deactivate/activate are being suppressed during a focus change -
351             // this may happen when an input method window is made visible
352             boolean ac = haveActiveClient();
353             setXICFocus(getPeer(awtFocussedComponent), false, ac);
354             setXICFocus(getPeer(component), true, ac);
355         }
356         awtFocussedComponent = component;
357     }
358 
359     /**
360      * @see sun.awt.im.InputMethodAdapter#stopListening
361      */
stopListening()362     protected void stopListening() {
363         // It is desirable to disable XIM by calling XSetICValues with
364         // XNPreeditState == XIMPreeditDisable.  But Solaris 2.6 and
365         // Solaris 7 do not implement this correctly without a patch,
366         // so just call resetXIC here.  Prior endComposition call commits
367         // the existing composed text.
368         endComposition();
369         // disable the native input method so that the other input
370         // method could get the input focus.
371         disableInputMethod();
372         if (needResetXIC) {
373             resetXIC();
374             needResetXICClient.clear();
375             needResetXIC = false;
376         }
377     }
378 
379     /**
380      * Returns the Window instance in which the client component is
381      * contained. If not found, null is returned. (IS THIS POSSIBLE?)
382      */
383     // NOTE: This method may be called by privileged threads.
384     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
getClientComponentWindow()385     protected Window getClientComponentWindow() {
386         Component client = getClientComponent();
387         Container container;
388 
389         if (client instanceof Container) {
390             container = (Container) client;
391         } else {
392             container = getParent(client);
393         }
394 
395         while (container != null && !(container instanceof java.awt.Window)) {
396             container = getParent(container);
397         }
398         return (Window) container;
399     }
400 
getParent(Component client)401     protected abstract Container getParent(Component client);
402 
403     /**
404      * Returns peer of the given client component. If the given client component
405      * doesn't have peer, peer of the native container of the client is returned.
406      */
getPeer(Component client)407     protected abstract ComponentPeer getPeer(Component client);
408 
409     /**
410      * Used to protect preedit data
411      */
awtLock()412     protected abstract void awtLock();
awtUnlock()413     protected abstract void awtUnlock();
414 
415     /**
416      * Creates an input method event from the arguments given
417      * and posts it on the AWT event queue. For arguments,
418      * see InputMethodEvent. Called by input method.
419      *
420      * @see java.awt.event.InputMethodEvent#InputMethodEvent
421      */
postInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition, long when)422     protected void postInputMethodEvent(int id,
423                                       AttributedCharacterIterator text,
424                                       int committedCharacterCount,
425                                       TextHitInfo caret,
426                                       TextHitInfo visiblePosition,
427                                       long when) {
428         Component source = getClientComponent();
429         if (source != null) {
430             InputMethodEvent event = new InputMethodEvent(source,
431                 id, when, text, committedCharacterCount, caret, visiblePosition);
432             SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event);
433         }
434     }
435 
postInputMethodEvent(int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition)436     private void postInputMethodEvent(int id,
437                                       AttributedCharacterIterator text,
438                                       int committedCharacterCount,
439                                       TextHitInfo caret,
440                                       TextHitInfo visiblePosition) {
441         postInputMethodEvent(id, text, committedCharacterCount,
442                              caret, visiblePosition, EventQueue.getMostRecentEventTime());
443     }
444 
445     /**
446      * Dispatches committed text from XIM to the awt event queue. This
447      * method is invoked from the event handler in canvas.c in the
448      * AWT Toolkit thread context and thus inside the AWT Lock.
449      * @param   str     committed text
450      * @param   when    when
451      */
452     // NOTE: This method may be called by privileged threads.
453     //       This functionality is implemented in a package-private method
454     //       to insure that it cannot be overridden by client subclasses.
455     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
dispatchCommittedText(String str, long when)456     void dispatchCommittedText(String str, long when) {
457         if (str == null)
458             return;
459 
460         if (composedText == null) {
461             AttributedString attrstr = new AttributedString(str);
462             postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
463                                  attrstr.getIterator(),
464                                  str.length(),
465                                  null,
466                                  null,
467                                  when);
468         } else {
469             // if there is composed text, wait until the preedit
470             // callback is invoked.
471             committedText = str;
472         }
473     }
474 
dispatchCommittedText(String str)475     private void dispatchCommittedText(String str) {
476         dispatchCommittedText(str, EventQueue.getMostRecentEventTime());
477     }
478 
479     /**
480      * Updates composed text with XIM preedit information and
481      * posts composed text to the awt event queue. The args of
482      * this method correspond to the XIM preedit callback
483      * information. The XIM highlight attributes are translated via
484      * fixed mapping (i.e., independent from any underlying input
485      * method engine). This method is invoked in the AWT Toolkit
486      * (X event loop) thread context and thus inside the AWT Lock.
487      */
488     // NOTE: This method may be called by privileged threads.
489     //       This functionality is implemented in a package-private method
490     //       to insure that it cannot be overridden by client subclasses.
491     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
dispatchComposedText(String chgText, int chgStyles[], int chgOffset, int chgLength, int caretPosition, long when)492     abstract void dispatchComposedText(String chgText,
493                                            int chgStyles[],
494                                            int chgOffset,
495                                            int chgLength,
496                                            int caretPosition,
497                                            long when);
498 
499     /**
500      * Flushes composed and committed text held in this context.
501      * This method is invoked in the AWT Toolkit (X event loop) thread context
502      * and thus inside the AWT Lock.
503      */
504     // NOTE: This method may be called by privileged threads.
505     //       This functionality is implemented in a package-private method
506     //       to insure that it cannot be overridden by client subclasses.
507     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
flushText()508     void flushText() {
509         String flush = (committedText != null ? committedText : "");
510         if (composedText != null) {
511             flush += composedText.toString();
512         }
513 
514         if (!flush.equals("")) {
515             AttributedString attrstr = new AttributedString(flush);
516             postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
517                                  attrstr.getIterator(),
518                                  flush.length(),
519                                  null,
520                                  null,
521                                  EventQueue.getMostRecentEventTime());
522             composedText = null;
523             committedText = null;
524         }
525     }
526 
527     /*
528      * Subclasses should override disposeImpl() instead of dispose(). Client
529      * code should always invoke dispose(), never disposeImpl().
530      */
disposeImpl()531     protected abstract void disposeImpl();
532 
533     /**
534      * Frees all X Window resources associated with this object.
535      *
536      * @see java.awt.im.spi.InputMethod#dispose
537      */
dispose()538     public final void dispose() {
539         boolean call_disposeImpl = false;
540 
541         if (!disposed) {
542             synchronized (this) {
543                 if (!disposed) {
544                     disposed = call_disposeImpl = true;
545                 }
546             }
547         }
548 
549         if (call_disposeImpl) {
550             disposeImpl();
551         }
552     }
553 
554     /**
555      * Returns null.
556      *
557      * @see java.awt.im.spi.InputMethod#getControlObject
558      */
getControlObject()559     public Object getControlObject() {
560         return null;
561     }
562 
563     /**
564      * @see java.awt.im.spi.InputMethod#removeNotify
565      */
removeNotify()566     public synchronized void removeNotify() {
567         dispose();
568     }
569 
570     /**
571      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
572      */
setCompositionEnabled(boolean enable)573     public abstract void setCompositionEnabled(boolean enable);
574 
575     /**
576      * @see java.awt.im.spi.InputMethod#isCompositionEnabled
577      */
isCompositionEnabled()578     public boolean isCompositionEnabled() {
579         /* isCompositionEnabledNative may throw UnsupportedOperationException.
580            Don't try to catch it since this method may be called by clients.
581            Use package private method 'getCompositionState' if you want the
582            exception to be caught.
583         */
584         return isCompositionEnabledNative();
585     }
586 
587     /**
588      * Ends any input composition that may currently be going on in this
589      * context. Depending on the platform and possibly user preferences,
590      * this may commit or delete uncommitted text. Any changes to the text
591      * are communicated to the active component using an input method event.
592      *
593      * <p>
594      * A text editing component may call this in a variety of situations,
595      * for example, when the user moves the insertion point within the text
596      * (but outside the composed text), or when the component's text is
597      * saved to a file or copied to the clipboard.
598      *
599      */
endComposition()600     public void endComposition() {
601         if (disposed) {
602             return;
603         }
604 
605         /* Before calling resetXIC, record the current composition mode
606            so that it can be restored later. */
607         savedCompositionState = getCompositionState();
608         boolean active = haveActiveClient();
609         if (active && composedText == null && committedText == null){
610             needResetXIC = true;
611             needResetXICClient = new WeakReference<>(getClientComponent());
612             return;
613         }
614 
615         String text = resetXIC();
616         /* needResetXIC is only set to true for active client. So passive
617            client should not reset the flag to false. */
618         if (active) {
619             needResetXIC = false;
620         }
621 
622         // Remove any existing composed text by posting an InputMethodEvent
623         // with null composed text.  It would be desirable to wait for a
624         // dispatchComposedText call from X input method engine, but some
625         // input method does not conform to the XIM specification and does
626         // not call the preedit callback to erase preedit text on calling
627         // XmbResetIC.  To work around this problem, do it here by ourselves.
628         awtLock();
629         try {
630             composedText = null;
631             postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
632                                  null,
633                                  0,
634                                  null,
635                                  null);
636 
637             if (text != null && text.length() > 0) {
638                 dispatchCommittedText(text);
639             }
640         } finally {
641             // Put awtUnlock into finally block in case an exception is thrown.
642             awtUnlock();
643         }
644 
645         // Restore the preedit state if it was enabled
646         if (savedCompositionState) {
647             resetCompositionState();
648         }
649     }
650 
651     /**
652      * Returns a string with information about the current input method server, or null.
653      * On both Linux & SunOS, the value of environment variable XMODIFIERS is
654      * returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed
655      * to find out the language service engine (atok or wnn) since there is
656      * no API in Xlib which returns the information of native
657      * IM server or language service and we want to try our best to return as much
658      * information as possible.
659      *
660      * Note: This method could return null on Linux if XMODIFIERS is not set properly or
661      * if any IOException is thrown.
662      * See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS,
663      * atok12setup(1) and wnn6setup(1) for the information written to
664      * $HOME/.dtprofile when you run these two commands.
665      *
666      */
getNativeInputMethodInfo()667     public String getNativeInputMethodInfo() {
668         String xmodifiers = System.getenv("XMODIFIERS");
669         String imInfo = null;
670 
671         // If XMODIFIERS is set, return the value
672         if (xmodifiers != null) {
673             int imIndex = xmodifiers.indexOf("@im=");
674             if (imIndex != -1) {
675                 imInfo = xmodifiers.substring(imIndex + 4);
676             }
677         } else if (System.getProperty("os.name").startsWith("SunOS")) {
678             File dtprofile = new File(System.getProperty("user.home") +
679                                       "/.dtprofile");
680             String languageEngineInfo = null;
681             try {
682                 BufferedReader br = new BufferedReader(new FileReader(dtprofile));
683                 String line = null;
684 
685                 while ( languageEngineInfo == null && (line = br.readLine()) != null) {
686                     if (line.contains("atok") || line.contains("wnn")) {
687                         StringTokenizer tokens =  new StringTokenizer(line);
688                         while (tokens.hasMoreTokens()) {
689                             String token = tokens.nextToken();
690                             if (Pattern.matches("atok.*setup", token) ||
691                                 Pattern.matches("wnn.*setup", token)){
692                                 languageEngineInfo = token.substring(0, token.indexOf("setup"));
693                                 break;
694                             }
695                         }
696                     }
697                 }
698 
699                 br.close();
700             } catch(IOException ioex) {
701                 // Since this method is provided for internal testing only,
702                 // we dump the stack trace for the ease of debugging.
703                 ioex.printStackTrace();
704             }
705 
706             imInfo = "htt " + languageEngineInfo;
707         }
708 
709         return imInfo;
710     }
711 
712 
713     /**
714      * Performs mapping from an XIM visible feedback value to Java IM highlight.
715      * @return Java input method highlight
716      */
convertVisualFeedbackToHighlight(int feedback)717     protected InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {
718         InputMethodHighlight highlight;
719 
720         switch (feedback) {
721         case XIMUnderline:
722             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
723             break;
724         case XIMReverse:
725             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
726             break;
727         case XIMHighlight:
728             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
729             break;
730         case 0: //None of the values are set by Wnn
731         case XIMPrimary:
732             highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
733             break;
734         case XIMSecondary:
735             highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
736             break;
737         case XIMTertiary:
738             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
739             break;
740         default:
741             highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
742             break;
743         }
744         return highlight;
745     }
746 
747     // initial capacity size for string buffer, etc.
748     protected static final int INITIAL_SIZE = 64;
749 
750     /**
751      * IntBuffer is an inner class that manipulates an int array and
752      * provides UNIX file io stream-like programming interfaces to
753      * access it. (An alternative would be to use ArrayList which may
754      * be too expensive for the work.)
755      */
756     protected final class IntBuffer {
757         private int[] intArray;
758         private int size;
759         private int index;
760 
IntBuffer(int initialCapacity)761         IntBuffer(int initialCapacity) {
762             intArray = new int[initialCapacity];
763             size = 0;
764             index = 0;
765         }
766 
insert(int offset, int[] values)767         void insert(int offset, int[] values) {
768             int newSize = size + values.length;
769             if (intArray.length < newSize) {
770                 int[] newIntArray = new int[newSize * 2];
771                 System.arraycopy(intArray, 0, newIntArray, 0, size);
772                 intArray = newIntArray;
773             }
774             System.arraycopy(intArray, offset, intArray, offset+values.length,
775                              size - offset);
776             System.arraycopy(values, 0, intArray, offset, values.length);
777             size += values.length;
778             if (index > offset)
779                 index = offset;
780         }
781 
remove(int offset, int length)782         void remove(int offset, int length) {
783             if (offset + length != size)
784                 System.arraycopy(intArray, offset+length, intArray, offset,
785                                  size - offset - length);
786             size -= length;
787             if (index > offset)
788                 index = offset;
789         }
790 
replace(int offset, int[] values)791         void replace(int offset, int[] values) {
792             System.arraycopy(values, 0, intArray, offset, values.length);
793         }
794 
removeAll()795         void removeAll() {
796             size = 0;
797             index = 0;
798         }
799 
rewind()800         void rewind() {
801             index = 0;
802         }
803 
getNext()804         int getNext() {
805             if (index == size)
806                 return -1;
807             return intArray[index++];
808         }
809 
unget()810         void unget() {
811             if (index != 0)
812                 index--;
813         }
814 
getOffset()815         int getOffset() {
816             return index;
817         }
818 
toString()819         public String toString() {
820             StringBuffer s = new StringBuffer();
821             for (int i = 0; i < size;) {
822                 s.append(intArray[i++]);
823                 if (i < size)
824                     s.append(",");
825             }
826             return s.toString();
827         }
828     }
829 
830     /*
831      * Native methods
832      */
833 
834     /**
835      * Initialize JNI field and method IDs for fields that may be
836      * accessed from C.
837      */
initIDs()838     private static native void initIDs();
839 
turnoffStatusWindow()840     protected native void turnoffStatusWindow();
841 
disposeXIC()842     protected native void disposeXIC();
843 
resetXIC()844     private native String resetXIC();
845 
setCompositionEnabledNative(boolean enable)846     protected native boolean setCompositionEnabledNative(boolean enable);
847 
isCompositionEnabledNative()848     private native boolean isCompositionEnabledNative();
849 }
850