AbstractKeyboardBuilder.java revision f247b171ce98fd35f1f8de7e3d7f8f35099cf6fe
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 java.util.Arrays;
20
21/**
22 * This class builds a keyboard that is a two dimensional array of elements <code>E</code>.
23 *
24 * A keyboard consists of array of rows, and a row consists of array of elements. Each row may have
25 * different number of elements. A element of a keyboard can be specified by a row number and a
26 * column number, both numbers starts from 1.
27 *
28 * @param <E> the type of a keyboard element. A keyboard element must be an immutable object.
29 */
30abstract class AbstractKeyboardBuilder<E> {
31    // A building array of rows.
32    private final E[][] mRows;
33
34    // Returns an instance of default element.
35    abstract E defaultElement();
36    // Returns an <code>E</code> array instance of the <code>size</code>.
37    abstract E[] newArray(final int size);
38    // Returns an <code>E[]</code> array instance of the <code>size</code>.
39    abstract E[][] newArrayOfArray(final int size);
40
41    /**
42     * Construct a builder filled with the default element.
43     * @param dimensions the integer array of each row's size.
44     */
45    AbstractKeyboardBuilder(final int ... dimensions) {
46        mRows = newArrayOfArray(dimensions.length);
47        for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) {
48            mRows[rowIndex] = newArray(dimensions[rowIndex]);
49            Arrays.fill(mRows[rowIndex], defaultElement());
50        }
51    }
52
53    /**
54     * Construct a builder from template keyboard. This builder has the same dimensions and
55     * elements of <code>rows</rows>.
56     * @param rows the template keyboard rows. The elements of the <code>rows</code> will be
57     *        shared with this builder. Therefore a element must be an immutable object.
58     */
59    AbstractKeyboardBuilder(final E[][] rows) {
60        mRows = newArrayOfArray(rows.length);
61        for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
62            final E[] row = rows[rowIndex];
63            mRows[rowIndex] = Arrays.copyOf(row, row.length);
64        }
65    }
66
67    /**
68     * Return current constructing keyboard.
69     * @return the array of the array of the element being constructed.
70     */
71    E[][] build() {
72        return mRows;
73    }
74
75    /**
76     * Return the number of rows.
77     * @return the number of rows being constructed.
78     */
79    int getRowCount() {
80        return mRows.length;
81    }
82
83    /**
84     * Get the current contents of the specified row.
85     * @param row the row number to get the contents.
86     * @return the array of elements at row number <code>row</code>.
87     * @throws {@link RuntimeException} if <code>row</code> is illegal.
88     */
89    E[] getRowAt(final int row) {
90        final int rowIndex = row - 1;
91        if (rowIndex < 0 || rowIndex >= mRows.length) {
92            throw new RuntimeException("Illegal row number: " + row);
93        }
94        return mRows[rowIndex];
95    }
96
97    /**
98     * Set an array of elements to the specified row.
99     * @param row the row number to set <code>elements</code>.
100     * @param elements the array of elements to set at row number <code>row</code>.
101     * @throws {@link RuntimeException} if <code>row</code> is illegal.
102     */
103    void setRowAt(final int row, final E[] elements) {
104        final int rowIndex = row - 1;
105        if (rowIndex < 0 || rowIndex >= mRows.length) {
106            throw new RuntimeException("Illegal row number: " + row);
107        }
108        mRows[rowIndex] = elements;
109    }
110
111    /**
112     * Set or insert an element at specified position.
113     * @param row the row number to set or insert the <code>element</code>.
114     * @param column the column number to set or insert the <code>element</code>.
115     * @param element the element to set or insert at <code>row,column</code>.
116     * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>.
117     *        Otherwise the <code>element</code> replace the element at <code>row,column</code>.
118     * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal.
119     */
120    void setElementAt(final int row, final int column, final E element, final boolean insert) {
121        final E[] elements = getRowAt(row);
122        final int columnIndex = column - 1;
123        if (insert) {
124            if (columnIndex < 0 || columnIndex >= elements.length + 1) {
125                throw new RuntimeException("Illegal column number: " + column);
126            }
127            final E[] newElements = Arrays.copyOf(elements, elements.length + 1);
128            // Shift the remaining elements.
129            System.arraycopy(newElements, columnIndex, newElements, columnIndex + 1,
130                    elements.length - columnIndex);
131            // Insert the element at <code>row,column</code>.
132            newElements[columnIndex] = element;
133            // Replace the current row with one.
134            setRowAt(row, newElements);
135            return;
136        }
137        if (columnIndex < 0 || columnIndex >= elements.length) {
138            throw new RuntimeException("Illegal column number: " + column);
139        }
140        elements[columnIndex] = element;
141    }
142}
143