1 /*
   2  * Copyright (c) 1997, 2019, 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);
 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);
 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);
 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);
 151         map.put(TextAttribute.SWAP_COLORS, TextAttribute.SWAP_COLORS_ON);
 152         styles[3] = Collections.unmodifiableMap(map);
 153 
 154         highlightStyles = styles;
 155     }
 156 
 157     static {
 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      */
 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")
 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      */
 185     private synchronized boolean initXIM() {
 186         if (isXIMOpened == false)
 187             isXIMOpened = openXIM();
 188         return isXIMOpened;
 189     }
 190 
 191     protected abstract boolean openXIM();
 192 
 193     protected boolean isDisposed() {
 194         return disposed;
 195     }
 196 
 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      */
 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      */
 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      */
 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      */
 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      */
 249     public void dispatchEvent(AWTEvent e) {
 250     }
 251 
 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      */
 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      */
 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      */
 295     public abstract void activate();
 296 
 297     protected abstract boolean createXIC();
 298 
 299     /**
 300      * Deactivate input method.
 301      */
 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      */
 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
 321     public abstract void hideWindows();
 322 
 323     /**
 324      * @see java.awt.Toolkit#mapInputMethodHighlight
 325      */
 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      */
 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      */
 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!
 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 
 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      */
 407     protected abstract ComponentPeer getPeer(Component client);
 408 
 409     /**
 410      * Used to protect preedit data
 411      */
 412     protected abstract void awtLock();
 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      */
 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 
 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!
 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 
 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!
 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!
 508     void flushText() {
 509         String flush = (committedText != null ? committedText : "");
 510         if (composedText != null) {
 511             flush += composedText.toString();
 512         }
 513 
 514         if (!flush.isEmpty()) {
 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      */
 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      */
 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      */
 559     public Object getControlObject() {
 560         return null;
 561     }
 562 
 563     /**
 564      * @see java.awt.im.spi.InputMethod#removeNotify
 565      */
 566     public synchronized void removeNotify() {
 567         dispose();
 568     }
 569 
 570     /**
 571      * @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
 572      */
 573     public abstract void setCompositionEnabled(boolean enable);
 574 
 575     /**
 576      * @see java.awt.im.spi.InputMethod#isCompositionEnabled
 577      */
 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      */
 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      */
 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      */
 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 
 761         IntBuffer(int initialCapacity) {
 762             intArray = new int[initialCapacity];
 763             size = 0;
 764             index = 0;
 765         }
 766 
 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 
 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 
 791         void replace(int offset, int[] values) {
 792             System.arraycopy(values, 0, intArray, offset, values.length);
 793         }
 794 
 795         void removeAll() {
 796             size = 0;
 797             index = 0;
 798         }
 799 
 800         void rewind() {
 801             index = 0;
 802         }
 803 
 804         int getNext() {
 805             if (index == size)
 806                 return -1;
 807             return intArray[index++];
 808         }
 809 
 810         void unget() {
 811             if (index != 0)
 812                 index--;
 813         }
 814 
 815         int getOffset() {
 816             return index;
 817         }
 818 
 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      */
 838     private static native void initIDs();
 839 
 840     protected native void turnoffStatusWindow();
 841 
 842     protected native void disposeXIC();
 843 
 844     private native String resetXIC();
 845 
 846     protected native boolean setCompositionEnabledNative(boolean enable);
 847 
 848     private native boolean isCompositionEnabledNative();
 849 }