1ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka/*
2ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * Copyright (C) 2014 The Android Open Source Project
3ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka *
4ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
5ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * you may not use this file except in compliance with the License.
6ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * You may obtain a copy of the License at
7ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka *
8ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka *
10ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
11ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
12ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * See the License for the specific language governing permissions and
14ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * limitations under the License.
15ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka */
16ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
17ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokapackage com.android.inputmethod.keyboard.layout.expected;
18ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
19ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.keyboard.Key;
20ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
21ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.MoreKeySpec;
22ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
23ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.StringUtils;
24ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
25ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport java.util.ArrayList;
265326dcfb7dbdc1a3fc9cfb94046805f18bf3d3d7Tadashi G. Takaokaimport java.util.List;
27ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
28ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka/**
29ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * This class builds an actual keyboard for unit test.
30e6f467c0fe694d93b7f000fcca509587014fb7e8Tadashi G. Takaoka *
31e6f467c0fe694d93b7f000fcca509587014fb7e8Tadashi G. Takaoka * An actual keyboard is an array of rows, and a row consists of an array of {@link Key}s.
32e6f467c0fe694d93b7f000fcca509587014fb7e8Tadashi G. Takaoka * Each row may have different number of {@link Key}s.
33ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka */
34ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokapublic final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
35c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka    private static ArrayList<Key> filterOutSpacer(final List<Key> keys) {
36a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        final ArrayList<Key> filteredKeys = new ArrayList<>();
378c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        for (final Key key : keys) {
388c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            if (key.isSpacer()) {
398c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka                continue;
408c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            }
418c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            filteredKeys.add(key);
428c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        }
438c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        return filteredKeys;
448c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka    }
458c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka
46ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
47ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Create the keyboard that consists of the array of rows of the actual keyboard's keys.
48e6f467c0fe694d93b7f000fcca509587014fb7e8Tadashi G. Takaoka     * @param sortedKeys keys list of the actual keyboard that is sorted from top-left to
49e6f467c0fe694d93b7f000fcca509587014fb7e8Tadashi G. Takaoka     * bottom-right.
50ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the actual keyboard grouped with rows.
51ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
52c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka    public static Key[][] buildKeyboard(final List<Key> sortedKeys) {
53c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        // Filter out spacer to prepare to create rows.
54c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        final ArrayList<Key> filteredSortedKeys = filterOutSpacer(sortedKeys);
55ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
56ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        // Grouping keys into rows.
57a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        final ArrayList<ArrayList<Key>> rows = new ArrayList<>();
58a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        ArrayList<Key> elements = new ArrayList<>();
59c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        int lastY = filteredSortedKeys.get(0).getY();
60c13c1adfa72227b0006add5f13f555fbb9c9eb4eTadashi G. Takaoka        for (final Key key : filteredSortedKeys) {
61ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lastY != key.getY()) {
62ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                // A new row is starting.
63ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                lastY = key.getY();
64ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                rows.add(elements);
65a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka                elements = new ArrayList<>();
66ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
67ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            elements.add(key);
68ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
69ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        rows.add(elements); // Add the last row.
70ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
71ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        // Calculate each dimension of rows and create a builder.
72ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        final int[] dimensions = new int[rows.size()];
73ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
74ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            dimensions[rowIndex] = rows.get(rowIndex).size();
75ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
76cd15cfdaaba7f361f4744bd3ff51ce6cdae1e608Tadashi G. Takaoka        final ActualKeyboardBuilder builder = new ActualKeyboardBuilder();
77ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
78ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
79ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final int row = rowIndex + 1;
80ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final ArrayList<Key> rowKeys = rows.get(rowIndex);
81ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()]));
82ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
83ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return builder.build();
84ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
85ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
86ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
87ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key defaultElement() { return null; }
88ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
89ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
90ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key[] newArray(final int size) { return new Key[size]; }
91ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
92ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
93ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key[][] newArrayOfArray(final int size) { return new Key[size][]; }
94ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
95ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the key specification.
96ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> {
97ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer();
98ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
99ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
100ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final MoreKeySpec spec) {
101ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
102ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
103ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
104ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static String toString(final String label, final int iconId, final String outputText,
105ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                final int code) {
106ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
107ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                    ? KeyboardIconsSet.getIconName(iconId) : label;
108ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final String output;
109ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
110ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = outputText;
111ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            } else if (code < Constants.CODE_SPACE) {
112ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = Constants.printableCode(code);
113ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            } else {
114ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = StringUtils.newSingleCodePointString(code);
115ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
116ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (visual.equals(output)) {
117ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return visual;
118ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
119ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return visual + "|" + output;
120ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
121ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
122ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
123ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the key.
124ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class KeyStringizer extends StringUtils.Stringizer<Key> {
125ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final KeyStringizer STRINGIZER = new KeyStringizer();
126ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
127ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
128ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final Key key) {
129ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (key == null) {
130ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return "NULL";
131ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
132ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (key.isSpacer()) {
133ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return "SPACER";
134ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
135ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final StringBuilder sb = new StringBuilder();
136ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append(MoreKeySpecStringizer.toString(
137ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                    key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode()));
138ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final MoreKeySpec[] moreKeys = key.getMoreKeys();
139ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (moreKeys == null) {
140ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return sb.toString();
141ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
142ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append("^");
143ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys));
144ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return sb.toString();
145ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
146ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
147ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
148ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
149ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the key to human readable string.
150ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param key the key to be converted to string.
151ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>key</code>.
152ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
153ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key key) {
154ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyStringizer.STRINGIZER.stringize(key);
155ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
156ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
157ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
158ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the keyboard row to human readable string.
159ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param keys the keyboard row to be converted to string.
160ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>keys</code>.
161ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
162ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key[] keys) {
163ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyStringizer.STRINGIZER.join(keys);
164ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
165ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
166ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the array of the key.
167ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> {
168ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
169ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
170ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
171ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final Key[] keyArray) {
172ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return KeyStringizer.STRINGIZER.join(keyArray);
173ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
174ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
175ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
176ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
177ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the keyboard to human readable string.
178ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param rows the keyboard to be converted to string.
179ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>rows</code>.
180ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
181ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key[][] rows) {
182ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
183ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
184ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka}
185