AbstractKeyboardBuilder.java revision f7c84f35c73081c9d7606378e5d91a759e7aae42
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     * Get the current contents of the specified row.
77     * @param row the row number to get the contents.
78     * @return the array of elements at row number <code>row</code>.
79     * @throws {@link RuntimeException} if <code>row</code> is illegal.
80     */
81    E[] getRowAt(final int row) {
82        final int rowIndex = row - 1;
83        if (rowIndex < 0 || rowIndex >= mRows.length) {
84            throw new RuntimeException("Illegal row number: " + row);
85        }
86        return mRows[rowIndex];
87    }
88
89    /**
90     * Set an array of elements to the specified row.
91     * @param row the row number to set <code>elements</code>.
92     * @param elements the array of elements to set at row number <code>row</code>.
93     * @throws {@link RuntimeException} if <code>row</code> is illegal.
94     */
95    void setRowAt(final int row, final E[] elements) {
96        final int rowIndex = row - 1;
97        if (rowIndex < 0 || rowIndex >= mRows.length) {
98            throw new RuntimeException("Illegal row number: " + row);
99        }
100        mRows[rowIndex] = elements;
101    }
102
103    /**
104     * Set or insert an element at specified position.
105     * @param row the row number to set or insert the <code>element</code>.
106     * @param column the column number to set or insert the <code>element</code>.
107     * @param element the element to set or insert at <code>row,column</code>.
108     * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>.
109     *        Otherwise the <code>element</code> replace the element at <code>row,column</code>.
110     * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal.
111     */
112    void setElementAt(final int row, final int column, final E element, final boolean insert) {
113        final E[] elements = getRowAt(row);
114        final int columnIndex = column - 1;
115        if (insert) {
116            if (columnIndex < 0 || columnIndex >= elements.length + 1) {
117                throw new RuntimeException("Illegal column number: " + column);
118            }
119            final E[] newElements = Arrays.copyOf(elements, elements.length + 1);
120            // Shift the remaining elements.
121            System.arraycopy(newElements, columnIndex, newElements, columnIndex + 1,
122                    elements.length - columnIndex);
123            // Insert the element at <code>row,column</code>.
124            newElements[columnIndex] = element;
125            // Replace the current row with one.
126            setRowAt(row, newElements);
127            return;
128        }
129        if (columnIndex < 0 || columnIndex >= elements.length) {
130            throw new RuntimeException("Illegal column number: " + column);
131        }
132        elements[columnIndex] = element;
133    }
134}
135