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
17package com.android.inputmethod.keyboard.layout.expected;
18
19import com.android.inputmethod.keyboard.Key;
20import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
21import com.android.inputmethod.keyboard.internal.MoreKeySpec;
22import com.android.inputmethod.latin.Constants;
23import com.android.inputmethod.latin.utils.StringUtils;
24
25import java.util.ArrayList;
26import java.util.List;
27
28/**
29 * This class builds an actual keyboard for unit test.
30 *
31 * An actual keyboard is an array of rows, and a row consists of an array of {@link Key}s.
32 * Each row may have different number of {@link Key}s.
33 */
34public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
35    private static ArrayList<Key> filterOutSpacer(final List<Key> keys) {
36        final ArrayList<Key> filteredKeys = new ArrayList<>();
37        for (final Key key : keys) {
38            if (key.isSpacer()) {
39                continue;
40            }
41            filteredKeys.add(key);
42        }
43        return filteredKeys;
44    }
45
46    /**
47     * Create the keyboard that consists of the array of rows of the actual keyboard's keys.
48     * @param sortedKeys keys list of the actual keyboard that is sorted from top-left to
49     * bottom-right.
50     * @return the actual keyboard grouped with rows.
51     */
52    public static Key[][] buildKeyboard(final List<Key> sortedKeys) {
53        // Filter out spacer to prepare to create rows.
54        final ArrayList<Key> filteredSortedKeys = filterOutSpacer(sortedKeys);
55
56        // Grouping keys into rows.
57        final ArrayList<ArrayList<Key>> rows = new ArrayList<>();
58        ArrayList<Key> elements = new ArrayList<>();
59        int lastY = filteredSortedKeys.get(0).getY();
60        for (final Key key : filteredSortedKeys) {
61            if (lastY != key.getY()) {
62                // A new row is starting.
63                lastY = key.getY();
64                rows.add(elements);
65                elements = new ArrayList<>();
66            }
67            elements.add(key);
68        }
69        rows.add(elements); // Add the last row.
70
71        // Calculate each dimension of rows and create a builder.
72        final int[] dimensions = new int[rows.size()];
73        for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
74            dimensions[rowIndex] = rows.get(rowIndex).size();
75        }
76        final ActualKeyboardBuilder builder = new ActualKeyboardBuilder();
77
78        for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
79            final int row = rowIndex + 1;
80            final ArrayList<Key> rowKeys = rows.get(rowIndex);
81            builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()]));
82        }
83        return builder.build();
84    }
85
86    @Override
87    Key defaultElement() { return null; }
88
89    @Override
90    Key[] newArray(final int size) { return new Key[size]; }
91
92    @Override
93    Key[][] newArrayOfArray(final int size) { return new Key[size][]; }
94
95    // Helper class to create concise representation from the key specification.
96    static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> {
97        static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer();
98
99        @Override
100        public String stringize(final MoreKeySpec spec) {
101            return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
102        }
103
104        static String toString(final String label, final int iconId, final String outputText,
105                final int code) {
106            final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
107                    ? KeyboardIconsSet.getIconName(iconId) : label;
108            final String output;
109            if (code == Constants.CODE_OUTPUT_TEXT) {
110                output = outputText;
111            } else if (code < Constants.CODE_SPACE) {
112                output = Constants.printableCode(code);
113            } else {
114                output = StringUtils.newSingleCodePointString(code);
115            }
116            if (visual.equals(output)) {
117                return visual;
118            }
119            return visual + "|" + output;
120        }
121    }
122
123    // Helper class to create concise representation from the key.
124    static class KeyStringizer extends StringUtils.Stringizer<Key> {
125        static final KeyStringizer STRINGIZER = new KeyStringizer();
126
127        @Override
128        public String stringize(final Key key) {
129            if (key == null) {
130                return "NULL";
131            }
132            if (key.isSpacer()) {
133                return "SPACER";
134            }
135            final StringBuilder sb = new StringBuilder();
136            sb.append(MoreKeySpecStringizer.toString(
137                    key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode()));
138            final MoreKeySpec[] moreKeys = key.getMoreKeys();
139            if (moreKeys == null) {
140                return sb.toString();
141            }
142            sb.append("^");
143            sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys));
144            return sb.toString();
145        }
146    }
147
148    /**
149     * Convert the key to human readable string.
150     * @param key the key to be converted to string.
151     * @return the human readable representation of <code>key</code>.
152     */
153    public static String toString(final Key key) {
154        return KeyStringizer.STRINGIZER.stringize(key);
155    }
156
157    /**
158     * Convert the keyboard row to human readable string.
159     * @param keys the keyboard row to be converted to string.
160     * @return the human readable representation of <code>keys</code>.
161     */
162    public static String toString(final Key[] keys) {
163        return KeyStringizer.STRINGIZER.join(keys);
164    }
165
166    // Helper class to create concise representation from the array of the key.
167    static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> {
168        static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
169
170        @Override
171        public String stringize(final Key[] keyArray) {
172            return KeyStringizer.STRINGIZER.join(keyArray);
173        }
174    }
175
176    /**
177     * Convert the keyboard to human readable string.
178     * @param rows the keyboard to be converted to string.
179     * @return the human readable representation of <code>rows</code>.
180     */
181    public static String toString(final Key[][] rows) {
182        return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
183    }
184}
185