///*
// * Copyright (C) 2009 Google Inc.
// *
// * Licensed under the Apache License, Version 2.0 (the "License"); you may not
// * use this file except in compliance with the License. You may obtain a copy of
// * the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// * License for the specific language governing permissions and limitations under
// * the License.
// */
//
//package com.anysoftkeyboard.utils;
//
//import android.text.TextUtils;
//import android.view.inputmethod.ExtractedText;
//import android.view.inputmethod.ExtractedTextRequest;
//import android.view.inputmethod.InputConnection;
//
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//import java.util.regex.Pattern;
//
///**
// * Utility methods to deal with editing text through an InputConnection.
// */
//public class EditingUtil {
// /**
// * Number of characters we want to look back in order to identify the previous word
// */
// private static final int LOOKBACK_CHARACTER_NUM = 15;
//
// // Cache Method pointers
// private static boolean sMethodsInitialized;
// private static Method sMethodGetSelectedText;
// private static Method sMethodSetComposingRegion;
//
// private EditingUtil() {};
//
// /**
// * Append newText to the text field represented by connection.
// * The new text becomes selected.
// */
// public static void appendText(InputConnection connection, String newText) {
// if (connection == null) {
// return;
// }
//
// // Commit the composing text
// connection.finishComposingText();
//
// // Add a space if the field already has text.
// CharSequence charBeforeCursor = connection.getTextBeforeCursor(1, 0);
// if (charBeforeCursor != null
// && !charBeforeCursor.equals(" ")
// && (charBeforeCursor.length() > 0)) {
// newText = " " + newText;
// }
//
// connection.setComposingText(newText, 1);
// }
//
// private static int getCursorPosition(InputConnection connection) {
// ExtractedText extracted = connection.getExtractedText(
// new ExtractedTextRequest(), 0);
// if (extracted == null) {
// return -1;
// }
// return extracted.startOffset + extracted.selectionStart;
// }
//
// /**
// * @param connection connection to the current text field.
// * @param sep characters which may separate words
// * @param range the range object to store the result into
// * @return the word that surrounds the cursor, including up to one trailing
// * separator. For example, if the field contains "he|llo world", where |
// * represents the cursor, then "hello " will be returned.
// */
// public static String getWordAtCursor(
// InputConnection connection, Range range) {
// Range r = getWordRangeAtCursor(connection, range);
// return (r == null) ? null : r.word;
// }
//
// /**
// * Removes the word surrounding the cursor. Parameters are identical to
// * getWordAtCursor.
// */
// public static void deleteWordAtCursor(InputConnection connection) {
//
// Range range = getWordRangeAtCursor(connection, null);
// if (range == null) return;
//
// connection.finishComposingText();
// // Move cursor to beginning of word, to avoid crash when cursor is outside
// // of valid range after deleting text.
// int newCursor = getCursorPosition(connection) - range.charsBefore;
// connection.setSelection(newCursor, newCursor);
// connection.deleteSurroundingText(0, range.charsBefore + range.charsAfter);
// }
//
// /**
// * Represents a range of text, relative to the current cursor position.
// */
// public static class Range {
// /** Characters before selection start */
// public int charsBefore;
//
// /**
// * Characters after selection start, including one trailing word
// * separator.
// */
// public int charsAfter;
//
// /** The actual characters that make up a word */
// public String word;
//
// public Range() {}
//
// public Range(int charsBefore, int charsAfter, String word) {
// if (charsBefore < 0 || charsAfter < 0) {
// throw new IndexOutOfBoundsException();
// }
// this.charsBefore = charsBefore;
// this.charsAfter = charsAfter;
// this.word = word;
// }
// }
//
// private static Range getWordRangeAtCursor(
// InputConnection connection, Range range) {
// if (connection == null) {
// return null;
// }
// CharSequence before = connection.getTextBeforeCursor(1000, 0);
// CharSequence after = connection.getTextAfterCursor(1000, 0);
// if (before == null || after == null) {
// return null;
// }
//
// // Find first word separator before the cursor
// int start = before.length();
// while (start > 0 && !isWhitespace(before.charAt(start - 1))) start--;
//
// // Find last word separator after the cursor
// int end = -1;
// while (++end < after.length() && !isWhitespace(after.charAt(end)));
//
// int cursor = getCursorPosition(connection);
// if (start >= 0 && cursor + end <= after.length() + before.length()) {
// String word = before.toString().substring(start, before.length())
// + after.toString().substring(0, end);
//
// Range returnRange = range != null? range : new Range();
// returnRange.charsBefore = before.length() - start;
// returnRange.charsAfter = end;
// returnRange.word = word;
// return returnRange;
// }
//
// return null;
// }
//
// private static boolean isWhitespace(int code) {
// //return whitespace.contains(String.valueOf((char) code));
// return !Character.isLetter((char)code);
// }
//
// private static final Pattern spaceRegex = Pattern.compile("\\s+");
//
// public static CharSequence getPreviousWord(InputConnection connection,
// String sentenceSeperators) {
// //TODO: Should fix this. This could be slow!
// CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
// if (prev == null) {
// return null;
// }
// String[] w = spaceRegex.split(prev);
// if (w.length >= 2 && w[w.length-2].length() > 0) {
// char lastChar = w[w.length-2].charAt(w[w.length-2].length() -1);
// if (sentenceSeperators.contains(String.valueOf(lastChar))) {
// return null;
// }
// return w[w.length-2];
// } else {
// return null;
// }
// }
//
// public static class SelectedWord {
// public int start;
// public int end;
// public CharSequence word;
// }
//
// /**
// * Takes a character sequence with a single character and checks if the character occurs
// * in a list of word separators or is empty.
// * @param singleChar A CharSequence with null, zero or one character
// * @param wordSeparators A String containing the word separators
// * @return true if the character is at a word boundary, false otherwise
// */
// private static boolean isWordBoundary(CharSequence singleChar) {
// return TextUtils.isEmpty(singleChar) || isWhitespace(singleChar.charAt(0));
// }
//
// /**
// * Checks if the cursor is inside a word or the current selection is a whole word.
// * @param ic the InputConnection for accessing the text field
// * @param selStart the start position of the selection within the text field
// * @param selEnd the end position of the selection within the text field. This could be
// * the same as selStart, if there's no selection.
// * @param wordSeparators the word separator characters for the current language
// * @return an object containing the text and coordinates of the selected/touching word,
// * null if the selection/cursor is not marking a whole word.
// */
// public static SelectedWord getWordAtCursorOrSelection(final InputConnection ic, int selStart, int selEnd) {
// if (selStart == selEnd) {
// // There is just a cursor, so get the word at the cursor
// EditingUtil.Range range = new EditingUtil.Range();
// CharSequence touching = getWordAtCursor(ic, range);
// if (!TextUtils.isEmpty(touching)) {
// SelectedWord selWord = new SelectedWord();
// selWord.word = touching;
// selWord.start = selStart - range.charsBefore;
// selWord.end = selEnd + range.charsAfter;
// return selWord;
// }
// } else {
// // Is the previous character empty or a word separator? If not, return null.
// CharSequence charsBefore = ic.getTextBeforeCursor(1, 0);
// if (!isWordBoundary(charsBefore)) {
// return null;
// }
//
// // Is the next character empty or a word separator? If not, return null.
// CharSequence charsAfter = ic.getTextAfterCursor(1, 0);
// if (!isWordBoundary(charsAfter)) {
// return null;
// }
//
// // Extract the selection alone
// CharSequence touching = getSelectedText(ic, selStart, selEnd);
// if (TextUtils.isEmpty(touching)) return null;
// // Is any part of the selection a separator? If so, return null.
// final int length = touching.length();
// for (int i = 0; i < length; i++) {
// if (isWhitespace(touching.subSequence(i, i + 1).charAt(0))) {
// return null;
// }
// }
// // Prepare the selected word
// SelectedWord selWord = new SelectedWord();
// selWord.start = selStart;
// selWord.end = selEnd;
// selWord.word = touching;
// return selWord;
// }
// return null;
// }
//
//// /**
//// * Cache method pointers for performance
//// */
//// private static void initializeMethodsForReflection() {
//// try {
//// // These will either both exist or not, so no need for separate try/catch blocks.
//// // If other methods are added later, use separate try/catch blocks.
//// sMethodGetSelectedText = InputConnection.class.getMethod("getSelectedText", int.class);
//// sMethodSetComposingRegion = InputConnection.class.getMethod("setComposingRegion",
//// int.class, int.class);
//// } catch (NoSuchMethodException exc) {
//// // Ignore
//// }
//// sMethodsInitialized = true;
//// }
////
//// /**
//// * Returns the selected text between the selStart and selEnd positions.
//// */
//// private static CharSequence getSelectedText(InputConnection ic, int selStart, int selEnd) {
//// // Use reflection, for backward compatibility
//// CharSequence result = null;
//// if (!sMethodsInitialized) {
//// initializeMethodsForReflection();
//// }
//// if (sMethodGetSelectedText != null) {
//// try {
//// result = (CharSequence) sMethodGetSelectedText.invoke(ic, 0);
//// return result;
//// } catch (InvocationTargetException exc) {
//// // Ignore
//// } catch (IllegalArgumentException e) {
//// // Ignore
//// } catch (IllegalAccessException e) {
//// // Ignore
//// }
//// }
//// // Reflection didn't work, try it the poor way, by moving the cursor to the start,
//// // getting the text after the cursor and moving the text back to selected mode.
//// // TODO: Verify that this works properly in conjunction with
//// // LatinIME#onUpdateSelection
//// ic.setSelection(selStart, selEnd);
//// result = ic.getTextAfterCursor(selEnd - selStart, 0);
//// ic.setSelection(selStart, selEnd);
//// return result;
//// }
//
//// /**
//// * Tries to set the text into composition mode if there is support for it in the framework.
//// */
//// public static void underlineWord(InputConnection ic, SelectedWord word) {
//// // Use reflection, for backward compatibility
//// // If method not found, there's nothing we can do. It still works but just wont underline
//// // the word.
//// if (!sMethodsInitialized) {
//// initializeMethodsForReflection();
//// }
//// if (sMethodSetComposingRegion != null) {
//// try {
//// sMethodSetComposingRegion.invoke(ic, word.start, word.end);
//// } catch (InvocationTargetException exc) {
//// // Ignore
//// } catch (IllegalArgumentException e) {
//// // Ignore
//// } catch (IllegalAccessException e) {
//// // Ignore
//// }
//// }
//// }
//}