- clean up javadocs
- enhance public api classes
- add @usage tags to util classes
-- add unit tests for Row update/delete methods, add/update *FromMap methods
-- add reason to unsupop throws for indexes
+* add unit tests for Row update/delete methods, add/update *FromMap methods
+* add reason to unsupop throws for indexes
/**
* Update the current row.
+ * @return the given row values if long enough, otherwise a new array,
+ * updated with the current row values
* @throws IllegalStateException if the current row is not valid (at
* beginning or end of table), or deleted.
*/
- public void updateCurrentRow(Object... row) throws IOException;
+ public Object[] updateCurrentRow(Object... row) throws IOException;
/**
* Update the current row.
+ * @return the given row, updated with the current row values
* @throws IllegalStateException if the current row is not valid (at
* beginning or end of table), or deleted.
*/
- public void updateCurrentRowFromMap(Map<String,?> row) throws IOException;
+ public <M extends Map<String,Object>> M updateCurrentRowFromMap(M row)
+ throws IOException;
/**
* Moves to the next row in the table and returns it.
package com.healthmarketscience.jackcess;
import java.io.IOException;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
* @param row row values for a single row. the given row array will be
* modified if this table contains an auto-number column,
* otherwise it will not be modified.
+ * @return the given row values if long enough, otherwise a new array. the
+ * returned array will contain any autonumbers generated
* @usage _general_method_
*/
- public void addRow(Object... row) throws IOException;
+ public Object[] addRow(Object... row) throws IOException;
/**
* Calls {@link #asRow} on the given row map and passes the result to {@link
* <p/>
* Note, if this table has an auto-number column, the value generated will be
* put back into the given row map.
+ * @return the given row map, which will contain any autonumbers generated
+ * @usage _general_method_
*/
- public void addRowFromMap(Map<String,Object> row) throws IOException;
+ public <M extends Map<String,Object>> M addRowFromMap(M row)
+ throws IOException;
/**
* Add multiple rows to this table, only writing to disk after all
* rows have been written, and every time a data page is filled. This
- * is much more efficient than calling <code>addRow</code> multiple times.
+ * is much more efficient than calling {@link #addRow} multiple times.
* <p>
* Note, if this table has an auto-number column, the values written will be
* put back into the given row arrays (assuming the given row array is at
* @param rows List of Object[] row values. the rows will be modified if
* this table contains an auto-number column, otherwise they
* will not be modified.
+ * @return the given row values list (unless row values were to small), with
+ * appropriately sized row values (the ones passed in if long
+ * enough). the returned arrays will contain any autonumbers
+ * generated
* @usage _general_method_
*/
- public void addRows(List<? extends Object[]> rows) throws IOException;
+ public List<? extends Object[]> addRows(List<? extends Object[]> rows)
+ throws IOException;
/**
* Calls {@link #asRow} on the given row maps and passes the results to
* <p/>
* Note, if this table has an auto-number column, the values generated will
* be put back into the appropriate row maps.
+ * @return the given row map list, where the row maps will contain any
+ * autonumbers generated
+ * @usage _general_method_
*/
- public void addRowsFromMaps(List<? extends Map<String,Object>> rows)
+ public <M extends Map<String,Object>> List<M> addRowsFromMaps(List<M> rows)
throws IOException;
/**
* Update the given row. Provided Row must have previously been returned
* from this Table.
+ * @return the given row, updated with the current row values
* @throws IllegalStateException if the given row is not valid, or deleted.
*/
- public void updateRow(Row row) throws IOException;
+ public Row updateRow(Row row) throws IOException;
/**
* Delete the given row. Provided Row must have previously been returned
* from this Table.
+ * @return the given row
* @throws IllegalStateException if the given row is not valid
*/
- public void deleteRow(Row row) throws IOException;
+ public Row deleteRow(Row row) throws IOException;
+
+ /**
+ * Calls {@link #reset} on this table and returns a modifiable
+ * Iterator which will iterate through all the rows of this table. Use of
+ * the Iterator follows the same restrictions as a call to
+ * {@link #getNextRow}.
+ * <p/>
+ * For more advanced iteration, use the {@link #getDefaultCursor default
+ * cursor} directly.
+ * @throws RuntimeIOException if an IOException is thrown by one of the
+ * operations, the actual exception will be contained within
+ * @usage _general_method_
+ */
+ public Iterator<Row> iterator();
/**
* After calling this method, {@link #getNextRow} will return the first row
- * in the table, see {@link Cursor#reset} (uses the default cursor).
+ * in the table, see {@link Cursor#reset} (uses the {@link #getDefaultCursor
+ * default cursor}).
* @usage _general_method_
*/
public void reset();
/**
* @return The next row in this table (Column name -> Column value) (uses
- * the default cursor)
+ * the {@link #getDefaultCursor default cursor})
* @usage _general_method_
*/
public Row getNextRow() throws IOException;
* @param rowPattern pattern to be used to find the row
* @return the matching row or {@code null} if a match could not be found.
*/
- public static Row findRow(TableImpl table,
- Map<String,?> rowPattern)
+ public static Row findRow(TableImpl table, Map<String,?> rowPattern)
throws IOException
{
CursorImpl cursor = createCursor(table);
* @return the matching row or {@code null} if a match could not be found.
*/
public static Row findRow(TableImpl table, IndexImpl index,
- Map<String,?> rowPattern)
+ Map<String,?> rowPattern)
throws IOException
{
CursorImpl cursor = createIndexCursor(table, index);
_table.deleteRow(_rowState, _curPos.getRowId());
}
- public void updateCurrentRow(Object... row) throws IOException {
- _table.updateRow(_rowState, _curPos.getRowId(), row);
+ public Object[] updateCurrentRow(Object... row) throws IOException {
+ return _table.updateRow(_rowState, _curPos.getRowId(), row);
}
- public void updateCurrentRowFromMap(Map<String,?> row) throws IOException {
- _table.updateRow(_rowState, _curPos.getRowId(), _table.asUpdateRow(row));
+ public <M extends Map<String,Object>> M updateCurrentRowFromMap(M row)
+ throws IOException
+ {
+ return _table.updateRowFromMap(_rowState, _curPos.getRowId(), row);
}
public Row getNextRow() throws IOException {
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import com.healthmarketscience.jackcess.IndexBuilder;
import com.healthmarketscience.jackcess.PropertyMap;
import com.healthmarketscience.jackcess.Row;
-import com.healthmarketscience.jackcess.RowId;
import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.util.ErrorHandler;
import org.apache.commons.logging.Log;
getDefaultCursor().reset();
}
- public void deleteRow(Row row) throws IOException {
+ public Row deleteRow(Row row) throws IOException {
deleteRow(getDefaultCursor().getRowState(), (RowIdImpl)row.getId());
+ return row;
}
/**
- * Delete the row on which the given rowState is currently positioned.
- * <p>
- * Note, this method is not generally meant to be used directly. You should
- * use the {@link #deleteCurrentRow} method or use the Cursor class, which
- * allows for more complex table interactions.
+ * Delete the row for the given rowId.
* @usage _advanced_method_
*/
public void deleteRow(RowState rowState, RowIdImpl rowId)
/**
* Reads a single column from the given row.
- * <p>
- * Note, this method is not generally meant to be used directly. Instead
- * use the Cursor class, which allows for more complex table interactions,
- * e.g. {@link Cursor#getCurrentRowValue}.
* @usage _advanced_method_
*/
public Object getRowValue(RowState rowState, RowIdImpl rowId,
}
}
-
- /**
- * Calls <code>reset</code> on this table and returns a modifiable
- * Iterator which will iterate through all the rows of this table. Use of
- * the Iterator follows the same restrictions as a call to
- * <code>getNextRow</code>.
- * @throws RuntimeIOException if an IOException is thrown by one of the
- * operations, the actual exception will be contained within
- * @usage _general_method_
- */
- public Iterator<Row> iterator()
- {
- reset();
+ public Iterator<Row> iterator() {
return getDefaultCursor().iterator();
}
return row;
}
- public void addRow(Object... row) throws IOException {
- addRows(Collections.singletonList(row), _singleRowBufferH);
+ public Object[] addRow(Object... row) throws IOException {
+ return addRows(Collections.singletonList(row), _singleRowBufferH).get(0);
}
- public void addRowFromMap(Map<String,Object> row) throws IOException {
+ public <M extends Map<String,Object>> M addRowFromMap(M row)
+ throws IOException
+ {
Object[] rowValues = asRow(row);
addRow(rowValues);
- returnAutoNumValues(row, rowValues);
+ returnRowValues(row, rowValues, _autoNumColumns);
+ return row;
}
- public void addRows(List<? extends Object[]> rows) throws IOException {
- addRows(rows, _multiRowBufferH);
+ public List<? extends Object[]> addRows(List<? extends Object[]> rows)
+ throws IOException
+ {
+ return addRows(rows, _multiRowBufferH);
}
- public void addRowsFromMaps(List<? extends Map<String,Object>> rows)
+ public <M extends Map<String,Object>> List<M> addRowsFromMaps(List<M> rows)
throws IOException
{
List<Object[]> rowValuesList = new ArrayList<Object[]>(rows.size());
for(int i = 0; i < rowValuesList.size(); ++i) {
Map<String,Object> row = rows.get(i);
Object[] rowValues = rowValuesList.get(i);
- returnAutoNumValues(row, rowValues);
+ returnRowValues(row, rowValues, _autoNumColumns);
}
}
+ return rows;
}
- private void returnAutoNumValues(Map<String,Object> row, Object[] rowValues)
+ private static void returnRowValues(Map<String,Object> row, Object[] rowValues,
+ List<ColumnImpl> cols)
{
- for(ColumnImpl col : _autoNumColumns) {
+ for(ColumnImpl col : cols) {
col.setRowValue(row, col.getRowValue(rowValues));
}
}
* @param writeRowBufferH TempBufferHolder used to generate buffers for
* writing the row data
*/
- private void addRows(List<? extends Object[]> inRows,
- TempBufferHolder writeRowBufferH)
+ private List<? extends Object[]> addRows(List<? extends Object[]> rows,
+ TempBufferHolder writeRowBufferH)
throws IOException
{
- if(inRows.isEmpty()) {
- return;
+ if(rows.isEmpty()) {
+ return rows;
}
- // copy the input rows to a modifiable list so we can update the elements
- List<Object[]> rows = new ArrayList<Object[]>(inRows);
+ List<Object[]> dupeRows = null;
ByteBuffer[] rowData = new ByteBuffer[rows.size()];
for (int i = 0; i < rows.size(); i++) {
Object[] row = rows.get(i);
if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
row = dupeRow(row, _columns.size());
+ // copy the input rows to a modifiable list so we can update the
+ // elements
+ if(dupeRows == null) {
+ dupeRows = new ArrayList<Object[]>(rows);
+ rows = dupeRows;
+ }
// we copied the row, so put the copy back into the rows list
- rows.set(i, row);
+ dupeRows.set(i, row);
}
// fill in autonumbers
handleAutoNumbersForAdd(row);
// write the row of data to a temporary buffer
- rowData[i] = createRow(row, writeRowBufferH.getPageBuffer(getPageChannel()));
+ rowData[i] = createRow(row,
+ writeRowBufferH.getPageBuffer(getPageChannel()));
if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
throw new IOException("Row size " + rowData[i].limit() +
// Update tdef page
updateTableDefinition(rows.size());
+
+ return rows;
}
- public void updateRow(Row row) throws IOException {
- updateRow(getDefaultCursor().getRowState(), (RowIdImpl)row.getId(),
- asUpdateRow(row));
+ public Row updateRow(Row row) throws IOException {
+ return updateRowFromMap(
+ getDefaultCursor().getRowState(), (RowIdImpl)row.getId(), row);
+ }
+
+ public <M extends Map<String,Object>> M updateRowFromMap(
+ RowState rowState, RowIdImpl rowId, M row)
+ throws IOException
+ {
+ Object[] rowValues = updateRow(rowState, rowId, asUpdateRow(row));
+ returnRowValues(row, rowValues, _columns);
+ return row;
}
/**
- * Update the row on which the given rowState is currently positioned.
- * <p>
- * Note, this method is not generally meant to be used directly. You should
- * use the {@link #updateCurrentRow} method or use the Cursor class, which
- * allows for more complex table interactions, e.g.
- * {@link Cursor#setCurrentRowValue} and {@link Cursor#updateCurrentRow}.
+ * Update the row for the given rowId.
* @usage _advanced_method_
*/
- public void updateRow(RowState rowState, RowIdImpl rowId, Object... row)
+ public Object[] updateRow(RowState rowState, RowIdImpl rowId, Object... row)
throws IOException
{
requireValidRowId(rowId);
writeDataPage(dataPage, pageNumber);
updateTableDefinition(0);
+
+ return row;
}
private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
public ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
throws IOException
{
- return createRow(rowArray, buffer, 0, Collections.<ColumnImpl,byte[]>emptyMap());
+ return createRow(rowArray, buffer, 0,
+ Collections.<ColumnImpl,byte[]>emptyMap());
}
/**
* @return the given buffer, filled with the row data
*/
private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
- int minRowSize, Map<ColumnImpl,byte[]> rawVarValues)
+ int minRowSize,
+ Map<ColumnImpl,byte[]> rawVarValues)
throws IOException
{
buffer.putShort(_maxColumnCount);
import com.healthmarketscience.jackcess.impl.IndexImpl;
import com.healthmarketscience.jackcess.impl.JetFormat;
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
-import com.healthmarketscience.jackcess.impl.RowImpl;
import com.healthmarketscience.jackcess.impl.RowIdImpl;
+import com.healthmarketscience.jackcess.impl.RowImpl;
import com.healthmarketscience.jackcess.impl.TableImpl;
import com.healthmarketscience.jackcess.util.LinkResolver;
import com.healthmarketscience.jackcess.util.MemFileChannel;
+import com.healthmarketscience.jackcess.util.RowFilterTest;
import junit.framework.TestCase;
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
Database db = create(fileFormat);
createTestTable(db);
- Object[] row1 = createTestRow("Tim1");
- Object[] row2 = createTestRow("Tim2");
- Object[] row3 = createTestRow("Tim3");
+ Map<String,Object> row1 = createTestRowMap("Tim1");
+ Map<String,Object> row2 = createTestRowMap("Tim2");
+ Map<String,Object> row3 = createTestRowMap("Tim3");
Table table = db.getTable("Test");
- table.addRows(Arrays.asList(row1, row2, row3));
+ @SuppressWarnings("unchecked")
+ List<Map<String,Object>> rows = Arrays.asList(row1, row2, row3);
+ table.addRowsFromMaps(rows);
assertRowCount(3, table);
table.reset();
}
}
+ public void testDeleteRow() throws Exception {
+
+ // make sure correct row is deleted
+ for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
+ Database db = create(fileFormat);
+ createTestTable(db);
+ Table table = db.getTable("Test");
+ for(int i = 0; i < 10; ++i) {
+ table.addRowFromMap(createTestRowMap("Tim" + i));
+ }
+ assertRowCount(10, table);
+
+ table.reset();
+
+ List<Row> rows = RowFilterTest.toList(table);
+
+ Row r1 = rows.remove(7);
+ Row r2 = rows.remove(3);
+ assertEquals(8, rows.size());
+
+ assertSame(r2, table.deleteRow(r2));
+ assertSame(r1, table.deleteRow(r1));
+
+ assertTable(rows, table);
+
+ table.deleteRow(r2);
+ table.deleteRow(r1);
+
+ assertTable(rows, table);
+ }
+ }
+
public void testReadLongValue() throws Exception {
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.TEST2, true)) {
private void doTestAutoNumber(Table table) throws Exception
{
- table.addRow(null, "row1");
- table.addRow(13, "row2");
- table.addRow("flubber", "row3");
+ Object[] row = {null, "row1"};
+ assertSame(row, table.addRow(row));
+ assertEquals(1, ((Integer)row[0]).intValue());
+ row = table.addRow(13, "row2");
+ assertEquals(2, ((Integer)row[0]).intValue());
+ row = table.addRow("flubber", "row3");
+ assertEquals(3, ((Integer)row[0]).intValue());
table.reset();
- table.addRow(Column.AUTO_NUMBER, "row4");
- table.addRow(Column.AUTO_NUMBER, "row5");
+ row = table.addRow(Column.AUTO_NUMBER, "row4");
+ assertEquals(4, ((Integer)row[0]).intValue());
+ row = table.addRow(Column.AUTO_NUMBER, "row5");
+ assertEquals(5, ((Integer)row[0]).intValue());
+
+ Object[] smallRow = {Column.AUTO_NUMBER};
+ row = table.addRow(smallRow);
+ assertNotSame(row, smallRow);
+ assertEquals(6, ((Integer)row[0]).intValue());
table.reset();
"b", "row4"),
createExpectedRow(
"a", 5,
- "b", "row5"));
+ "b", "row5"),
+ createExpectedRow(
+ "a", 6,
+ "b", null));
assertTable(expectedRows, table);
}
"data", "initial data"),
row);
- c.updateCurrentRow(Column.KEEP_VALUE, Column.AUTO_NUMBER, "new data");
+ Map<String,Object> newRow = createExpectedRow(
+ "name", Column.KEEP_VALUE,
+ "id", Column.AUTO_NUMBER,
+ "data", "new data");
+ assertSame(newRow, c.updateCurrentRowFromMap(newRow));
+ assertEquals(createExpectedRow("name", "row1",
+ "id", 2,
+ "data", "new data"),
+ newRow);
c.moveNextRows(3);
row = c.getCurrentRow();
"data", newText),
row);
+ List<Row> rows = RowFilterTest.toList(t);
+ assertEquals(10, rows.size());
+
+ for(Row r : rows) {
+ r.put("data", "final data " + r.get("id"));
+ }
+
+ for(Row r : rows) {
+ assertSame(r, t.updateRow(r));
+ }
+
+ t.reset();
+
+ for(Row r : t) {
+ assertEquals("final data " + r.get("id"), r.get("data"));
+ }
+
db.close();
}
}
return createTestRow("Tim");
}
+ static Map<String,Object> createTestRowMap(String col1Val) {
+ return createExpectedRow("A", col1Val, "B", "R", "C", "McCune",
+ "D", 1234, "E", (byte) 0xad, "F", 555.66d,
+ "G", 777.88f, "H", (short) 999, "I", new Date());
+ }
+
static void createTestTable(Database db) throws Exception {
new TableBuilder("test")
.addColumn(new ColumnBuilder("A", DataType.TEXT))