1 /*
2  * Copyright (C) 2006 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 android.text.method;
18 
19 import android.graphics.Rect;
20 import android.text.Editable;
21 import android.text.GetChars;
22 import android.text.Spannable;
23 import android.text.Spanned;
24 import android.text.SpannedString;
25 import android.text.TextUtils;
26 import android.view.View;
27 
28 /**
29  * This transformation method causes the characters in the {@link #getOriginal}
30  * array to be replaced by the corresponding characters in the
31  * {@link #getReplacement} array.
32  */
33 public abstract class ReplacementTransformationMethod
34 implements TransformationMethod
35 {
36     /**
37      * Returns the list of characters that are to be replaced by other
38      * characters when displayed.
39      */
getOriginal()40     protected abstract char[] getOriginal();
41     /**
42      * Returns a parallel array of replacement characters for the ones
43      * that are to be replaced.
44      */
getReplacement()45     protected abstract char[] getReplacement();
46 
47     /**
48      * Returns a CharSequence that will mirror the contents of the
49      * source CharSequence but with the characters in {@link #getOriginal}
50      * replaced by ones from {@link #getReplacement}.
51      */
getTransformation(CharSequence source, View v)52     public CharSequence getTransformation(CharSequence source, View v) {
53         char[] original = getOriginal();
54         char[] replacement = getReplacement();
55 
56         /*
57          * Short circuit for faster display if the text will never change.
58          */
59         if (!(source instanceof Editable)) {
60             /*
61              * Check whether the text does not contain any of the
62              * source characters so can be used unchanged.
63              */
64             boolean doNothing = true;
65             int n = original.length;
66             for (int i = 0; i < n; i++) {
67                 if (TextUtils.indexOf(source, original[i]) >= 0) {
68                     doNothing = false;
69                     break;
70                 }
71             }
72             if (doNothing) {
73                 return source;
74             }
75 
76             if (!(source instanceof Spannable)) {
77                 /*
78                  * The text contains some of the source characters,
79                  * but they can be flattened out now instead of
80                  * at display time.
81                  */
82                 if (source instanceof Spanned) {
83                     return new SpannedString(new SpannedReplacementCharSequence(
84                                                         (Spanned) source,
85                                                         original, replacement));
86                 } else {
87                     return new ReplacementCharSequence(source,
88                                                        original,
89                                                        replacement).toString();
90                 }
91             }
92         }
93 
94         if (source instanceof Spanned) {
95             return new SpannedReplacementCharSequence((Spanned) source,
96                                                       original, replacement);
97         } else {
98             return new ReplacementCharSequence(source, original, replacement);
99         }
100     }
101 
onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Rect previouslyFocusedRect)102     public void onFocusChanged(View view, CharSequence sourceText,
103                                boolean focused, int direction,
104                                Rect previouslyFocusedRect) {
105         // This callback isn't used.
106     }
107 
108     private static class ReplacementCharSequence
109     implements CharSequence, GetChars {
110         private char[] mOriginal, mReplacement;
111 
ReplacementCharSequence(CharSequence source, char[] original, char[] replacement)112         public ReplacementCharSequence(CharSequence source, char[] original,
113                                        char[] replacement) {
114             mSource = source;
115             mOriginal = original;
116             mReplacement = replacement;
117         }
118 
length()119         public int length() {
120             return mSource.length();
121         }
122 
charAt(int i)123         public char charAt(int i) {
124             char c = mSource.charAt(i);
125 
126             int n = mOriginal.length;
127             for (int j = 0; j < n; j++) {
128                 if (c == mOriginal[j]) {
129                     c = mReplacement[j];
130                 }
131             }
132 
133             return c;
134         }
135 
subSequence(int start, int end)136         public CharSequence subSequence(int start, int end) {
137             char[] c = new char[end - start];
138 
139             getChars(start, end, c, 0);
140             return new String(c);
141         }
142 
toString()143         public String toString() {
144             char[] c = new char[length()];
145 
146             getChars(0, length(), c, 0);
147             return new String(c);
148         }
149 
getChars(int start, int end, char[] dest, int off)150         public void getChars(int start, int end, char[] dest, int off) {
151             TextUtils.getChars(mSource, start, end, dest, off);
152             int offend = end - start + off;
153             int n = mOriginal.length;
154 
155             for (int i = off; i < offend; i++) {
156                 char c = dest[i];
157 
158                 for (int j = 0; j < n; j++) {
159                     if (c == mOriginal[j]) {
160                         dest[i] = mReplacement[j];
161                     }
162                 }
163             }
164         }
165 
166         private CharSequence mSource;
167     }
168 
169     private static class SpannedReplacementCharSequence
170     extends ReplacementCharSequence
171     implements Spanned
172     {
SpannedReplacementCharSequence(Spanned source, char[] original, char[] replacement)173         public SpannedReplacementCharSequence(Spanned source, char[] original,
174                                               char[] replacement) {
175             super(source, original, replacement);
176             mSpanned = source;
177         }
178 
subSequence(int start, int end)179         public CharSequence subSequence(int start, int end) {
180             return new SpannedString(this).subSequence(start, end);
181         }
182 
getSpans(int start, int end, Class<T> type)183         public <T> T[] getSpans(int start, int end, Class<T> type) {
184             return mSpanned.getSpans(start, end, type);
185         }
186 
getSpanStart(Object tag)187         public int getSpanStart(Object tag) {
188             return mSpanned.getSpanStart(tag);
189         }
190 
getSpanEnd(Object tag)191         public int getSpanEnd(Object tag) {
192             return mSpanned.getSpanEnd(tag);
193         }
194 
getSpanFlags(Object tag)195         public int getSpanFlags(Object tag) {
196             return mSpanned.getSpanFlags(tag);
197         }
198 
nextSpanTransition(int start, int end, Class type)199         public int nextSpanTransition(int start, int end, Class type) {
200             return mSpanned.nextSpanTransition(start, end, type);
201         }
202 
203         private Spanned mSpanned;
204     }
205 }
206