ActualKeyboardBuilder.java revision cd15cfdaaba7f361f4744bd3ff51ce6cdae1e608
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.CollectionUtils;
24ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.StringUtils;
25ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
26ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport java.util.ArrayList;
27ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport java.util.Collections;
28ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokaimport java.util.Comparator;
29ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
30ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka/**
31ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka * This class builds an actual keyboard for unit test.
32ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka */
33ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaokapublic final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
34ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Comparator to sort {@link Key}s from top-left to bottom-right order.
35ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    private static final Comparator<Key> ROW_COLUMN_COMPARATOR = new Comparator<Key>() {
36ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
37ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public int compare(final Key lhs, final Key rhs) {
38ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lhs.getY() < rhs.getY()) return -1;
39ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lhs.getY() > rhs.getY()) return 1;
40ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lhs.getX() < rhs.getX()) return -1;
41ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lhs.getX() > rhs.getX()) return 1;
42ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return 0;
43ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
44ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    };
45ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
468c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka    private static ArrayList<Key> filterOutSpacerAndSortKeys(final Key[] keys) {
478c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        final ArrayList<Key> filteredKeys = CollectionUtils.newArrayList();
488c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        for (final Key key : keys) {
498c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            if (key.isSpacer()) {
508c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka                continue;
518c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            }
528c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka            filteredKeys.add(key);
538c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        }
548c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        Collections.sort(filteredKeys, ROW_COLUMN_COMPARATOR);
558c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        return filteredKeys;
568c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka    }
578c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka
58ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
59ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Create the keyboard that consists of the array of rows of the actual keyboard's keys.
60ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param keys the array of keys of the actual keyboard.
61ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the actual keyboard grouped with rows.
62ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
63ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static Key[][] buildKeyboard(final Key[] keys) {
648c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        // Filter out spacer and sort keys from top-left to bottom-right order to prepare to
658c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        // create rows.
668c6b34e51dfd139d55ad1ea7d6e39a7223117fc3Tadashi G. Takaoka        final ArrayList<Key> sortedKeys = filterOutSpacerAndSortKeys(keys);
67ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
68ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        // Grouping keys into rows.
69ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        final ArrayList<ArrayList<Key>> rows = CollectionUtils.newArrayList();
70ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        ArrayList<Key> elements = CollectionUtils.newArrayList();
71ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        int lastY = sortedKeys.get(0).getY();
72ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        for (final Key key : sortedKeys) {
73ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (lastY != key.getY()) {
74ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                // A new row is starting.
75ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                lastY = key.getY();
76ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                rows.add(elements);
77ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                elements = CollectionUtils.newArrayList();
78ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
79ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            elements.add(key);
80ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
81ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        rows.add(elements); // Add the last row.
82ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
83ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        // Calculate each dimension of rows and create a builder.
84ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        final int[] dimensions = new int[rows.size()];
85ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
86ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            dimensions[rowIndex] = rows.get(rowIndex).size();
87ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
88cd15cfdaaba7f361f4744bd3ff51ce6cdae1e608Tadashi G. Takaoka        final ActualKeyboardBuilder builder = new ActualKeyboardBuilder();
89ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
90ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
91ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final int row = rowIndex + 1;
92ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final ArrayList<Key> rowKeys = rows.get(rowIndex);
93ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()]));
94ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
95ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return builder.build();
96ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
97ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
98ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
99ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key defaultElement() { return null; }
100ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
101ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
102ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key[] newArray(final int size) { return new Key[size]; }
103ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
104ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    @Override
105ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    Key[][] newArrayOfArray(final int size) { return new Key[size][]; }
106ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
107ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the key specification.
108ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> {
109ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer();
110ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
111ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
112ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final MoreKeySpec spec) {
113ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
114ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
115ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
116ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static String toString(final String label, final int iconId, final String outputText,
117ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                final int code) {
118ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
119ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                    ? KeyboardIconsSet.getIconName(iconId) : label;
120ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final String output;
121ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (code == Constants.CODE_OUTPUT_TEXT) {
122ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = outputText;
123ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            } else if (code < Constants.CODE_SPACE) {
124ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = Constants.printableCode(code);
125ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            } else {
126ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                output = StringUtils.newSingleCodePointString(code);
127ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
128ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (visual.equals(output)) {
129ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return visual;
130ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
131ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return visual + "|" + output;
132ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
133ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
134ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
135ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the key.
136ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class KeyStringizer extends StringUtils.Stringizer<Key> {
137ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final KeyStringizer STRINGIZER = new KeyStringizer();
138ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
139ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
140ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final Key key) {
141ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (key == null) {
142ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return "NULL";
143ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
144ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (key.isSpacer()) {
145ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return "SPACER";
146ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
147ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final StringBuilder sb = new StringBuilder();
148ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append(MoreKeySpecStringizer.toString(
149ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                    key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode()));
150ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            final MoreKeySpec[] moreKeys = key.getMoreKeys();
151ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            if (moreKeys == null) {
152ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka                return sb.toString();
153ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            }
154ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append("^");
155ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys));
156ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return sb.toString();
157ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
158ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
159ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
160ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
161ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the key to human readable string.
162ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param key the key to be converted to string.
163ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>key</code>.
164ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
165ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key key) {
166ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyStringizer.STRINGIZER.stringize(key);
167ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
168ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
169ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
170ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the keyboard row to human readable string.
171ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param keys the keyboard row to be converted to string.
172ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>keys</code>.
173ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
174ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key[] keys) {
175ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyStringizer.STRINGIZER.join(keys);
176ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
177ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
178ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    // Helper class to create concise representation from the array of the key.
179ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> {
180ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
181ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
182ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        @Override
183ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        public String stringize(final Key[] keyArray) {
184ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka            return KeyStringizer.STRINGIZER.join(keyArray);
185ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        }
186ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
187ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka
188ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    /**
189ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * Convert the keyboard to human readable string.
190ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @param rows the keyboard to be converted to string.
191ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     * @return the human readable representation of <code>rows</code>.
192ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka     */
193ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    public static String toString(final Key[][] rows) {
194ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka        return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
195ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka    }
196ff8405cdfbd575657a6f615a1ac4d86eb1b07f74Tadashi G. Takaoka}
197