001: package com.mockrunner.jdbc;
002:
003: import java.util.ArrayList;
004: import java.util.Arrays;
005: import java.util.HashSet;
006: import java.util.List;
007: import java.util.Set;
008:
009: /**
010: * A data structure providing tabular (row and column) access semantics to
011: * items within. While applicable to several usages, the primary purpose is
012: * (in conjunction with <code>ArrayResultSetFactory</code>) to provide for easy
013: * set up of unit test fixtures and assertion of outcomes with the same data
014: * structures, without any need for external sources of test data.
015: *
016: * @author Erick G. Reid
017: */
018: public class StringValuesTable {
019: private String name; // the table name
020: private List columnNames = new ArrayList(0); // the columns
021: private String[][] stringMatrix; // the table data
022:
023: /**
024: * Creates a new <code>StringValuesTable</code> with the given name,
025: * columns and string matrix.
026: *
027: * @param name the table name. This argument cannot be <code>null</code>
028: * and must contain at least <code>1</code> non-blank character.
029: * @param stringMatrix the string matrix. This argument cannot be <code>null</code>,
030: * must not contain any null values, and each array in the matrix
031: * must contain the same number of elements as the first (<code>stringMatrix[0].length == stringMatrix[n].length</code>
032: * for any given valid row number, <code>n</code>). Further,
033: * this matrix must, at a minimum represent <code>1</code> row
034: * and <code>1</code> column of items (<code>stringMatrix.length >= 1</code>,
035: * and <code>stringMatrix[0].length >= 1</code>).
036: */
037: public StringValuesTable(String name, String[][] stringMatrix) {
038: this (name, null, stringMatrix);
039: }
040:
041: /**
042: * Creates a new <code>StringValuesTable</code> with the given name,
043: * columns and string matrix.
044: *
045: * @param name the table name. This argument cannot be <code>null</code>
046: * and must contain at least <code>1</code> non-blank character.
047: * @param columnNames the names for the columns in this <code>
048: * StringValuesTable</code>. This argument may be <code>null</code> if no column names
049: * are desired, but if a non-<code>null</code> array reference
050: * is given, the array cannot contain any <code>null</code> nor
051: * duplicate elements, and must have the same number of elements
052: * as there are columns in the given string matrix (<code>stringMatrix[n]</code>
053: * for any given valid row number, <code>n</code>).
054: * @param stringMatrix the string matrix. This argument cannot be <code>null</code>,
055: * must not contain any null values, and each array in the matrix
056: * must contain the same number of elements as the first (<code>stringMatrix[0].length == stringMatrix[n].length</code>
057: * for any given valid row number, <code>n</code>). Further,
058: * this matrix must, at a minimum represent <code>1</code> row
059: * and <code>1</code> column of items (<code>stringMatrix.length >= 1</code>,
060: * and <code>stringMatrix[0].length >= 1</code>).
061: */
062: public StringValuesTable(String name, String[] columnNames,
063: String[][] stringMatrix) {
064:
065: if (name != null) {
066: if (name.trim().length() > 0) {
067: this .name = name;
068: this .stringMatrix = verifyStringMatrix(stringMatrix);
069: if (columnNames != null) {
070: this .columnNames = Arrays.asList(verifyColumnNames(
071: columnNames, stringMatrix));
072: }
073: return;
074: }
075: throw new IllegalArgumentException(
076: "invalid table name given");
077: }
078: throw new IllegalArgumentException(
079: "the table name cannot be null");
080: }
081:
082: /**
083: * Returns the contents of the given column.
084: *
085: * @param columnName the name of the desired column. This argument cannot be
086: * <code>null</code> and must be a valid column for this
087: * <code>StringValuesTable</code>.
088: * @return the contents of the given column.
089: */
090: public String[] getColumn(String columnName) {
091: if (columnName != null) {
092: int index = this .columnNames.indexOf(columnName);
093: if (index >= 0) {
094: return doGetColumn(index);
095: }
096: throw new IllegalArgumentException(columnName
097: + " is not a valid column name");
098: }
099: throw new IllegalArgumentException(
100: "the column name cannot be null");
101: }
102:
103: /**
104: * Returns the contents of the given column.
105: *
106: * @param columnNumber the index of the desired column (<code>1</code>-based).
107: * This argument must be a valid column index for this
108: * <code>StringValuesTable</code>.
109: * @return the contents of the given column.
110: */
111: public String[] getColumn(int columnNumber) {
112: return doGetColumn(--columnNumber);
113: }
114:
115: /**
116: * Returns the column names. This array may be empty if column names are not
117: * being used.
118: *
119: * @return the column names.
120: */
121: public String[] getColumnNames() {
122: return (String[]) this .columnNames
123: .toArray(new String[this .columnNames.size()]);
124: }
125:
126: /**
127: * Returns the item found in the string matrix at the given coordinate.
128: *
129: * @param rowNumber the number of the desired row (<code>1</code>-based). This
130: * argument must be a valid row number for this
131: * <code>StringValuesTable</code>.
132: * @param columnName the name of the desired column. This argument cannot be
133: * <code>null</code> and must be a valid column for this
134: * <code>StringValuesTable</code>.
135: * @return the item found in the string matrix at the given coordinate.
136: */
137: public String getItem(int rowNumber, String columnName) {
138: if (columnName != null) {
139: int index = this .columnNames.indexOf(columnName);
140: if (index >= 0) {
141: return doGetRow(rowNumber)[index];
142: }
143: throw new IllegalArgumentException(columnName
144: + " is not a valid column index");
145: }
146: throw new IllegalArgumentException(
147: "the column name cannot be null");
148: }
149:
150: /**
151: * Returns the item found in the string matrix at the given coordinate.
152: *
153: * @param rowNumber the number of the desired row (<code>1</code>-based). This
154: * argument must be a valid row number for this
155: * <code>StringValuesTable</code>.
156: * @param columnNumber the index of the desired column (<code>1</code>-based).
157: * This argument must be a valid column index for this
158: * <code>StringValuesTable</code>.
159: * @return the item found in the string matrix at the given coordinate.
160: */
161: public String getItem(int rowNumber, int columnNumber) {
162: if (isColumnNumberValid(columnNumber)) {
163: return doGetRow(rowNumber)[--columnNumber];
164: }
165: throw new IllegalArgumentException(columnNumber
166: + " is not a valid column index");
167: }
168:
169: /**
170: * Returns the table name.
171: *
172: * @return the table name.
173: */
174: public String getName() {
175: return this .name;
176: }
177:
178: /**
179: * Returns the number of columns found in the string matrix for this
180: * <code>StringValuesTable</code>.
181: *
182: * @return the number of columns found in the string matrix for this
183: * <code>StringValuesTable</code>.
184: */
185: public int getNumberOfColumns() {
186: return this .stringMatrix[0].length;
187: }
188:
189: /**
190: * Returns the number of rows found in the string matrix for this
191: * <code>StringValuesTable</code>.
192: *
193: * @return the number of rows found in the string matrix for this
194: * <code>StringValuesTable</code>.
195: */
196: public int getNumberOfRows() {
197: return this .stringMatrix.length;
198: }
199:
200: /**
201: * Returns the elements of the given row.
202: *
203: * @param rowNumber the number of the desired row (<code>1</code>-based). This
204: * argument must be a valid row number for this
205: * <code>StringValuesTable</code>.
206: * @return the elements of the given row.
207: */
208: public String[] getRow(int rowNumber) {
209: return doGetRow(rowNumber);
210: }
211:
212: /**
213: * Returns <code>true</code> if the given column name is valid for this
214: * <code>StringValuesTable</code>; returns <code>false</code>
215: * otherwise.
216: *
217: * @param columnName the column name to verify.
218: * @return <code>true</code> if the given column name is valid for this
219: * <code>StringValuesTable</code>.
220: */
221: public boolean isValidColumnName(String columnName) {
222: return columnName == null ? false
223: : isColumnNumberValid(this .columnNames
224: .indexOf(columnName) + 1);
225: }
226:
227: /**
228: * Returns <code>true</code> if the given column number is valid for this
229: * <code>StringValuesTable</code>; returns <code>false</code>
230: * otherwise.
231: *
232: * @param columnNumber the column number to verify.
233: * @return <code>true</code> if the given column number is valid for this
234: * <code>StringValuesTable</code>.
235: */
236: public boolean isValidColumnNumber(int columnNumber) {
237: return isColumnNumberValid(columnNumber);
238: }
239:
240: /**
241: * Returns <code>true</code> if the given row number is valid for this
242: * <code>StringValuesTable</code>; returns <code>false</code>
243: * otherwise.
244: *
245: * @param row the row number to verify.
246: * @return <code>true</code> if the given index is valid for this
247: * <code>StringValuesTable</code>.
248: */
249: public boolean isValidRowNumber(int row) {
250: return --row >= 0 && row < this .stringMatrix.length;
251: }
252:
253: /**
254: * Returns the tabular data for this <code>StringValuesTable</code>.
255: *
256: * @return the tabular data for this <code>StringValuesTable</code>.
257: */
258: public String[][] getStringMatrix() {
259: return this .stringMatrix;
260: }
261:
262: /**
263: * Returns the given array if it is found to indeed be valid according to
264: * the published contract.
265: */
266: public synchronized static String[] verifyColumnNames(
267: final String[] columnNames, final String[][] stringMatrix) {
268: // note: the string matrix must already have been verified at this
269: // point...
270:
271: if (columnNames != null) {
272: if (columnNames.length == stringMatrix[0].length) {
273: String name = null;
274: Set names = new HashSet();
275: for (int i = 0; i < columnNames.length; i++) {
276: name = columnNames[i];
277: if (name == null) {
278: throw new IllegalArgumentException(
279: "the column names array must not contain null elements");
280: } else {
281: if (names.contains(name)) {
282: throw new IllegalArgumentException(
283: "the column names array must not contain duplicate elements");
284: }
285: names.add(name);
286: }
287: }
288: return columnNames;
289: }
290: throw new IllegalArgumentException(columnNames.length
291: + " columns were given where "
292: + stringMatrix[0].length
293: + (stringMatrix[0].length == 1 ? " is" : " are")
294: + " required");
295: }
296: throw new IllegalArgumentException(
297: "the column names array cannot be null");
298: }
299:
300: /**
301: * Returns the given matrix if it is found to indeed be valid according to
302: * the published contract.
303: */
304: public synchronized static String[][] verifyStringMatrix(
305: final String[][] stringMatrix) {
306: if (stringMatrix != null) {
307: if (stringMatrix.length > 0) {
308: if (stringMatrix[0] != null
309: && stringMatrix[0].length > 0) {
310: for (int ii = 0; ii < stringMatrix.length; ii++) {
311: if (stringMatrix[ii] == null) {
312: throw new IllegalArgumentException(
313: "the string matrix cannot contain any null arrays");
314: }
315: if (stringMatrix[ii].length != stringMatrix[0].length) {
316: throw new IllegalArgumentException(
317: "arrays in the string matrix must all contain "
318: + stringMatrix[0].length
319: + " elements");
320: }
321: for (int jj = 0; jj < stringMatrix[ii].length; jj++) {
322: if (stringMatrix[ii][jj] == null) {
323: throw new IllegalArgumentException(
324: "arrays in the string matrix must not contain null elements");
325: }
326: }
327: }
328: return stringMatrix;
329: }
330: throw new IllegalArgumentException(
331: "the string matrix must contain at least 1 column of items");
332: }
333: throw new IllegalArgumentException(
334: "the string matrix must contain at least 1 row of items");
335: }
336: throw new IllegalArgumentException(
337: "the string matrix cannot be null");
338: }
339:
340: /**
341: * Returns an array of items from each row of the given column.
342: */
343: private String[] doGetColumn(int index) {
344: if (index >= 0 && index < this .stringMatrix[0].length) {
345: String[] data = new String[this .stringMatrix.length];
346: for (int row = 0; row < this .stringMatrix.length; row++) {
347: data[row] = this .stringMatrix[row][index];
348: }
349: return data;
350: }
351: throw new IllegalArgumentException(++index
352: + " is not a valid column index");
353: }
354:
355: /**
356: * Returns the elements of the given row.
357: */
358: private String[] doGetRow(int rowNumber) {
359: if (--rowNumber >= 0 && rowNumber < this .stringMatrix.length) {
360: return this .stringMatrix[rowNumber];
361: }
362: throw new IllegalArgumentException(++rowNumber
363: + " is not a valid row number");
364: }
365:
366: /**
367: * Returns <code>true</code> if the given column number is valid
368: */
369: private boolean isColumnNumberValid(int columnNumber) {
370: return --columnNumber >= 0
371: && columnNumber < this .stringMatrix[0].length;
372: }
373: }
|