Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11InputMethod.java
32287 views
/*1* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt;2627import java.util.Collections;28import java.util.Locale;29import java.util.Map;30import java.util.HashMap;31import java.awt.AWTEvent;32import java.awt.AWTException;33import java.awt.Component;34import java.awt.Container;35import java.awt.EventQueue;36import java.awt.Window;37import java.awt.im.InputContext;38import java.awt.im.InputMethodHighlight;39import java.awt.im.spi.InputMethodContext;40import sun.awt.im.InputMethodAdapter;41import java.awt.event.InputEvent;42import java.awt.event.KeyEvent;43import java.awt.event.MouseEvent;44import java.awt.event.FocusEvent;45import java.awt.event.ComponentEvent;46import java.awt.event.WindowEvent;47import java.awt.event.InputMethodEvent;48import java.awt.font.TextAttribute;49import java.awt.font.TextHitInfo;50import java.awt.peer.ComponentPeer;51import java.lang.Character.Subset;52import java.text.AttributedString;53import java.text.AttributedCharacterIterator;5455import java.io.File;56import java.io.FileReader;57import java.io.BufferedReader;58import java.io.IOException;59import java.lang.ref.WeakReference;60import sun.util.logging.PlatformLogger;61import java.util.StringTokenizer;62import java.util.regex.Pattern;636465/**66* Input Method Adapter for XIM67*68* @author JavaSoft International69*/70public abstract class X11InputMethod extends InputMethodAdapter {71private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod");72/*73* The following XIM* values must be the same as those defined in74* Xlib.h75*/76private static final int XIMReverse = (1<<0);77private static final int XIMUnderline = (1<<1);78private static final int XIMHighlight = (1<<2);79private static final int XIMPrimary = (1<<5);80private static final int XIMSecondary = (1<<6);81private static final int XIMTertiary = (1<<7);8283/*84* visible position values85*/86private static final int XIMVisibleToForward = (1<<8);87private static final int XIMVisibleToBackward = (1<<9);88private static final int XIMVisibleCenter = (1<<10);89private static final int XIMVisibleMask = (XIMVisibleToForward|90XIMVisibleToBackward|91XIMVisibleCenter);9293private Locale locale;94private static boolean isXIMOpened = false;95protected Container clientComponentWindow = null;96private Component awtFocussedComponent = null;97private Component lastXICFocussedComponent = null;98private boolean isLastXICActive = false;99private boolean isLastTemporary = false;100private boolean isActive = false;101private boolean isActiveClient = false;102private static Map[] highlightStyles;103private boolean disposed = false;104105//reset the XIC if necessary106private boolean needResetXIC = false;107private WeakReference<Component> needResetXICClient = new WeakReference<>(null);108109// The use of compositionEnableSupported is to reduce unnecessary110// native calls if set/isCompositionEnabled111// throws UnsupportedOperationException.112// It is set to false if that exception is thrown first time113// either of the two methods are called.114private boolean compositionEnableSupported = true;115// The savedCompositionState indicates the composition mode when116// endComposition or setCompositionEnabled is called. It doesn't always117// reflect the actual composition state because it doesn't get updated118// when the user changes the composition state through direct interaction119// with the input method. It is used to save the composition mode when120// focus is traversed across different client components sharing the121// same java input context. Also if set/isCompositionEnabled are not122// supported, it remains false.123private boolean savedCompositionState = false;124125// variables to keep track of preedit context.126// these variables need to be accessed within AWT_LOCK/UNLOCK127private String committedText = null;128private StringBuffer composedText = null;129private IntBuffer rawFeedbacks;130131// private data (X11InputMethodData structure defined in132// awt_InputMethod.c) for native methods133// this structure needs to be accessed within AWT_LOCK/UNLOCK134transient private long pData = 0; // accessed by native135136// Initialize highlight mapping table137static {138Map styles[] = new Map[4];139HashMap map;140141// UNSELECTED_RAW_TEXT_HIGHLIGHT142map = new HashMap(1);143map.put(TextAttribute.WEIGHT,144TextAttribute.WEIGHT_BOLD);145styles[0] = Collections.unmodifiableMap(map);146147// SELECTED_RAW_TEXT_HIGHLIGHT148map = new HashMap(1);149map.put(TextAttribute.SWAP_COLORS,150TextAttribute.SWAP_COLORS_ON);151styles[1] = Collections.unmodifiableMap(map);152153// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT154map = new HashMap(1);155map.put(TextAttribute.INPUT_METHOD_UNDERLINE,156TextAttribute.UNDERLINE_LOW_ONE_PIXEL);157styles[2] = Collections.unmodifiableMap(map);158159// SELECTED_CONVERTED_TEXT_HIGHLIGHT160map = new HashMap(1);161map.put(TextAttribute.SWAP_COLORS,162TextAttribute.SWAP_COLORS_ON);163styles[3] = Collections.unmodifiableMap(map);164165highlightStyles = styles;166}167168static {169initIDs();170}171172/**173* Initialize JNI field and method IDs for fields that may be174accessed from C.175*/176private static native void initIDs();177178/**179* Constructs an X11InputMethod instance. It initializes the XIM180* environment if it's not done yet.181*182* @exception AWTException if XOpenIM() failed.183*/184public X11InputMethod() throws AWTException {185// supports only the locale in which the VM is started186locale = X11InputMethodDescriptor.getSupportedLocale();187if (initXIM() == false) {188throw new AWTException("Cannot open X Input Method");189}190}191192protected void finalize() throws Throwable {193dispose();194super.finalize();195}196197/**198* Invokes openIM() that invokes XOpenIM() if it's not opened yet.199* @return true if openXIM() is successful or it's already been opened.200*/201private synchronized boolean initXIM() {202if (isXIMOpened == false)203isXIMOpened = openXIM();204return isXIMOpened;205}206207protected abstract boolean openXIM();208209protected boolean isDisposed() {210return disposed;211}212213protected abstract void setXICFocus(ComponentPeer peer,214boolean value, boolean active);215216/**217* Does nothing - this adapter doesn't use the input method context.218*219* @see java.awt.im.spi.InputMethod#setInputMethodContext220*/221public void setInputMethodContext(InputMethodContext context) {222}223224/**225* Set locale to input. If input method doesn't support specified locale,226* false will be returned and its behavior is not changed.227*228* @param lang locale to input229* @return the true is returned when specified locale is supported.230*/231public boolean setLocale(Locale lang) {232if (lang.equals(locale)) {233return true;234}235// special compatibility rule for Japanese and Korean236if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||237locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {238return true;239}240return false;241}242243/**244* Returns current input locale.245*/246public Locale getLocale() {247return locale;248}249250/**251* Does nothing - XIM doesn't let you specify which characters you expect.252*253* @see java.awt.im.spi.InputMethod#setCharacterSubsets254*/255public void setCharacterSubsets(Subset[] subsets) {256}257258/**259* Dispatch event to input method. InputContext dispatch event with this260* method. Input method set consume flag if event is consumed in261* input method.262*263* @param e event264*/265public void dispatchEvent(AWTEvent e) {266}267268269protected final void resetXICifneeded(){270/* needResetXIC is used to indicate whether to call271resetXIC on the active client. resetXIC will always be272called on the passive client when endComposition is called.273*/274if (needResetXIC && haveActiveClient() &&275getClientComponent() != needResetXICClient.get()){276resetXIC();277278// needs to reset the last xic focussed component.279lastXICFocussedComponent = null;280isLastXICActive = false;281282needResetXICClient.clear();283needResetXIC = false;284}285}286287/**288* Reset the composition state to the current composition state.289*/290private void resetCompositionState() {291if (compositionEnableSupported) {292try {293/* Restore the composition mode to the last saved composition294mode. */295setCompositionEnabled(savedCompositionState);296} catch (UnsupportedOperationException e) {297compositionEnableSupported = false;298}299}300}301302/**303* Query and then return the current composition state.304* @returns the composition state if isCompositionEnabled call305* is successful. Otherwise, it returns false.306*/307private boolean getCompositionState() {308boolean compositionState = false;309if (compositionEnableSupported) {310try {311compositionState = isCompositionEnabled();312} catch (UnsupportedOperationException e) {313compositionEnableSupported = false;314}315}316return compositionState;317}318319/**320* Activate input method.321*/322public synchronized void activate() {323clientComponentWindow = getClientComponentWindow();324if (clientComponentWindow == null)325return;326327if (lastXICFocussedComponent != null){328if (log.isLoggable(PlatformLogger.Level.FINE)) {329log.fine("XICFocused {0}, AWTFocused {1}",330lastXICFocussedComponent, awtFocussedComponent);331}332}333334if (pData == 0) {335if (!createXIC()) {336return;337}338disposed = false;339}340341/* reset input context if necessary and set the XIC focus342*/343resetXICifneeded();344ComponentPeer lastXICFocussedComponentPeer = null;345ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);346347if (lastXICFocussedComponent != null) {348lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);349}350351/* If the last XIC focussed component has a different peer as the352current focussed component, change the XIC focus to the newly353focussed component.354*/355if (isLastTemporary || lastXICFocussedComponentPeer != awtFocussedComponentPeer ||356isLastXICActive != haveActiveClient()) {357if (lastXICFocussedComponentPeer != null) {358setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);359}360if (awtFocussedComponentPeer != null) {361setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());362}363lastXICFocussedComponent = awtFocussedComponent;364isLastXICActive = haveActiveClient();365}366resetCompositionState();367isActive = true;368}369370protected abstract boolean createXIC();371372/**373* Deactivate input method.374*/375public synchronized void deactivate(boolean isTemporary) {376boolean isAc = haveActiveClient();377/* Usually as the client component, let's call it component A,378loses the focus, this method is called. Then when another client379component, let's call it component B, gets the focus, activate is first called on380the previous focused compoent which is A, then endComposition is called on A,381deactivate is called on A again. And finally activate is called on the newly382focused component B. Here is the call sequence.383384A loses focus B gains focus385-------------> deactivate A -------------> activate A -> endComposition A ->386deactivate A -> activate B ----....387388So in order to carry the composition mode across the components sharing the same389input context, we save it when deactivate is called so that when activate is390called, it can be restored correctly till activate is called on the newly focused391component. (See also sun/awt/im/InputContext and bug 6184471).392Last note, getCompositionState should be called before setXICFocus since393setXICFocus here sets the XIC to 0.394*/395savedCompositionState = getCompositionState();396397if (isTemporary){398//turn the status window off...399turnoffStatusWindow();400}401402/* Delay resetting the XIC focus until activate is called and the newly403focussed component has a different peer as the last focussed component.404*/405lastXICFocussedComponent = awtFocussedComponent;406isLastXICActive = isAc;407isLastTemporary = isTemporary;408isActive = false;409}410411/**412* Explicitly disable the native IME. Native IME is not disabled when413* deactivate is called.414*/415public void disableInputMethod() {416if (lastXICFocussedComponent != null) {417setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive);418lastXICFocussedComponent = null;419isLastXICActive = false;420421resetXIC();422needResetXICClient.clear();423needResetXIC = false;424}425}426427// implements java.awt.im.spi.InputMethod.hideWindows428public void hideWindows() {429// ??? need real implementation430}431432/**433* @see java.awt.Toolkit#mapInputMethodHighlight434*/435public static Map mapInputMethodHighlight(InputMethodHighlight highlight) {436int index;437int state = highlight.getState();438if (state == InputMethodHighlight.RAW_TEXT) {439index = 0;440} else if (state == InputMethodHighlight.CONVERTED_TEXT) {441index = 2;442} else {443return null;444}445if (highlight.isSelected()) {446index += 1;447}448return highlightStyles[index];449}450451/**452* @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent453*/454protected void setAWTFocussedComponent(Component component) {455if (component == null) {456return;457}458if (isActive) {459// deactivate/activate are being suppressed during a focus change -460// this may happen when an input method window is made visible461boolean ac = haveActiveClient();462setXICFocus(getPeer(awtFocussedComponent), false, ac);463setXICFocus(getPeer(component), true, ac);464}465awtFocussedComponent = component;466}467468/**469* @see sun.awt.im.InputMethodAdapter#stopListening470*/471protected void stopListening() {472// It is desirable to disable XIM by calling XSetICValues with473// XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and474// Solaris 7 do not implement this correctly without a patch,475// so just call resetXIC here. Prior endComposition call commits476// the existing composed text.477endComposition();478// disable the native input method so that the other input479// method could get the input focus.480disableInputMethod();481if (needResetXIC) {482resetXIC();483needResetXICClient.clear();484needResetXIC = false;485}486}487488/**489* Returns the Window instance in which the client component is490* contained. If not found, null is returned. (IS THIS POSSIBLE?)491*/492// NOTE: This method may be called by privileged threads.493// DO NOT INVOKE CLIENT CODE ON THIS THREAD!494private Window getClientComponentWindow() {495Component client = getClientComponent();496Container container;497498if (client instanceof Container) {499container = (Container) client;500} else {501container = getParent(client);502}503504while (container != null && !(container instanceof java.awt.Window)) {505container = getParent(container);506}507return (Window) container;508}509510protected abstract Container getParent(Component client);511512/**513* Returns peer of the given client component. If the given client component514* doesn't have peer, peer of the native container of the client is returned.515*/516protected abstract ComponentPeer getPeer(Component client);517518/**519* Used to protect preedit data520*/521protected abstract void awtLock();522protected abstract void awtUnlock();523524/**525* Creates an input method event from the arguments given526* and posts it on the AWT event queue. For arguments,527* see InputMethodEvent. Called by input method.528*529* @see java.awt.event.InputMethodEvent#InputMethodEvent530*/531private void postInputMethodEvent(int id,532AttributedCharacterIterator text,533int committedCharacterCount,534TextHitInfo caret,535TextHitInfo visiblePosition,536long when) {537Component source = getClientComponent();538if (source != null) {539InputMethodEvent event = new InputMethodEvent(source,540id, when, text, committedCharacterCount, caret, visiblePosition);541SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event);542}543}544545private void postInputMethodEvent(int id,546AttributedCharacterIterator text,547int committedCharacterCount,548TextHitInfo caret,549TextHitInfo visiblePosition) {550postInputMethodEvent(id, text, committedCharacterCount,551caret, visiblePosition, EventQueue.getMostRecentEventTime());552}553554/**555* Dispatches committed text from XIM to the awt event queue. This556* method is invoked from the event handler in canvas.c in the557* AWT Toolkit thread context and thus inside the AWT Lock.558* @param str committed text559* @param long when560*/561// NOTE: This method may be called by privileged threads.562// This functionality is implemented in a package-private method563// to insure that it cannot be overridden by client subclasses.564// DO NOT INVOKE CLIENT CODE ON THIS THREAD!565void dispatchCommittedText(String str, long when) {566if (str == null)567return;568569if (composedText == null) {570AttributedString attrstr = new AttributedString(str);571postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,572attrstr.getIterator(),573str.length(),574null,575null,576when);577} else {578// if there is composed text, wait until the preedit579// callback is invoked.580committedText = str;581}582}583584private void dispatchCommittedText(String str) {585dispatchCommittedText(str, EventQueue.getMostRecentEventTime());586}587588/**589* Updates composed text with XIM preedit information and590* posts composed text to the awt event queue. The args of591* this method correspond to the XIM preedit callback592* information. The XIM highlight attributes are translated via593* fixed mapping (i.e., independent from any underlying input594* method engine). This method is invoked in the AWT Toolkit595* (X event loop) thread context and thus inside the AWT Lock.596*/597// NOTE: This method may be called by privileged threads.598// This functionality is implemented in a package-private method599// to insure that it cannot be overridden by client subclasses.600// DO NOT INVOKE CLIENT CODE ON THIS THREAD!601void dispatchComposedText(String chgText,602int chgStyles[],603int chgOffset,604int chgLength,605int caretPosition,606long when) {607if (disposed) {608return;609}610611//Workaround for deadlock bug on solaris2.6_zh bug#4170760612if (chgText == null613&& chgStyles == null614&& chgOffset == 0615&& chgLength == 0616&& caretPosition == 0617&& composedText == null618&& committedText == null)619return;620621if (composedText == null) {622// TODO: avoid reallocation of those buffers623composedText = new StringBuffer(INITIAL_SIZE);624rawFeedbacks = new IntBuffer(INITIAL_SIZE);625}626if (chgLength > 0) {627if (chgText == null && chgStyles != null) {628rawFeedbacks.replace(chgOffset, chgStyles);629} else {630if (chgLength == composedText.length()) {631// optimization for the special case to replace the632// entire previous text633composedText = new StringBuffer(INITIAL_SIZE);634rawFeedbacks = new IntBuffer(INITIAL_SIZE);635} else {636if (composedText.length() > 0) {637if (chgOffset+chgLength < composedText.length()) {638String text;639text = composedText.toString().substring(chgOffset+chgLength,640composedText.length());641composedText.setLength(chgOffset);642composedText.append(text);643} else {644// in case to remove substring from chgOffset645// to the end646composedText.setLength(chgOffset);647}648rawFeedbacks.remove(chgOffset, chgLength);649}650}651}652}653if (chgText != null) {654composedText.insert(chgOffset, chgText);655if (chgStyles != null)656rawFeedbacks.insert(chgOffset, chgStyles);657}658659if (composedText.length() == 0) {660composedText = null;661rawFeedbacks = null;662663// if there is any outstanding committed text stored by664// dispatchCommittedText(), it has to be sent to the665// client component.666if (committedText != null) {667dispatchCommittedText(committedText, when);668committedText = null;669return;670}671672// otherwise, send null text to delete client's composed673// text.674postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,675null,6760,677null,678null,679when);680681return;682}683684// Now sending the composed text to the client685int composedOffset;686AttributedString inputText;687688// if there is any partially committed text, concatenate it to689// the composed text.690if (committedText != null) {691composedOffset = committedText.length();692inputText = new AttributedString(committedText + composedText);693committedText = null;694} else {695composedOffset = 0;696inputText = new AttributedString(composedText.toString());697}698699int currentFeedback;700int nextFeedback;701int startOffset = 0;702int currentOffset;703int visiblePosition = 0;704TextHitInfo visiblePositionInfo = null;705706rawFeedbacks.rewind();707currentFeedback = rawFeedbacks.getNext();708rawFeedbacks.unget();709while ((nextFeedback = rawFeedbacks.getNext()) != -1) {710if (visiblePosition == 0) {711visiblePosition = nextFeedback & XIMVisibleMask;712if (visiblePosition != 0) {713int index = rawFeedbacks.getOffset() - 1;714715if (visiblePosition == XIMVisibleToBackward)716visiblePositionInfo = TextHitInfo.leading(index);717else718visiblePositionInfo = TextHitInfo.trailing(index);719}720}721nextFeedback &= ~XIMVisibleMask;722if (currentFeedback != nextFeedback) {723rawFeedbacks.unget();724currentOffset = rawFeedbacks.getOffset();725inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,726convertVisualFeedbackToHighlight(currentFeedback),727composedOffset + startOffset,728composedOffset + currentOffset);729startOffset = currentOffset;730currentFeedback = nextFeedback;731}732}733currentOffset = rawFeedbacks.getOffset();734if (currentOffset >= 0) {735inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,736convertVisualFeedbackToHighlight(currentFeedback),737composedOffset + startOffset,738composedOffset + currentOffset);739}740741postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,742inputText.getIterator(),743composedOffset,744TextHitInfo.leading(caretPosition),745visiblePositionInfo,746when);747}748749/**750* Flushes composed and committed text held in this context.751* This method is invoked in the AWT Toolkit (X event loop) thread context752* and thus inside the AWT Lock.753*/754// NOTE: This method may be called by privileged threads.755// This functionality is implemented in a package-private method756// to insure that it cannot be overridden by client subclasses.757// DO NOT INVOKE CLIENT CODE ON THIS THREAD!758void flushText() {759String flush = (committedText != null ? committedText : "");760if (composedText != null) {761flush += composedText.toString();762}763764if (!flush.equals("")) {765AttributedString attrstr = new AttributedString(flush);766postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,767attrstr.getIterator(),768flush.length(),769null,770null,771EventQueue.getMostRecentEventTime());772composedText = null;773committedText = null;774}775}776777/*778* Subclasses should override disposeImpl() instead of dispose(). Client779* code should always invoke dispose(), never disposeImpl().780*/781protected synchronized void disposeImpl() {782disposeXIC();783awtLock();784composedText = null;785committedText = null;786rawFeedbacks = null;787awtUnlock();788awtFocussedComponent = null;789lastXICFocussedComponent = null;790}791792/**793* Frees all X Window resources associated with this object.794*795* @see java.awt.im.spi.InputMethod#dispose796*/797public final void dispose() {798boolean call_disposeImpl = false;799800if (!disposed) {801synchronized (this) {802if (!disposed) {803disposed = call_disposeImpl = true;804}805}806}807808if (call_disposeImpl) {809disposeImpl();810}811}812813/**814* Returns null.815*816* @see java.awt.im.spi.InputMethod#getControlObject817*/818public Object getControlObject() {819return null;820}821822/**823* @see java.awt.im.spi.InputMethod#removeNotify824*/825public synchronized void removeNotify() {826dispose();827}828829/**830* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)831*/832public void setCompositionEnabled(boolean enable) {833/* If the composition state is successfully changed, set834the savedCompositionState to 'enable'. Otherwise, simply835return.836setCompositionEnabledNative may throw UnsupportedOperationException.837Don't try to catch it since the method may be called by clients.838Use package private mthod 'resetCompositionState' if you want the839exception to be caught.840*/841if (setCompositionEnabledNative(enable)) {842savedCompositionState = enable;843}844}845846/**847* @see java.awt.im.spi.InputMethod#isCompositionEnabled848*/849public boolean isCompositionEnabled() {850/* isCompositionEnabledNative may throw UnsupportedOperationException.851Don't try to catch it since this method may be called by clients.852Use package private method 'getCompositionState' if you want the853exception to be caught.854*/855return isCompositionEnabledNative();856}857858/**859* Ends any input composition that may currently be going on in this860* context. Depending on the platform and possibly user preferences,861* this may commit or delete uncommitted text. Any changes to the text862* are communicated to the active component using an input method event.863*864* <p>865* A text editing component may call this in a variety of situations,866* for example, when the user moves the insertion point within the text867* (but outside the composed text), or when the component's text is868* saved to a file or copied to the clipboard.869*870*/871public void endComposition() {872if (disposed) {873return;874}875876/* Before calling resetXIC, record the current composition mode877so that it can be restored later. */878savedCompositionState = getCompositionState();879boolean active = haveActiveClient();880if (active && composedText == null && committedText == null){881needResetXIC = true;882needResetXICClient = new WeakReference<>(getClientComponent());883return;884}885886String text = resetXIC();887/* needResetXIC is only set to true for active client. So passive888client should not reset the flag to false. */889if (active) {890needResetXIC = false;891}892893// Remove any existing composed text by posting an InputMethodEvent894// with null composed text. It would be desirable to wait for a895// dispatchComposedText call from X input method engine, but some896// input method does not conform to the XIM specification and does897// not call the preedit callback to erase preedit text on calling898// XmbResetIC. To work around this problem, do it here by ourselves.899awtLock();900composedText = null;901postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,902null,9030,904null,905null);906907if (text != null && text.length() > 0) {908dispatchCommittedText(text);909}910awtUnlock();911912// Restore the preedit state if it was enabled913if (savedCompositionState) {914resetCompositionState();915}916}917918/**919* Returns a string with information about the current input method server, or null.920* On both Linux & SunOS, the value of environment variable XMODIFIERS is921* returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed922* to find out the language service engine (atok or wnn) since there is923* no API in Xlib which returns the information of native924* IM server or language service and we want to try our best to return as much925* information as possible.926*927* Note: This method could return null on Linux if XMODIFIERS is not set properly or928* if any IOException is thrown.929* See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS,930* atok12setup(1) and wnn6setup(1) for the information written to931* $HOME/.dtprofile when you run these two commands.932*933*/934public String getNativeInputMethodInfo() {935String xmodifiers = System.getenv("XMODIFIERS");936String imInfo = null;937938// If XMODIFIERS is set, return the value939if (xmodifiers != null) {940int imIndex = xmodifiers.indexOf("@im=");941if (imIndex != -1) {942imInfo = xmodifiers.substring(imIndex + 4);943}944} else if (System.getProperty("os.name").startsWith("SunOS")) {945File dtprofile = new File(System.getProperty("user.home") +946"/.dtprofile");947String languageEngineInfo = null;948try {949BufferedReader br = new BufferedReader(new FileReader(dtprofile));950String line = null;951952while ( languageEngineInfo == null && (line = br.readLine()) != null) {953if (line.contains("atok") || line.contains("wnn")) {954StringTokenizer tokens = new StringTokenizer(line);955while (tokens.hasMoreTokens()) {956String token = tokens.nextToken();957if (Pattern.matches("atok.*setup", token) ||958Pattern.matches("wnn.*setup", token)){959languageEngineInfo = token.substring(0, token.indexOf("setup"));960break;961}962}963}964}965966br.close();967} catch(IOException ioex) {968// Since this method is provided for internal testing only,969// we dump the stack trace for the ease of debugging.970ioex.printStackTrace();971}972973imInfo = "htt " + languageEngineInfo;974}975976return imInfo;977}978979980/**981* Performs mapping from an XIM visible feedback value to Java IM highlight.982* @return Java input method highlight983*/984private InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {985InputMethodHighlight highlight;986987switch (feedback) {988case XIMUnderline:989highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;990break;991case XIMReverse:992highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;993break;994case XIMHighlight:995highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;996break;997case XIMPrimary:998highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;999break;1000case XIMSecondary:1001highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;1002break;1003case XIMTertiary:1004highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;1005break;1006default:1007highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;1008break;1009}1010return highlight;1011}10121013// initial capacity size for string buffer, etc.1014private static final int INITIAL_SIZE = 64;10151016/**1017* IntBuffer is an inner class that manipulates an int array and1018* provides UNIX file io stream-like programming interfaces to1019* access it. (An alternative would be to use ArrayList which may1020* be too expensive for the work.)1021*/1022private final class IntBuffer {1023private int[] intArray;1024private int size;1025private int index;10261027IntBuffer(int initialCapacity) {1028intArray = new int[initialCapacity];1029size = 0;1030index = 0;1031}10321033void insert(int offset, int[] values) {1034int newSize = size + values.length;1035if (intArray.length < newSize) {1036int[] newIntArray = new int[newSize * 2];1037System.arraycopy(intArray, 0, newIntArray, 0, size);1038intArray = newIntArray;1039}1040System.arraycopy(intArray, offset, intArray, offset+values.length,1041size - offset);1042System.arraycopy(values, 0, intArray, offset, values.length);1043size += values.length;1044if (index > offset)1045index = offset;1046}10471048void remove(int offset, int length) {1049if (offset + length != size)1050System.arraycopy(intArray, offset+length, intArray, offset,1051size - offset - length);1052size -= length;1053if (index > offset)1054index = offset;1055}10561057void replace(int offset, int[] values) {1058System.arraycopy(values, 0, intArray, offset, values.length);1059}10601061void removeAll() {1062size = 0;1063index = 0;1064}10651066void rewind() {1067index = 0;1068}10691070int getNext() {1071if (index == size)1072return -1;1073return intArray[index++];1074}10751076void unget() {1077if (index != 0)1078index--;1079}10801081int getOffset() {1082return index;1083}10841085public String toString() {1086StringBuffer s = new StringBuffer();1087for (int i = 0; i < size;) {1088s.append(intArray[i++]);1089if (i < size)1090s.append(",");1091}1092return s.toString();1093}1094}10951096/*1097* Native methods1098*/1099protected native String resetXIC();1100private native void disposeXIC();1101private native boolean setCompositionEnabledNative(boolean enable);1102private native boolean isCompositionEnabledNative();1103private native void turnoffStatusWindow();1104}110511061107