You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CursorImpl.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  1. /*
  2. Copyright (c) 2007 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess.impl;
  23. import java.io.IOException;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.Iterator;
  27. import java.util.Map;
  28. import java.util.NoSuchElementException;
  29. import com.healthmarketscience.jackcess.Column;
  30. import com.healthmarketscience.jackcess.Cursor;
  31. import com.healthmarketscience.jackcess.CursorBuilder;
  32. import com.healthmarketscience.jackcess.Row;
  33. import com.healthmarketscience.jackcess.RowId;
  34. import com.healthmarketscience.jackcess.RuntimeIOException;
  35. import com.healthmarketscience.jackcess.impl.TableImpl.RowState;
  36. import com.healthmarketscience.jackcess.util.ColumnMatcher;
  37. import com.healthmarketscience.jackcess.util.ErrorHandler;
  38. import com.healthmarketscience.jackcess.util.IterableBuilder;
  39. import com.healthmarketscience.jackcess.util.SimpleColumnMatcher;
  40. import org.apache.commons.logging.Log;
  41. import org.apache.commons.logging.LogFactory;
  42. /**
  43. * Manages iteration for a Table. Different cursors provide different methods
  44. * of traversing a table. Cursors should be fairly robust in the face of
  45. * table modification during traversal (although depending on how the table is
  46. * traversed, row updates may or may not be seen). Multiple cursors may
  47. * traverse the same table simultaneously.
  48. * <p>
  49. * The Cursor provides a variety of static utility methods to construct
  50. * cursors with given characteristics or easily search for specific values.
  51. * For even friendlier and more flexible construction, see
  52. * {@link CursorBuilder}.
  53. * <p>
  54. * Is not thread-safe.
  55. *
  56. * @author James Ahlborn
  57. */
  58. public abstract class CursorImpl implements Cursor
  59. {
  60. private static final Log LOG = LogFactory.getLog(CursorImpl.class);
  61. /** boolean value indicating forward movement */
  62. public static final boolean MOVE_FORWARD = true;
  63. /** boolean value indicating reverse movement */
  64. public static final boolean MOVE_REVERSE = false;
  65. /** identifier for this cursor */
  66. private final IdImpl _id;
  67. /** owning table */
  68. private final TableImpl _table;
  69. /** State used for reading the table rows */
  70. private final RowState _rowState;
  71. /** the first (exclusive) row id for this cursor */
  72. private final PositionImpl _firstPos;
  73. /** the last (exclusive) row id for this cursor */
  74. private final PositionImpl _lastPos;
  75. /** the previous row */
  76. protected PositionImpl _prevPos;
  77. /** the current row */
  78. protected PositionImpl _curPos;
  79. /** ColumnMatcher to be used when matching column values */
  80. protected ColumnMatcher _columnMatcher = SimpleColumnMatcher.INSTANCE;
  81. protected CursorImpl(IdImpl id, TableImpl table, PositionImpl firstPos,
  82. PositionImpl lastPos) {
  83. _id = id;
  84. _table = table;
  85. _rowState = _table.createRowState();
  86. _firstPos = firstPos;
  87. _lastPos = lastPos;
  88. _curPos = firstPos;
  89. _prevPos = firstPos;
  90. }
  91. /**
  92. * Creates a normal, un-indexed cursor for the given table.
  93. * @param table the table over which this cursor will traverse
  94. */
  95. public static CursorImpl createCursor(TableImpl table) {
  96. return new TableScanCursor(table);
  97. }
  98. public RowState getRowState() {
  99. return _rowState;
  100. }
  101. public IdImpl getId() {
  102. return _id;
  103. }
  104. public TableImpl getTable() {
  105. return _table;
  106. }
  107. public JetFormat getFormat() {
  108. return getTable().getFormat();
  109. }
  110. public PageChannel getPageChannel() {
  111. return getTable().getPageChannel();
  112. }
  113. public ErrorHandler getErrorHandler() {
  114. return _rowState.getErrorHandler();
  115. }
  116. public void setErrorHandler(ErrorHandler newErrorHandler) {
  117. _rowState.setErrorHandler(newErrorHandler);
  118. }
  119. public ColumnMatcher getColumnMatcher() {
  120. return _columnMatcher;
  121. }
  122. public void setColumnMatcher(ColumnMatcher columnMatcher) {
  123. if(columnMatcher == null) {
  124. columnMatcher = getDefaultColumnMatcher();
  125. }
  126. _columnMatcher = columnMatcher;
  127. }
  128. /**
  129. * Returns the default ColumnMatcher for this Cursor.
  130. */
  131. protected ColumnMatcher getDefaultColumnMatcher() {
  132. return SimpleColumnMatcher.INSTANCE;
  133. }
  134. public SavepointImpl getSavepoint() {
  135. return new SavepointImpl(_id, _curPos, _prevPos);
  136. }
  137. public void restoreSavepoint(Savepoint savepoint)
  138. throws IOException
  139. {
  140. restoreSavepoint((SavepointImpl)savepoint);
  141. }
  142. public void restoreSavepoint(SavepointImpl savepoint)
  143. throws IOException
  144. {
  145. if(!_id.equals(savepoint.getCursorId())) {
  146. throw new IllegalArgumentException(
  147. "Savepoint " + savepoint + " is not valid for this cursor with id "
  148. + _id);
  149. }
  150. restorePosition(savepoint.getCurrentPosition(),
  151. savepoint.getPreviousPosition());
  152. }
  153. /**
  154. * Returns the first row id (exclusive) as defined by this cursor.
  155. */
  156. protected PositionImpl getFirstPosition() {
  157. return _firstPos;
  158. }
  159. /**
  160. * Returns the last row id (exclusive) as defined by this cursor.
  161. */
  162. protected PositionImpl getLastPosition() {
  163. return _lastPos;
  164. }
  165. public void reset() {
  166. beforeFirst();
  167. }
  168. public void beforeFirst() {
  169. reset(MOVE_FORWARD);
  170. }
  171. public void afterLast() {
  172. reset(MOVE_REVERSE);
  173. }
  174. public boolean isBeforeFirst() throws IOException {
  175. return isAtBeginning(MOVE_FORWARD);
  176. }
  177. public boolean isAfterLast() throws IOException {
  178. return isAtBeginning(MOVE_REVERSE);
  179. }
  180. protected boolean isAtBeginning(boolean moveForward) throws IOException {
  181. if(getDirHandler(moveForward).getBeginningPosition().equals(_curPos)) {
  182. return !recheckPosition(!moveForward);
  183. }
  184. return false;
  185. }
  186. public boolean isCurrentRowDeleted() throws IOException
  187. {
  188. // we need to ensure that the "deleted" flag has been read for this row
  189. // (or re-read if the table has been recently modified)
  190. TableImpl.positionAtRowData(_rowState, _curPos.getRowId());
  191. return _rowState.isDeleted();
  192. }
  193. /**
  194. * Resets this cursor for traversing the given direction.
  195. */
  196. protected void reset(boolean moveForward) {
  197. _curPos = getDirHandler(moveForward).getBeginningPosition();
  198. _prevPos = _curPos;
  199. _rowState.reset();
  200. }
  201. public Iterator<Row> iterator() {
  202. return new RowIterator(null, true, MOVE_FORWARD);
  203. }
  204. public IterableBuilder newIterable() {
  205. return new IterableBuilder(this);
  206. }
  207. public Iterator<Row> iterator(IterableBuilder iterBuilder) {
  208. switch(iterBuilder.getType()) {
  209. case SIMPLE:
  210. return new RowIterator(iterBuilder.getColumnNames(),
  211. iterBuilder.isReset(), iterBuilder.isForward());
  212. case COLUMN_MATCH: {
  213. @SuppressWarnings("unchecked")
  214. Map.Entry<Column,Object> matchPattern = (Map.Entry<Column,Object>)
  215. iterBuilder.getMatchPattern();
  216. return new ColumnMatchIterator(
  217. iterBuilder.getColumnNames(), (ColumnImpl)matchPattern.getKey(),
  218. matchPattern.getValue(), iterBuilder.isReset(),
  219. iterBuilder.isForward(), iterBuilder.getColumnMatcher());
  220. }
  221. case ROW_MATCH: {
  222. @SuppressWarnings("unchecked")
  223. Map<String,?> matchPattern = (Map<String,?>)
  224. iterBuilder.getMatchPattern();
  225. return new RowMatchIterator(
  226. iterBuilder.getColumnNames(), matchPattern,iterBuilder.isReset(),
  227. iterBuilder.isForward(), iterBuilder.getColumnMatcher());
  228. }
  229. default:
  230. throw new RuntimeException("unknown match type " + iterBuilder.getType());
  231. }
  232. }
  233. public void deleteCurrentRow() throws IOException {
  234. _table.deleteRow(_rowState, _curPos.getRowId());
  235. }
  236. public Object[] updateCurrentRow(Object... row) throws IOException {
  237. return _table.updateRow(_rowState, _curPos.getRowId(), row);
  238. }
  239. public <M extends Map<String,Object>> M updateCurrentRowFromMap(M row)
  240. throws IOException
  241. {
  242. return _table.updateRowFromMap(_rowState, _curPos.getRowId(), row);
  243. }
  244. public Row getNextRow() throws IOException {
  245. return getNextRow(null);
  246. }
  247. public Row getNextRow(Collection<String> columnNames)
  248. throws IOException
  249. {
  250. return getAnotherRow(columnNames, MOVE_FORWARD);
  251. }
  252. public Row getPreviousRow() throws IOException {
  253. return getPreviousRow(null);
  254. }
  255. public Row getPreviousRow(Collection<String> columnNames)
  256. throws IOException
  257. {
  258. return getAnotherRow(columnNames, MOVE_REVERSE);
  259. }
  260. /**
  261. * Moves to another row in the table based on the given direction and
  262. * returns it.
  263. * @param columnNames Only column names in this collection will be returned
  264. * @return another row in this table (Column name -> Column value), where
  265. * "next" may be backwards if moveForward is {@code false}, or
  266. * {@code null} if there is not another row in the given direction.
  267. */
  268. private Row getAnotherRow(Collection<String> columnNames,
  269. boolean moveForward)
  270. throws IOException
  271. {
  272. if(moveToAnotherRow(moveForward)) {
  273. return getCurrentRow(columnNames);
  274. }
  275. return null;
  276. }
  277. public boolean moveToNextRow() throws IOException
  278. {
  279. return moveToAnotherRow(MOVE_FORWARD);
  280. }
  281. public boolean moveToPreviousRow() throws IOException
  282. {
  283. return moveToAnotherRow(MOVE_REVERSE);
  284. }
  285. /**
  286. * Moves to another row in the given direction as defined by this cursor.
  287. * @return {@code true} if another valid row was found in the given
  288. * direction, {@code false} otherwise
  289. */
  290. protected boolean moveToAnotherRow(boolean moveForward)
  291. throws IOException
  292. {
  293. if(_curPos.equals(getDirHandler(moveForward).getEndPosition())) {
  294. // already at end, make sure nothing has changed
  295. return recheckPosition(moveForward);
  296. }
  297. return moveToAnotherRowImpl(moveForward);
  298. }
  299. /**
  300. * Restores a current position for the cursor (current position becomes
  301. * previous position).
  302. */
  303. protected void restorePosition(PositionImpl curPos)
  304. throws IOException
  305. {
  306. restorePosition(curPos, _curPos);
  307. }
  308. /**
  309. * Restores a current and previous position for the cursor if the given
  310. * positions are different from the current positions.
  311. */
  312. protected final void restorePosition(PositionImpl curPos,
  313. PositionImpl prevPos)
  314. throws IOException
  315. {
  316. if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
  317. restorePositionImpl(curPos, prevPos);
  318. }
  319. }
  320. /**
  321. * Restores a current and previous position for the cursor.
  322. */
  323. protected void restorePositionImpl(PositionImpl curPos, PositionImpl prevPos)
  324. throws IOException
  325. {
  326. // make the current position previous, and the new position current
  327. _prevPos = _curPos;
  328. _curPos = curPos;
  329. _rowState.reset();
  330. }
  331. /**
  332. * Rechecks the current position if the underlying data structures have been
  333. * modified.
  334. * @return {@code true} if the cursor ended up in a new position,
  335. * {@code false} otherwise.
  336. */
  337. private boolean recheckPosition(boolean moveForward)
  338. throws IOException
  339. {
  340. if(isUpToDate()) {
  341. // nothing has changed
  342. return false;
  343. }
  344. // move the cursor back to the previous position
  345. restorePosition(_prevPos);
  346. return moveToAnotherRowImpl(moveForward);
  347. }
  348. /**
  349. * Does the grunt work of moving the cursor to another position in the given
  350. * direction.
  351. */
  352. private boolean moveToAnotherRowImpl(boolean moveForward)
  353. throws IOException
  354. {
  355. _rowState.reset();
  356. _prevPos = _curPos;
  357. _curPos = findAnotherPosition(_rowState, _curPos, moveForward);
  358. TableImpl.positionAtRowHeader(_rowState, _curPos.getRowId());
  359. return(!_curPos.equals(getDirHandler(moveForward).getEndPosition()));
  360. }
  361. public boolean findRow(RowId rowId) throws IOException
  362. {
  363. RowIdImpl rowIdImpl = (RowIdImpl)rowId;
  364. PositionImpl curPos = _curPos;
  365. PositionImpl prevPos = _prevPos;
  366. boolean found = false;
  367. try {
  368. reset(MOVE_FORWARD);
  369. if(TableImpl.positionAtRowHeader(_rowState, rowIdImpl) == null) {
  370. return false;
  371. }
  372. restorePosition(getRowPosition(rowIdImpl));
  373. if(!isCurrentRowValid()) {
  374. return false;
  375. }
  376. found = true;
  377. return true;
  378. } finally {
  379. if(!found) {
  380. try {
  381. restorePosition(curPos, prevPos);
  382. } catch(IOException e) {
  383. LOG.error("Failed restoring position", e);
  384. }
  385. }
  386. }
  387. }
  388. public boolean findFirstRow(Column columnPattern, Object valuePattern)
  389. throws IOException
  390. {
  391. return findFirstRow((ColumnImpl)columnPattern, valuePattern);
  392. }
  393. public boolean findFirstRow(ColumnImpl columnPattern, Object valuePattern)
  394. throws IOException
  395. {
  396. return findAnotherRow(columnPattern, valuePattern, true, MOVE_FORWARD,
  397. _columnMatcher);
  398. }
  399. public boolean findNextRow(Column columnPattern, Object valuePattern)
  400. throws IOException
  401. {
  402. return findNextRow((ColumnImpl)columnPattern, valuePattern);
  403. }
  404. public boolean findNextRow(ColumnImpl columnPattern, Object valuePattern)
  405. throws IOException
  406. {
  407. return findAnotherRow(columnPattern, valuePattern, false, MOVE_FORWARD,
  408. _columnMatcher);
  409. }
  410. protected boolean findAnotherRow(ColumnImpl columnPattern, Object valuePattern,
  411. boolean reset, boolean moveForward,
  412. ColumnMatcher columnMatcher)
  413. throws IOException
  414. {
  415. PositionImpl curPos = _curPos;
  416. PositionImpl prevPos = _prevPos;
  417. boolean found = false;
  418. try {
  419. if(reset) {
  420. reset(moveForward);
  421. }
  422. found = findAnotherRowImpl(columnPattern, valuePattern, moveForward,
  423. columnMatcher);
  424. return found;
  425. } finally {
  426. if(!found) {
  427. try {
  428. restorePosition(curPos, prevPos);
  429. } catch(IOException e) {
  430. LOG.error("Failed restoring position", e);
  431. }
  432. }
  433. }
  434. }
  435. public boolean findFirstRow(Map<String,?> rowPattern) throws IOException
  436. {
  437. return findAnotherRow(rowPattern, true, MOVE_FORWARD, _columnMatcher);
  438. }
  439. public boolean findNextRow(Map<String,?> rowPattern)
  440. throws IOException
  441. {
  442. return findAnotherRow(rowPattern, false, MOVE_FORWARD, _columnMatcher);
  443. }
  444. protected boolean findAnotherRow(Map<String,?> rowPattern, boolean reset,
  445. boolean moveForward,
  446. ColumnMatcher columnMatcher)
  447. throws IOException
  448. {
  449. PositionImpl curPos = _curPos;
  450. PositionImpl prevPos = _prevPos;
  451. boolean found = false;
  452. try {
  453. if(reset) {
  454. reset(moveForward);
  455. }
  456. found = findAnotherRowImpl(rowPattern, moveForward, columnMatcher);
  457. return found;
  458. } finally {
  459. if(!found) {
  460. try {
  461. restorePosition(curPos, prevPos);
  462. } catch(IOException e) {
  463. LOG.error("Failed restoring position", e);
  464. }
  465. }
  466. }
  467. }
  468. public boolean currentRowMatches(Column columnPattern, Object valuePattern)
  469. throws IOException
  470. {
  471. return currentRowMatches((ColumnImpl)columnPattern, valuePattern);
  472. }
  473. public boolean currentRowMatches(ColumnImpl columnPattern, Object valuePattern)
  474. throws IOException
  475. {
  476. return currentRowMatchesImpl(columnPattern, valuePattern, _columnMatcher);
  477. }
  478. protected boolean currentRowMatchesImpl(ColumnImpl columnPattern,
  479. Object valuePattern,
  480. ColumnMatcher columnMatcher)
  481. throws IOException
  482. {
  483. return columnMatcher.matches(getTable(), columnPattern.getName(),
  484. valuePattern,
  485. getCurrentRowValue(columnPattern));
  486. }
  487. public boolean currentRowMatches(Map<String,?> rowPattern)
  488. throws IOException
  489. {
  490. return currentRowMatchesImpl(rowPattern, _columnMatcher);
  491. }
  492. protected boolean currentRowMatchesImpl(Map<String,?> rowPattern,
  493. ColumnMatcher columnMatcher)
  494. throws IOException
  495. {
  496. Row row = getCurrentRow(rowPattern.keySet());
  497. if(rowPattern.size() != row.size()) {
  498. return false;
  499. }
  500. for(Map.Entry<String,Object> e : row.entrySet()) {
  501. String columnName = e.getKey();
  502. if(!columnMatcher.matches(getTable(), columnName,
  503. rowPattern.get(columnName), e.getValue())) {
  504. return false;
  505. }
  506. }
  507. return true;
  508. }
  509. /**
  510. * Moves to the next row (as defined by the cursor) where the given column
  511. * has the given value. Caller manages save/restore on failure.
  512. * <p>
  513. * Default implementation scans the table from beginning to end.
  514. *
  515. * @param columnPattern column from the table for this cursor which is being
  516. * matched by the valuePattern
  517. * @param valuePattern value which is equal to the corresponding value in
  518. * the matched row
  519. * @return {@code true} if a valid row was found with the given value,
  520. * {@code false} if no row was found
  521. */
  522. protected boolean findAnotherRowImpl(
  523. ColumnImpl columnPattern, Object valuePattern, boolean moveForward,
  524. ColumnMatcher columnMatcher)
  525. throws IOException
  526. {
  527. while(moveToAnotherRow(moveForward)) {
  528. if(currentRowMatchesImpl(columnPattern, valuePattern, columnMatcher)) {
  529. return true;
  530. }
  531. }
  532. return false;
  533. }
  534. /**
  535. * Moves to the next row (as defined by the cursor) where the given columns
  536. * have the given values. Caller manages save/restore on failure.
  537. * <p>
  538. * Default implementation scans the table from beginning to end.
  539. *
  540. * @param rowPattern column names and values which must be equal to the
  541. * corresponding values in the matched row
  542. * @return {@code true} if a valid row was found with the given values,
  543. * {@code false} if no row was found
  544. */
  545. protected boolean findAnotherRowImpl(Map<String,?> rowPattern,
  546. boolean moveForward,
  547. ColumnMatcher columnMatcher)
  548. throws IOException
  549. {
  550. while(moveToAnotherRow(moveForward)) {
  551. if(currentRowMatchesImpl(rowPattern, columnMatcher)) {
  552. return true;
  553. }
  554. }
  555. return false;
  556. }
  557. public int moveNextRows(int numRows) throws IOException
  558. {
  559. return moveSomeRows(numRows, MOVE_FORWARD);
  560. }
  561. public int movePreviousRows(int numRows) throws IOException
  562. {
  563. return moveSomeRows(numRows, MOVE_REVERSE);
  564. }
  565. /**
  566. * Moves as many rows as possible in the given direction up to the given
  567. * number of rows.
  568. * @return the number of rows moved.
  569. */
  570. private int moveSomeRows(int numRows, boolean moveForward)
  571. throws IOException
  572. {
  573. int numMovedRows = 0;
  574. while((numMovedRows < numRows) && moveToAnotherRow(moveForward)) {
  575. ++numMovedRows;
  576. }
  577. return numMovedRows;
  578. }
  579. public Row getCurrentRow() throws IOException
  580. {
  581. return getCurrentRow(null);
  582. }
  583. public Row getCurrentRow(Collection<String> columnNames)
  584. throws IOException
  585. {
  586. return _table.getRow(_rowState, _curPos.getRowId(), columnNames);
  587. }
  588. public Object getCurrentRowValue(Column column)
  589. throws IOException
  590. {
  591. return getCurrentRowValue((ColumnImpl)column);
  592. }
  593. public Object getCurrentRowValue(ColumnImpl column)
  594. throws IOException
  595. {
  596. return _table.getRowValue(_rowState, _curPos.getRowId(), column);
  597. }
  598. public void setCurrentRowValue(Column column, Object value)
  599. throws IOException
  600. {
  601. setCurrentRowValue((ColumnImpl)column, value);
  602. }
  603. public void setCurrentRowValue(ColumnImpl column, Object value)
  604. throws IOException
  605. {
  606. Object[] row = new Object[_table.getColumnCount()];
  607. Arrays.fill(row, Column.KEEP_VALUE);
  608. column.setRowValue(row, value);
  609. _table.updateRow(_rowState, _curPos.getRowId(), row);
  610. }
  611. /**
  612. * Returns {@code true} if this cursor is up-to-date with respect to the
  613. * relevant table and related table objects, {@code false} otherwise.
  614. */
  615. protected boolean isUpToDate() {
  616. return _rowState.isUpToDate();
  617. }
  618. /**
  619. * Returns {@code true} of the current row is valid, {@code false} otherwise.
  620. */
  621. protected boolean isCurrentRowValid() throws IOException {
  622. return(_curPos.getRowId().isValid() && !isCurrentRowDeleted() &&
  623. !isBeforeFirst() && !isAfterLast());
  624. }
  625. @Override
  626. public String toString() {
  627. return getClass().getSimpleName() + " CurPosition " + _curPos +
  628. ", PrevPosition " + _prevPos;
  629. }
  630. /**
  631. * Returns the appropriate position information for the given row (which is
  632. * the current row and is valid).
  633. */
  634. protected abstract PositionImpl getRowPosition(RowIdImpl rowId)
  635. throws IOException;
  636. /**
  637. * Finds the next non-deleted row after the given row (as defined by this
  638. * cursor) and returns the id of the row, where "next" may be backwards if
  639. * moveForward is {@code false}. If there are no more rows, the returned
  640. * rowId should equal the value returned by {@link #getLastPosition} if
  641. * moving forward and {@link #getFirstPosition} if moving backward.
  642. */
  643. protected abstract PositionImpl findAnotherPosition(RowState rowState,
  644. PositionImpl curPos,
  645. boolean moveForward)
  646. throws IOException;
  647. /**
  648. * Returns the DirHandler for the given movement direction.
  649. */
  650. protected abstract DirHandler getDirHandler(boolean moveForward);
  651. /**
  652. * Base implementation of iterator for this cursor, modifiable.
  653. */
  654. protected abstract class BaseIterator implements Iterator<Row>
  655. {
  656. protected final Collection<String> _columnNames;
  657. protected final boolean _moveForward;
  658. protected final ColumnMatcher _colMatcher;
  659. protected Boolean _hasNext;
  660. protected boolean _validRow;
  661. protected BaseIterator(Collection<String> columnNames,
  662. boolean reset, boolean moveForward,
  663. ColumnMatcher columnMatcher)
  664. {
  665. _columnNames = columnNames;
  666. _moveForward = moveForward;
  667. _colMatcher = ((columnMatcher != null) ? columnMatcher : _columnMatcher);
  668. try {
  669. if(reset) {
  670. reset(_moveForward);
  671. } else if(isCurrentRowValid()) {
  672. _hasNext = _validRow = true;
  673. }
  674. } catch(IOException e) {
  675. throw new RuntimeIOException(e);
  676. }
  677. }
  678. public boolean hasNext() {
  679. if(_hasNext == null) {
  680. try {
  681. _hasNext = findNext();
  682. _validRow = _hasNext;
  683. } catch(IOException e) {
  684. throw new RuntimeIOException(e);
  685. }
  686. }
  687. return _hasNext;
  688. }
  689. public Row next() {
  690. if(!hasNext()) {
  691. throw new NoSuchElementException();
  692. }
  693. try {
  694. Row rtn = getCurrentRow(_columnNames);
  695. _hasNext = null;
  696. return rtn;
  697. } catch(IOException e) {
  698. throw new RuntimeIOException(e);
  699. }
  700. }
  701. public void remove() {
  702. if(_validRow) {
  703. try {
  704. deleteCurrentRow();
  705. _validRow = false;
  706. } catch(IOException e) {
  707. throw new RuntimeIOException(e);
  708. }
  709. } else {
  710. throw new IllegalStateException("Not at valid row");
  711. }
  712. }
  713. protected abstract boolean findNext() throws IOException;
  714. }
  715. /**
  716. * Row iterator for this cursor, modifiable.
  717. */
  718. private final class RowIterator extends BaseIterator
  719. {
  720. private RowIterator(Collection<String> columnNames, boolean reset,
  721. boolean moveForward)
  722. {
  723. super(columnNames, reset, moveForward, null);
  724. }
  725. @Override
  726. protected boolean findNext() throws IOException {
  727. return moveToAnotherRow(_moveForward);
  728. }
  729. }
  730. /**
  731. * Row iterator for this cursor, modifiable.
  732. */
  733. private final class ColumnMatchIterator extends BaseIterator
  734. {
  735. private final ColumnImpl _columnPattern;
  736. private final Object _valuePattern;
  737. private ColumnMatchIterator(Collection<String> columnNames,
  738. ColumnImpl columnPattern, Object valuePattern,
  739. boolean reset, boolean moveForward,
  740. ColumnMatcher columnMatcher)
  741. {
  742. super(columnNames, reset, moveForward, columnMatcher);
  743. _columnPattern = columnPattern;
  744. _valuePattern = valuePattern;
  745. }
  746. @Override
  747. protected boolean findNext() throws IOException {
  748. return findAnotherRow(_columnPattern, _valuePattern, false, _moveForward,
  749. _colMatcher);
  750. }
  751. }
  752. /**
  753. * Row iterator for this cursor, modifiable.
  754. */
  755. private final class RowMatchIterator extends BaseIterator
  756. {
  757. private final Map<String,?> _rowPattern;
  758. private RowMatchIterator(Collection<String> columnNames,
  759. Map<String,?> rowPattern,
  760. boolean reset, boolean moveForward,
  761. ColumnMatcher columnMatcher)
  762. {
  763. super(columnNames, reset, moveForward, columnMatcher);
  764. _rowPattern = rowPattern;
  765. }
  766. @Override
  767. protected boolean findNext() throws IOException {
  768. return findAnotherRow(_rowPattern, false, _moveForward, _colMatcher);
  769. }
  770. }
  771. /**
  772. * Handles moving the cursor in a given direction. Separates cursor
  773. * logic from value storage.
  774. */
  775. protected abstract class DirHandler
  776. {
  777. public abstract PositionImpl getBeginningPosition();
  778. public abstract PositionImpl getEndPosition();
  779. }
  780. /**
  781. * Identifier for a cursor. Will be equal to any other cursor of the same
  782. * type for the same table. Primarily used to check the validity of a
  783. * Savepoint.
  784. */
  785. protected static final class IdImpl implements Id
  786. {
  787. private final int _tablePageNumber;
  788. private final int _indexNumber;
  789. protected IdImpl(TableImpl table, IndexImpl index) {
  790. _tablePageNumber = table.getTableDefPageNumber();
  791. _indexNumber = ((index != null) ? index.getIndexNumber() : -1);
  792. }
  793. @Override
  794. public int hashCode() {
  795. return _tablePageNumber;
  796. }
  797. @Override
  798. public boolean equals(Object o) {
  799. return((this == o) ||
  800. ((o != null) && (getClass() == o.getClass()) &&
  801. (_tablePageNumber == ((IdImpl)o)._tablePageNumber) &&
  802. (_indexNumber == ((IdImpl)o)._indexNumber)));
  803. }
  804. @Override
  805. public String toString() {
  806. return getClass().getSimpleName() + " " + _tablePageNumber + ":" + _indexNumber;
  807. }
  808. }
  809. /**
  810. * Value object which maintains the current position of the cursor.
  811. */
  812. protected static abstract class PositionImpl implements Position
  813. {
  814. protected PositionImpl() {
  815. }
  816. @Override
  817. public final int hashCode() {
  818. return getRowId().hashCode();
  819. }
  820. @Override
  821. public final boolean equals(Object o) {
  822. return((this == o) ||
  823. ((o != null) && (getClass() == o.getClass()) && equalsImpl(o)));
  824. }
  825. /**
  826. * Returns the unique RowId of the position of the cursor.
  827. */
  828. public abstract RowIdImpl getRowId();
  829. /**
  830. * Returns {@code true} if the subclass specific info in a Position is
  831. * equal, {@code false} otherwise.
  832. * @param o object being tested for equality, guaranteed to be the same
  833. * class as this object
  834. */
  835. protected abstract boolean equalsImpl(Object o);
  836. }
  837. /**
  838. * Value object which represents a complete save state of the cursor.
  839. */
  840. protected static final class SavepointImpl implements Savepoint
  841. {
  842. private final IdImpl _cursorId;
  843. private final PositionImpl _curPos;
  844. private final PositionImpl _prevPos;
  845. private SavepointImpl(IdImpl cursorId, PositionImpl curPos,
  846. PositionImpl prevPos) {
  847. _cursorId = cursorId;
  848. _curPos = curPos;
  849. _prevPos = prevPos;
  850. }
  851. public IdImpl getCursorId() {
  852. return _cursorId;
  853. }
  854. public PositionImpl getCurrentPosition() {
  855. return _curPos;
  856. }
  857. private PositionImpl getPreviousPosition() {
  858. return _prevPos;
  859. }
  860. @Override
  861. public String toString() {
  862. return getClass().getSimpleName() + " " + _cursorId + " CurPosition " +
  863. _curPos + ", PrevPosition " + _prevPos;
  864. }
  865. }
  866. }