135ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka/* 235ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project 335ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka * 1035ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 1535ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka */ 1635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 1735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaokapackage com.android.inputmethod.keyboard.internal; 1835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 1964c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaokaimport android.text.TextUtils; 2064c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka 21f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key; 22240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants; 232dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaokaimport com.android.inputmethod.latin.define.DebugFlags; 24e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaokaimport com.android.inputmethod.latin.utils.CollectionUtils; 25e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils; 2635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 27e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaokaimport java.util.ArrayList; 28e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaokaimport java.util.Arrays; 2935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaokaimport java.util.Locale; 3035ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 31e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka/** 32e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * The more key specification object. The more keys are an array of {@link MoreKeySpec}. 33e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * 34e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * The more keys specification is comma separated "key specification" each of which represents one 35e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * "more key". 36e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * The key specification might have label or string resource reference in it. These references are 37e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * expanded before parsing comma. 38e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * Special character, comma ',' backslash '\' can be escaped by '\' character. 39e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * Note that the '\' is also parsed by XML parser and {@link MoreKeySpec#splitKeySpecs(String)} 40e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * as well. 41e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka */ 429684b33b69a62a058c767786ae6a23b809d27385Tadashi G. Takaoka// TODO: Should extend the key specification object. 4364c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaokapublic final class MoreKeySpec { 4435ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka public final int mCode; 4535ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka public final String mLabel; 4635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka public final String mOutputText; 4735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka public final int mIconId; 4835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 497ae6721ffad1e79ee446de87d13f18a27619830bTadashi G. Takaoka public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, final Locale locale) { 50d9c6b332090c90e4d4840e62fe3eb45c834b2e14Tadashi G. Takaoka if (TextUtils.isEmpty(moreKeySpec)) { 51d9c6b332090c90e4d4840e62fe3eb45c834b2e14Tadashi G. Takaoka throw new KeySpecParser.KeySpecParserError("Empty more key spec"); 52d9c6b332090c90e4d4840e62fe3eb45c834b2e14Tadashi G. Takaoka } 53639bf62e4fc12c4f75ed0491512f6ed9d2fc2432Tadashi G. Takaoka mLabel = StringUtils.toUpperCaseOfStringForLocale( 5435ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale); 55639bf62e4fc12c4f75ed0491512f6ed9d2fc2432Tadashi G. Takaoka final int code = StringUtils.toUpperCaseOfCodeForLocale( 567ae6721ffad1e79ee446de87d13f18a27619830bTadashi G. Takaoka KeySpecParser.getCode(moreKeySpec), needsToUpperCase, locale); 57240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka if (code == Constants.CODE_UNSPECIFIED) { 5835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters 5935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka // upper case representation ("SS"). 60240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka mCode = Constants.CODE_OUTPUT_TEXT; 6135ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka mOutputText = mLabel; 6235ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } else { 6335ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka mCode = code; 64639bf62e4fc12c4f75ed0491512f6ed9d2fc2432Tadashi G. Takaoka mOutputText = StringUtils.toUpperCaseOfStringForLocale( 6535ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka KeySpecParser.getOutputText(moreKeySpec), needsToUpperCase, locale); 6635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } 6735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka mIconId = KeySpecParser.getIconId(moreKeySpec); 6835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } 6935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka 70f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka public Key buildKey(final int x, final int y, final int labelFlags, 71f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka final KeyboardParams params) { 72f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka return new Key(mLabel, mIconId, mCode, mOutputText, null /* hintLabel */, labelFlags, 73f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka Key.BACKGROUND_TYPE_NORMAL, x, y, params.mDefaultKeyWidth, params.mDefaultRowHeight, 74f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka params.mHorizontalGap, params.mVerticalGap); 75f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka } 76f70bcf3d323b13b60c0567c69768ed986647f86aTadashi G. Takaoka 7735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka @Override 7864c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka public int hashCode() { 7964c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka int hashCode = 1; 8064c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka hashCode = 31 + mCode; 8164c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka hashCode = hashCode * 31 + mIconId; 8264c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka hashCode = hashCode * 31 + (mLabel == null ? 0 : mLabel.hashCode()); 8364c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka hashCode = hashCode * 31 + (mOutputText == null ? 0 : mOutputText.hashCode()); 8464c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka return hashCode; 8564c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka } 8664c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka 8764c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka @Override 8864c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka public boolean equals(final Object o) { 8964c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka if (this == o) return true; 9064c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka if (o instanceof MoreKeySpec) { 9164c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka final MoreKeySpec other = (MoreKeySpec)o; 9264c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka return mCode == other.mCode 9364c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka && mIconId == other.mIconId 9464c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka && TextUtils.equals(mLabel, other.mLabel) 9564c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka && TextUtils.equals(mOutputText, other.mOutputText); 9664c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka } 9764c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka return false; 9864c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka } 9964c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka 10064c65ce6d780175fe606fdd7ee694a3b5af4e37fTadashi G. Takaoka @Override 10135ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka public String toString() { 10235ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel 1033b4eb03fa171ab42ad4f38abcfa5184c5362e5aeTadashi G. Takaoka : KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId)); 104240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka final String output = (mCode == Constants.CODE_OUTPUT_TEXT ? mOutputText 105240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka : Constants.printableCode(mCode)); 10635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) { 10735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka return output; 10835ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } else { 10935ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka return label + "|" + output; 11035ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } 11135ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka } 112e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 1132dae79b1966a7970c25c8b79beec1c95c13f6c87Tadashi G. Takaoka private static final boolean DEBUG = DebugFlags.DEBUG_ENABLED; 114e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Constants for parsing. 115e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka private static final char COMMA = Constants.CODE_COMMA; 116e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka private static final char BACKSLASH = Constants.CODE_BACKSLASH; 117e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka private static final String ADDITIONAL_MORE_KEY_MARKER = 118e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka StringUtils.newSingleCodePointString(Constants.CODE_PERCENT); 119e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 120e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka /** 121e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * Split the text containing multiple key specifications separated by commas into an array of 122e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * key specifications. 123e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * A key specification can contain a character escaped by the backslash character, including a 124e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * comma character. 125e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * Note that an empty key specification will be eliminated from the result array. 126e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * 127e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * @param text the text containing multiple key specifications. 128e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * @return an array of key specification text. Null if the specified <code>text</code> is empty 129e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka * or has no key specifications. 130e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka */ 131e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka public static String[] splitKeySpecs(final String text) { 132e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (TextUtils.isEmpty(text)) { 133e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return null; 134e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 135e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final int size = text.length(); 136e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Optimization for one-letter key specification. 137e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (size == 1) { 138e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return text.charAt(0) == COMMA ? null : new String[] { text }; 139e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 140e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 141e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka ArrayList<String> list = null; 142e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka int start = 0; 143e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // The characters in question in this loop are COMMA and BACKSLASH. These characters never 144e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // match any high or low surrogate character. So it is OK to iterate through with char 145e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // index. 146e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int pos = 0; pos < size; pos++) { 147e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final char c = text.charAt(pos); 148e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (c == COMMA) { 149e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Skip empty entry. 150e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (pos - start > 0) { 151e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (list == null) { 152a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka list = new ArrayList<>(); 153e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 154e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka list.add(text.substring(start, pos)); 155e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 156e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Skip comma 157e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka start = pos + 1; 158e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else if (c == BACKSLASH) { 159e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Skip escape character and escaped character. 160e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka pos++; 161e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 162e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 163e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String remain = (size - start > 0) ? text.substring(start) : null; 164e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (list == null) { 165e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return remain != null ? new String[] { remain } : null; 166e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 167e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (remain != null) { 168e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka list.add(remain); 169e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 170e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return list.toArray(new String[list.size()]); 171e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 172e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 173e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka private static final String[] EMPTY_STRING_ARRAY = new String[0]; 174e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 175e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka private static String[] filterOutEmptyString(final String[] array) { 176e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (array == null) { 177e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return EMPTY_STRING_ARRAY; 178e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 179e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka ArrayList<String> out = null; 180e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int i = 0; i < array.length; i++) { 181e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String entry = array[i]; 182e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (TextUtils.isEmpty(entry)) { 183e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out == null) { 184e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out = CollectionUtils.arrayAsList(array, 0, i); 185e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 186e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else if (out != null) { 187e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out.add(entry); 188e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 189e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 190e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out == null) { 191e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return array; 192e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 193e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return out.toArray(new String[out.size()]); 194e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 195e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 196e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka public static String[] insertAdditionalMoreKeys(final String[] moreKeySpecs, 197e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String[] additionalMoreKeySpecs) { 198e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String[] moreKeys = filterOutEmptyString(moreKeySpecs); 199e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs); 200e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final int moreKeysCount = moreKeys.length; 201e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final int additionalCount = additionalMoreKeys.length; 202e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka ArrayList<String> out = null; 203e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka int additionalIndex = 0; 204e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int moreKeyIndex = 0; moreKeyIndex < moreKeysCount; moreKeyIndex++) { 205e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String moreKeySpec = moreKeys[moreKeyIndex]; 206e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (moreKeySpec.equals(ADDITIONAL_MORE_KEY_MARKER)) { 207e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (additionalIndex < additionalCount) { 208e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Replace '%' marker with additional more key specification. 209e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String additionalMoreKey = additionalMoreKeys[additionalIndex]; 210e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out != null) { 211e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out.add(additionalMoreKey); 212e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else { 213e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka moreKeys[moreKeyIndex] = additionalMoreKey; 214e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 215e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka additionalIndex++; 216e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else { 217e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Filter out excessive '%' marker. 218e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out == null) { 219e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeyIndex); 220e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 221e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 222e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else { 223e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out != null) { 224e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out.add(moreKeySpec); 225e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 226e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 227e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 228e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (additionalCount > 0 && additionalIndex == 0) { 229e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // No '%' marker is found in more keys. 230e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Insert all additional more keys to the head of more keys. 231e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (DEBUG && out != null) { 232e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka throw new RuntimeException("Internal logic error:" 233e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka + " moreKeys=" + Arrays.toString(moreKeys) 234e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys)); 235e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 236e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out = CollectionUtils.arrayAsList(additionalMoreKeys, additionalIndex, additionalCount); 237e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int i = 0; i < moreKeysCount; i++) { 238e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out.add(moreKeys[i]); 239e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 240e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else if (additionalIndex < additionalCount) { 241e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // The number of '%' markers are less than additional more keys. 242e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka // Append remained additional more keys to the tail of more keys. 243e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (DEBUG && out != null) { 244e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka throw new RuntimeException("Internal logic error:" 245e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka + " moreKeys=" + Arrays.toString(moreKeys) 246e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys)); 247e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 248e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeysCount); 249e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int i = additionalIndex; i < additionalCount; i++) { 250e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka out.add(additionalMoreKeys[additionalIndex]); 251e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 252e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 253e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (out == null && moreKeysCount > 0) { 254e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return moreKeys; 255e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else if (out != null && out.size() > 0) { 256e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return out.toArray(new String[out.size()]); 257e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } else { 258e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return null; 259e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 260e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 261e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 262e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka public static int getIntValue(final String[] moreKeys, final String key, 263e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final int defaultValue) { 264e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (moreKeys == null) { 265e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return defaultValue; 266e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 267e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final int keyLen = key.length(); 268e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka boolean foundValue = false; 269e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka int value = defaultValue; 270e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int i = 0; i < moreKeys.length; i++) { 271e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String moreKeySpec = moreKeys[i]; 272e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (moreKeySpec == null || !moreKeySpec.startsWith(key)) { 273e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka continue; 274e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 275e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka moreKeys[i] = null; 276e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka try { 277e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (!foundValue) { 278e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka value = Integer.parseInt(moreKeySpec.substring(keyLen)); 279e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka foundValue = true; 280e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 281e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } catch (NumberFormatException e) { 282e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka throw new RuntimeException( 283e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka "integer should follow after " + key + ": " + moreKeySpec); 284e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 285e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 286e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return value; 287e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 288e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka 289e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka public static boolean getBooleanValue(final String[] moreKeys, final String key) { 290e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (moreKeys == null) { 291e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return false; 292e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 293e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka boolean value = false; 294e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka for (int i = 0; i < moreKeys.length; i++) { 295e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka final String moreKeySpec = moreKeys[i]; 296e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka if (moreKeySpec == null || !moreKeySpec.equals(key)) { 297e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka continue; 298e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 299e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka moreKeys[i] = null; 300e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka value = true; 301e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 302e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka return value; 303e855093f5513e46f7f2da6d99e74873ac4f1eeefTadashi G. Takaoka } 30435ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka} 305