1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.keyboard.layout.expected;
18 
19 import com.android.inputmethod.keyboard.Key;
20 import com.android.inputmethod.keyboard.internal.MoreKeySpec;
21 import com.android.inputmethod.latin.common.Constants;
22 import com.android.inputmethod.latin.common.StringUtils;
23 
24 import java.util.Locale;
25 
26 /**
27  * This class represents an expected output of a key.
28  *
29  * There are two types of expected output, an integer code point and a string output text.
30  */
31 abstract class ExpectedKeyOutput {
newInstance(final int code)32     static ExpectedKeyOutput newInstance(final int code) {
33         return new Code(code);
34     }
35 
newInstance(final String outputText)36     static ExpectedKeyOutput newInstance(final String outputText) {
37         // If the <code>outputText</code> is one code point string, use {@link CodePoint} object.
38         if (StringUtils.codePointCount(outputText) == 1) {
39             return new Code(outputText.codePointAt(0));
40         }
41         return new Text(outputText);
42     }
43 
toUpperCase(final Locale locale)44     abstract ExpectedKeyOutput toUpperCase(final Locale locale);
preserveCase()45     abstract ExpectedKeyOutput preserveCase();
hasSameKeyOutput(final String text)46     abstract boolean hasSameKeyOutput(final String text);
hasSameKeyOutput(final Key key)47     abstract boolean hasSameKeyOutput(final Key key);
hasSameKeyOutput(final MoreKeySpec moreKeySpec)48     abstract boolean hasSameKeyOutput(final MoreKeySpec moreKeySpec);
hasSameKeyOutput(final ExpectedKeyOutput output)49     abstract boolean hasSameKeyOutput(final ExpectedKeyOutput output);
50 
51     /**
52      * This class represents an integer code point.
53      */
54     private static class Code extends ExpectedKeyOutput {
55         // UNICODE code point or a special negative value defined in {@link Constants}.
56         private final int mCode;
57 
Code(final int code)58         Code(final int code) { mCode = code; }
59 
60         @Override
toUpperCase(final Locale locale)61         ExpectedKeyOutput toUpperCase(final Locale locale) {
62             if (Constants.isLetterCode(mCode)) {
63                 final String codeString = StringUtils.newSingleCodePointString(mCode);
64                 // A letter may have an upper case counterpart that consists of multiple code
65                 // points, for instance the upper case of "ß" is "SS".
66                 return newInstance(StringUtils.toTitleCaseOfKeyLabel(codeString, locale));
67             }
68             // A special negative value has no upper case.
69             return this;
70         }
71 
72         @Override
preserveCase()73         ExpectedKeyOutput preserveCase() {
74             return new CasePreservedCode(mCode);
75         }
76 
77         @Override
hasSameKeyOutput(final String text)78         boolean hasSameKeyOutput(final String text) {
79             return StringUtils.codePointCount(text) == 1 && text.codePointAt(0) == mCode;
80         }
81 
82         @Override
hasSameKeyOutput(final Key key)83         boolean hasSameKeyOutput(final Key key) {
84             return mCode == key.getCode();
85         }
86 
87         @Override
hasSameKeyOutput(final MoreKeySpec moreKeySpec)88         boolean hasSameKeyOutput(final MoreKeySpec moreKeySpec) {
89             return mCode == moreKeySpec.mCode;
90         }
91 
92         @Override
hasSameKeyOutput(final ExpectedKeyOutput output)93         boolean hasSameKeyOutput(final ExpectedKeyOutput output) {
94             return (output instanceof Code) && mCode == ((Code)output).mCode;
95         }
96 
97         @Override
toString()98         public String toString() {
99             return Constants.isLetterCode(mCode) ? StringUtils.newSingleCodePointString(mCode)
100                     : Constants.printableCode(mCode);
101         }
102 
103         private static class CasePreservedCode extends Code {
CasePreservedCode(final int code)104             CasePreservedCode(final int code) { super(code); }
105 
106             @Override
toUpperCase(final Locale locale)107             ExpectedKeyOutput toUpperCase(final Locale locale) { return this; }
108 
109             @Override
preserveCase()110             ExpectedKeyOutput preserveCase() { return this; }
111         }
112     }
113 
114     /**
115      * This class represents a string output text.
116      */
117     private static class Text extends ExpectedKeyOutput {
118         private final String mText;
119 
Text(final String text)120         Text(final String text) { mText = text; }
121 
122         @Override
toUpperCase(final Locale locale)123         ExpectedKeyOutput toUpperCase(final Locale locale) {
124             return newInstance(mText.toUpperCase(locale));
125         }
126 
127         @Override
preserveCase()128         ExpectedKeyOutput preserveCase() {
129             return new CasePreservedText(mText);
130         }
131 
132         @Override
hasSameKeyOutput(final String text)133         boolean hasSameKeyOutput(final String text) {
134             return text.equals(text);
135         }
136 
137         @Override
hasSameKeyOutput(final Key key)138         boolean hasSameKeyOutput(final Key key) {
139             return key.getCode() == Constants.CODE_OUTPUT_TEXT
140                     && mText.equals(key.getOutputText());
141         }
142 
143         @Override
hasSameKeyOutput(final MoreKeySpec moreKeySpec)144         boolean hasSameKeyOutput(final MoreKeySpec moreKeySpec) {
145             return moreKeySpec.mCode == Constants.CODE_OUTPUT_TEXT
146                     && mText.equals(moreKeySpec.mOutputText);
147         }
148 
149         @Override
hasSameKeyOutput(final ExpectedKeyOutput output)150         boolean hasSameKeyOutput(final ExpectedKeyOutput output) {
151             return (output instanceof Text) && mText == ((Text)output).mText;
152         }
153 
154         @Override
toString()155         public String toString() {
156             return mText;
157         }
158 
159         private static class CasePreservedText extends Text {
CasePreservedText(final String text)160             CasePreservedText(final String text) { super(text); }
161 
162             @Override
toUpperCase(final Locale locale)163             ExpectedKeyOutput toUpperCase(final Locale locale) { return this; }
164 
165             @Override
preserveCase()166             ExpectedKeyOutput preserveCase() { return this; }
167         }
168     }
169 }
170