blob: 53854242877abe480d54352cf56b541908e12225 [file] [log] [blame]
Hans Boehm84614952014-11-25 18:46:17 -08001/*
2 * Copyright (C) 2015 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
17package com.android.calculator2;
18
19import android.content.res.Resources;
20import android.content.Context;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070021import android.app.Activity;
22import android.util.Log;
Hans Boehm84614952014-11-25 18:46:17 -080023import android.view.View;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070024import android.widget.Button;
25
Hans Boehm84614952014-11-25 18:46:17 -080026import java.text.DecimalFormatSymbols;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -070027import java.util.HashMap;
28import java.util.Locale;
Hans Boehm84614952014-11-25 18:46:17 -080029
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070030/**
31 * Collection of mapping functions between key ids, characters, internationalized
Justin Klaassene2711cb2015-05-28 11:13:17 -070032 * and non-internationalized characters, etc.
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070033 * <p>
34 * KeyMap instances are not meaningful; everything here is static.
35 * All functions are either pure, or are assumed to be called only from a single UI thread.
36 */
Hans Boehm84614952014-11-25 18:46:17 -080037public class KeyMaps {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070038 /**
39 * Map key id to corresponding (internationalized) display string.
40 * Pure function.
41 */
Justin Klaassene2711cb2015-05-28 11:13:17 -070042 public static String toString(Context context, int id) {
Hans Boehm84614952014-11-25 18:46:17 -080043 switch(id) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070044 case R.id.const_pi:
Justin Klaassene2711cb2015-05-28 11:13:17 -070045 return context.getString(R.string.const_pi);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070046 case R.id.const_e:
Justin Klaassene2711cb2015-05-28 11:13:17 -070047 return context.getString(R.string.const_e);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070048 case R.id.op_sqrt:
Justin Klaassene2711cb2015-05-28 11:13:17 -070049 return context.getString(R.string.op_sqrt);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070050 case R.id.op_fact:
Justin Klaassene2711cb2015-05-28 11:13:17 -070051 return context.getString(R.string.op_fact);
52 case R.id.op_pct:
53 return context.getString(R.string.op_pct);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070054 case R.id.fun_sin:
Justin Klaassene2711cb2015-05-28 11:13:17 -070055 return context.getString(R.string.fun_sin) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070056 case R.id.fun_cos:
Justin Klaassene2711cb2015-05-28 11:13:17 -070057 return context.getString(R.string.fun_cos) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070058 case R.id.fun_tan:
Justin Klaassene2711cb2015-05-28 11:13:17 -070059 return context.getString(R.string.fun_tan) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070060 case R.id.fun_arcsin:
Justin Klaassene2711cb2015-05-28 11:13:17 -070061 return context.getString(R.string.fun_arcsin) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070062 case R.id.fun_arccos:
Justin Klaassene2711cb2015-05-28 11:13:17 -070063 return context.getString(R.string.fun_arccos) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070064 case R.id.fun_arctan:
Justin Klaassene2711cb2015-05-28 11:13:17 -070065 return context.getString(R.string.fun_arctan) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070066 case R.id.fun_ln:
Justin Klaassene2711cb2015-05-28 11:13:17 -070067 return context.getString(R.string.fun_ln) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070068 case R.id.fun_log:
Justin Klaassene2711cb2015-05-28 11:13:17 -070069 return context.getString(R.string.fun_log) + context.getString(R.string.lparen);
Hans Boehm4db31b42015-05-31 12:19:05 -070070 case R.id.fun_exp:
71 // Button label doesn't work.
72 return context.getString(R.string.exponential) + context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070073 case R.id.lparen:
Justin Klaassene2711cb2015-05-28 11:13:17 -070074 return context.getString(R.string.lparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070075 case R.id.rparen:
Justin Klaassene2711cb2015-05-28 11:13:17 -070076 return context.getString(R.string.rparen);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070077 case R.id.op_pow:
Justin Klaassene2711cb2015-05-28 11:13:17 -070078 return context.getString(R.string.op_pow);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070079 case R.id.op_mul:
Justin Klaassene2711cb2015-05-28 11:13:17 -070080 return context.getString(R.string.op_mul);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070081 case R.id.op_div:
Justin Klaassene2711cb2015-05-28 11:13:17 -070082 return context.getString(R.string.op_div);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070083 case R.id.op_add:
Justin Klaassene2711cb2015-05-28 11:13:17 -070084 return context.getString(R.string.op_add);
Hans Boehm4db31b42015-05-31 12:19:05 -070085 case R.id.op_sqr:
86 // Button label doesn't work.
87 return context.getString(R.string.squared);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070088 case R.id.op_sub:
Justin Klaassene2711cb2015-05-28 11:13:17 -070089 return context.getString(R.string.op_sub);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070090 case R.id.dec_point:
Justin Klaassene2711cb2015-05-28 11:13:17 -070091 return context.getString(R.string.dec_point);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070092 case R.id.digit_0:
Justin Klaassene2711cb2015-05-28 11:13:17 -070093 return context.getString(R.string.digit_0);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070094 case R.id.digit_1:
Justin Klaassene2711cb2015-05-28 11:13:17 -070095 return context.getString(R.string.digit_1);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070096 case R.id.digit_2:
Justin Klaassene2711cb2015-05-28 11:13:17 -070097 return context.getString(R.string.digit_2);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -070098 case R.id.digit_3:
Justin Klaassene2711cb2015-05-28 11:13:17 -070099 return context.getString(R.string.digit_3);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700100 case R.id.digit_4:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700101 return context.getString(R.string.digit_4);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700102 case R.id.digit_5:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700103 return context.getString(R.string.digit_5);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700104 case R.id.digit_6:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700105 return context.getString(R.string.digit_6);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700106 case R.id.digit_7:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700107 return context.getString(R.string.digit_7);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700108 case R.id.digit_8:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700109 return context.getString(R.string.digit_8);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700110 case R.id.digit_9:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700111 return context.getString(R.string.digit_9);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700112 default:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700113 return "";
Hans Boehm84614952014-11-25 18:46:17 -0800114 }
115 }
116
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700117 /**
118 * Does a button id correspond to a binary operator?
119 * Pure function.
120 */
Hans Boehm84614952014-11-25 18:46:17 -0800121 public static boolean isBinary(int id) {
122 switch(id) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700123 case R.id.op_pow:
124 case R.id.op_mul:
125 case R.id.op_div:
126 case R.id.op_add:
127 case R.id.op_sub:
128 return true;
129 default:
130 return false;
Hans Boehm84614952014-11-25 18:46:17 -0800131 }
132 }
133
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700134 /**
135 * Does a button id correspond to a suffix operator?
136 */
Hans Boehm84614952014-11-25 18:46:17 -0800137 public static boolean isSuffix(int id) {
Justin Klaassene2711cb2015-05-28 11:13:17 -0700138 switch (id) {
139 case R.id.op_fact:
140 case R.id.op_pct:
Hans Boehm4db31b42015-05-31 12:19:05 -0700141 case R.id.op_sqr:
Justin Klaassene2711cb2015-05-28 11:13:17 -0700142 return true;
143 default:
144 return false;
145 }
Hans Boehm84614952014-11-25 18:46:17 -0800146 }
147
148 public static final int NOT_DIGIT = 10;
149
Hans Boehm08e8f322015-04-21 13:18:38 -0700150 public static final String ELLIPSIS = "\u2026";
151
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700152 /**
153 * Map key id to digit or NOT_DIGIT
154 * Pure function.
155 */
Hans Boehm84614952014-11-25 18:46:17 -0800156 public static int digVal(int id) {
157 switch (id) {
158 case R.id.digit_0:
159 return 0;
160 case R.id.digit_1:
161 return 1;
162 case R.id.digit_2:
163 return 2;
164 case R.id.digit_3:
165 return 3;
166 case R.id.digit_4:
167 return 4;
168 case R.id.digit_5:
169 return 5;
170 case R.id.digit_6:
171 return 6;
172 case R.id.digit_7:
173 return 7;
174 case R.id.digit_8:
175 return 8;
176 case R.id.digit_9:
177 return 9;
178 default:
179 return NOT_DIGIT;
180 }
181 }
182
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700183 /**
184 * Map digit to corresponding key. Inverse of above.
185 * Pure function.
186 */
Hans Boehm84614952014-11-25 18:46:17 -0800187 public static int keyForDigVal(int v) {
188 switch(v) {
189 case 0:
190 return R.id.digit_0;
191 case 1:
192 return R.id.digit_1;
193 case 2:
194 return R.id.digit_2;
195 case 3:
196 return R.id.digit_3;
197 case 4:
198 return R.id.digit_4;
199 case 5:
200 return R.id.digit_5;
201 case 6:
202 return R.id.digit_6;
203 case 7:
204 return R.id.digit_7;
205 case 8:
206 return R.id.digit_8;
207 case 9:
208 return R.id.digit_9;
209 default:
210 return View.NO_ID;
211 }
212 }
213
Hans Boehm425ed0a2015-05-19 18:27:31 -0700214 // The following two are only used for recognizing additional
215 // input characters from a physical keyboard. They are not used
216 // for output internationalization.
217 private static char mDecimalPt;
Hans Boehm84614952014-11-25 18:46:17 -0800218
Hans Boehmffda5282015-05-18 15:00:12 -0700219 private static char mPiChar;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700220
Hans Boehmffda5282015-05-18 15:00:12 -0700221 /**
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700222 * Character used as a placeholder for digits that are currently unknown in a result that
223 * is being computed. We initially generate blanks, and then use this as a replacement
224 * during final translation.
Hans Boehmffda5282015-05-18 15:00:12 -0700225 * <p/>
226 * Note: the character must correspond closely to the width of a digit,
227 * otherwise the UI will visibly shift once the computation is finished.
228 */
229 private static final char CHAR_DIGIT_UNKNOWN = '\u2007';
230
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700231 /**
232 * Map typed function name strings to corresponding button ids.
233 * We (now redundantly?) include both localized and English names.
234 */
Hans Boehmffda5282015-05-18 15:00:12 -0700235 private static HashMap<String, Integer> sKeyValForFun;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700236
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700237 /**
238 * Result string corresponding to a character in the calculator result.
239 * The string values in the map are expected to be one character long.
240 */
Hans Boehmffda5282015-05-18 15:00:12 -0700241 private static HashMap<Character, String> sOutputForResultChar;
Hans Boehm013969e2015-04-13 20:29:47 -0700242
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700243 /**
244 * Locale string corresponding to preceding map and character constants.
245 * We recompute the map if this is not the current locale.
246 */
Hans Boehmffda5282015-05-18 15:00:12 -0700247 private static String sLocaleForMaps = "none";
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700248
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700249 /**
250 * Activity to use for looking up buttons.
251 */
252 private static Activity mActivity;
Hans Boehm013969e2015-04-13 20:29:47 -0700253
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700254 /**
255 * Set acttivity used for looking up button labels.
256 * Call only from UI thread.
257 */
Hans Boehm013969e2015-04-13 20:29:47 -0700258 public static void setActivity(Activity a) {
259 mActivity = a;
260 }
261
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700262 /**
263 * Return the button id corresponding to the supplied character or return NO_ID.
264 * Called only by UI thread.
265 */
Hans Boehm013969e2015-04-13 20:29:47 -0700266 public static int keyForChar(char c) {
267 validateMaps();
Hans Boehm84614952014-11-25 18:46:17 -0800268 if (Character.isDigit(c)) {
269 int i = Character.digit(c, 10);
270 return KeyMaps.keyForDigVal(i);
271 }
Hans Boehm84614952014-11-25 18:46:17 -0800272 switch (c) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700273 case '.':
274 case ',':
275 return R.id.dec_point;
276 case '-':
277 return R.id.op_sub;
278 case '+':
279 return R.id.op_add;
280 case '*':
281 return R.id.op_mul;
282 case '/':
283 return R.id.op_div;
284 // We no longer localize function names, so they can't start with an 'e' or 'p'.
285 case 'e':
286 case 'E':
287 return R.id.const_e;
288 case 'p':
289 case 'P':
290 return R.id.const_pi;
291 case '^':
292 return R.id.op_pow;
293 case '!':
294 return R.id.op_fact;
Justin Klaassene2711cb2015-05-28 11:13:17 -0700295 case '%':
296 return R.id.op_pct;
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700297 case '(':
298 return R.id.lparen;
299 case ')':
300 return R.id.rparen;
301 default:
302 if (c == mDecimalPt) return R.id.dec_point;
303 if (c == mPiChar) return R.id.const_pi;
304 // pi is not translated, but it might be typable on a Greek keyboard,
305 // so we check ...
306 return View.NO_ID;
Hans Boehm84614952014-11-25 18:46:17 -0800307 }
308 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700309
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700310 /**
311 * Add information corresponding to the given button id to sKeyValForFun, to be used
312 * when mapping keyboard input to button ids.
313 */
Hans Boehm013969e2015-04-13 20:29:47 -0700314 static void addButtonToFunMap(int button_id) {
315 Button button = (Button)mActivity.findViewById(button_id);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700316 sKeyValForFun.put(button.getText().toString(), button_id);
317 }
318
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700319 /**
320 * Add information corresponding to the given button to sOutputForResultChar, to be used
321 * when translating numbers on output.
322 */
Hans Boehm013969e2015-04-13 20:29:47 -0700323 static void addButtonToOutputMap(char c, int button_id) {
324 Button button = (Button)mActivity.findViewById(button_id);
325 sOutputForResultChar.put(c, button.getText().toString());
326 }
327
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700328 // Ensure that the preceding map and character constants are
329 // initialized and correspond to the current locale.
330 // Called only by a single thread, namely the UI thread.
Hans Boehm013969e2015-04-13 20:29:47 -0700331 static void validateMaps() {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700332 Locale locale = Locale.getDefault();
333 String lname = locale.toString();
Hans Boehm013969e2015-04-13 20:29:47 -0700334 if (lname != sLocaleForMaps) {
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700335 Log.v ("Calculator", "Setting local to: " + lname);
336 sKeyValForFun = new HashMap<String, Integer>();
337 sKeyValForFun.put("sin", R.id.fun_sin);
338 sKeyValForFun.put("cos", R.id.fun_cos);
339 sKeyValForFun.put("tan", R.id.fun_tan);
340 sKeyValForFun.put("arcsin", R.id.fun_arcsin);
341 sKeyValForFun.put("arccos", R.id.fun_arccos);
342 sKeyValForFun.put("arctan", R.id.fun_arctan);
343 sKeyValForFun.put("asin", R.id.fun_arcsin);
344 sKeyValForFun.put("acos", R.id.fun_arccos);
345 sKeyValForFun.put("atan", R.id.fun_arctan);
346 sKeyValForFun.put("ln", R.id.fun_ln);
347 sKeyValForFun.put("log", R.id.fun_log);
348 sKeyValForFun.put("sqrt", R.id.op_sqrt); // special treatment
Hans Boehm013969e2015-04-13 20:29:47 -0700349 addButtonToFunMap(R.id.fun_sin);
350 addButtonToFunMap(R.id.fun_cos);
351 addButtonToFunMap(R.id.fun_tan);
352 addButtonToFunMap(R.id.fun_arcsin);
353 addButtonToFunMap(R.id.fun_arccos);
354 addButtonToFunMap(R.id.fun_arctan);
355 addButtonToFunMap(R.id.fun_ln);
356 addButtonToFunMap(R.id.fun_log);
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700357
358 // Set locale-dependent character "constants"
Hans Boehm013969e2015-04-13 20:29:47 -0700359 mDecimalPt =
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700360 DecimalFormatSymbols.getInstance().getDecimalSeparator();
Hans Boehm425ed0a2015-05-19 18:27:31 -0700361 // We recognize this in keyboard input, even if we use
362 // a different character.
Hans Boehm013969e2015-04-13 20:29:47 -0700363 Resources res = mActivity.getResources();
Hans Boehm425ed0a2015-05-19 18:27:31 -0700364 mPiChar = 0;
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700365 String piString = res.getString(R.string.const_pi);
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700366 if (piString.length() == 1) {
367 mPiChar = piString.charAt(0);
368 }
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700369
Hans Boehm013969e2015-04-13 20:29:47 -0700370 sOutputForResultChar = new HashMap<Character, String>();
371 sOutputForResultChar.put('e', "E");
372 sOutputForResultChar.put('E', "E");
Hans Boehmffda5282015-05-18 15:00:12 -0700373 sOutputForResultChar.put(' ', String.valueOf(CHAR_DIGIT_UNKNOWN));
Hans Boehm08e8f322015-04-21 13:18:38 -0700374 sOutputForResultChar.put(ELLIPSIS.charAt(0), ELLIPSIS);
Hans Boehm013969e2015-04-13 20:29:47 -0700375 sOutputForResultChar.put('/', "/");
376 // Translate numbers for fraction display, but not
377 // the separating slash, which appears to be
378 // universal.
379 addButtonToOutputMap('-', R.id.op_sub);
Hans Boehm425ed0a2015-05-19 18:27:31 -0700380 addButtonToOutputMap('.', R.id.dec_point);
Hans Boehm013969e2015-04-13 20:29:47 -0700381 for (int i = 0; i <= 9; ++i) {
382 addButtonToOutputMap((char)('0' + i), keyForDigVal(i));
383 }
384
385 sLocaleForMaps = lname;
386
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700387 }
388 }
389
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700390 /**
391 * Return function button id for the substring of s starting at pos and ending with
392 * the next "(". Return NO_ID if there is none.
393 * We currently check for both (possibly localized) button labels, and standard
394 * English names. (They should currently be the same, and hence this is currently redundant.)
395 * Callable only from UI thread.
396 */
Hans Boehm013969e2015-04-13 20:29:47 -0700397 public static int funForString(String s, int pos) {
398 validateMaps();
Hans Boehm4a6b7cb2015-04-03 18:41:52 -0700399 int parenPos = s.indexOf('(', pos);
400 if (parenPos != -1) {
401 String funString = s.substring(pos, parenPos);
402 Integer keyValue = sKeyValForFun.get(funString);
403 if (keyValue == null) return View.NO_ID;
404 return keyValue;
405 }
406 return View.NO_ID;
407 }
Hans Boehm013969e2015-04-13 20:29:47 -0700408
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700409 /**
410 * Return the localization of the string s representing a numeric answer.
411 * Callable only from UI thread.
412 */
Hans Boehm013969e2015-04-13 20:29:47 -0700413 public static String translateResult(String s) {
414 StringBuilder result = new StringBuilder();
415 int len = s.length();
416 validateMaps();
417 for (int i = 0; i < len; ++i) {
418 char c = s.charAt(i);
419 String translation = sOutputForResultChar.get(c);
420 if (translation == null) {
Hans Boehm0b3a9fd2015-05-22 11:36:26 -0700421 // Should not get here. Report if we do.
Hans Boehm013969e2015-04-13 20:29:47 -0700422 Log.v("Calculator", "Bad character:" + c);
423 result.append(String.valueOf(c));
424 } else {
425 result.append(translation);
426 }
427 }
428 return result.toString();
429 }
430
Hans Boehm84614952014-11-25 18:46:17 -0800431}