Home | History | Annotate | Download | only in expected
      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.
     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 an empty builder.
     43      */
     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      */
     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      */
     66     E[][] build() {
     67         return mRows;
     68     }
     69 
     70     /**
     71      * Return the number of rows.
     72      * @return the number of rows being constructed.
     73      */
     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      */
     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      */
     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      */
    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