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