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.

Cursor.java 51KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607
  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;
  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.Table.RowState;
  30. import org.apache.commons.lang.ObjectUtils;
  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogFactory;
  33. /**
  34. * Manages iteration for a Table. Different cursors provide different methods
  35. * of traversing a table. Cursors should be fairly robust in the face of
  36. * table modification during traversal (although depending on how the table is
  37. * traversed, row updates may or may not be seen). Multiple cursors may
  38. * traverse the same table simultaneously.
  39. * <p>
  40. * The Cursor provides a variety of static utility methods to construct
  41. * cursors with given characteristics or easily search for specific values.
  42. * For even friendlier and more flexible construction, see
  43. * {@link CursorBuilder}.
  44. * <p>
  45. * Is not thread-safe.
  46. *
  47. * @author James Ahlborn
  48. */
  49. public abstract class Cursor implements Iterable<Map<String, Object>>
  50. {
  51. private static final Log LOG = LogFactory.getLog(Cursor.class);
  52. /** boolean value indicating forward movement */
  53. public static final boolean MOVE_FORWARD = true;
  54. /** boolean value indicating reverse movement */
  55. public static final boolean MOVE_REVERSE = false;
  56. /** first position for the TableScanCursor */
  57. private static final ScanPosition FIRST_SCAN_POSITION =
  58. new ScanPosition(RowId.FIRST_ROW_ID);
  59. /** last position for the TableScanCursor */
  60. private static final ScanPosition LAST_SCAN_POSITION =
  61. new ScanPosition(RowId.LAST_ROW_ID);
  62. /** identifier for this cursor */
  63. private final Id _id;
  64. /** owning table */
  65. private final Table _table;
  66. /** State used for reading the table rows */
  67. private final RowState _rowState;
  68. /** the first (exclusive) row id for this cursor */
  69. private final Position _firstPos;
  70. /** the last (exclusive) row id for this cursor */
  71. private final Position _lastPos;
  72. /** the previous row */
  73. protected Position _prevPos;
  74. /** the current row */
  75. protected Position _curPos;
  76. /** ColumnMatcher to be used when matching column values */
  77. protected ColumnMatcher _columnMatcher = SimpleColumnMatcher.INSTANCE;
  78. protected Cursor(Id id, Table table, Position firstPos, Position lastPos) {
  79. _id = id;
  80. _table = table;
  81. _rowState = _table.createRowState();
  82. _firstPos = firstPos;
  83. _lastPos = lastPos;
  84. _curPos = firstPos;
  85. _prevPos = firstPos;
  86. }
  87. /**
  88. * Creates a normal, un-indexed cursor for the given table.
  89. * @param table the table over which this cursor will traverse
  90. */
  91. public static Cursor createCursor(Table table) {
  92. return new TableScanCursor(table);
  93. }
  94. /**
  95. * Creates an indexed cursor for the given table.
  96. * <p>
  97. * Note, index based table traversal may not include all rows, as certain
  98. * types of indexes do not include all entries (namely, some indexes ignore
  99. * null entries, see {@link Index#shouldIgnoreNulls}).
  100. *
  101. * @param table the table over which this cursor will traverse
  102. * @param index index for the table which will define traversal order as
  103. * well as enhance certain lookups
  104. */
  105. public static Cursor createIndexCursor(Table table, Index index)
  106. throws IOException
  107. {
  108. return IndexCursor.createCursor(table, index);
  109. }
  110. /**
  111. * Creates an indexed cursor for the given table, narrowed to the given
  112. * range.
  113. * <p>
  114. * Note, index based table traversal may not include all rows, as certain
  115. * types of indexes do not include all entries (namely, some indexes ignore
  116. * null entries, see {@link Index#shouldIgnoreNulls}).
  117. *
  118. * @param table the table over which this cursor will traverse
  119. * @param index index for the table which will define traversal order as
  120. * well as enhance certain lookups
  121. * @param startRow the first row of data for the cursor (inclusive), or
  122. * {@code null} for the first entry
  123. * @param endRow the last row of data for the cursor (inclusive), or
  124. * {@code null} for the last entry
  125. */
  126. public static Cursor createIndexCursor(Table table, Index index,
  127. Object[] startRow, Object[] endRow)
  128. throws IOException
  129. {
  130. return IndexCursor.createCursor(table, index, startRow, endRow);
  131. }
  132. /**
  133. * Creates an indexed cursor for the given table, narrowed to the given
  134. * range.
  135. * <p>
  136. * Note, index based table traversal may not include all rows, as certain
  137. * types of indexes do not include all entries (namely, some indexes ignore
  138. * null entries, see {@link Index#shouldIgnoreNulls}).
  139. *
  140. * @param table the table over which this cursor will traverse
  141. * @param index index for the table which will define traversal order as
  142. * well as enhance certain lookups
  143. * @param startRow the first row of data for the cursor, or {@code null} for
  144. * the first entry
  145. * @param startInclusive whether or not startRow is inclusive or exclusive
  146. * @param endRow the last row of data for the cursor, or {@code null} for
  147. * the last entry
  148. * @param endInclusive whether or not endRow is inclusive or exclusive
  149. */
  150. public static Cursor createIndexCursor(Table table, Index index,
  151. Object[] startRow,
  152. boolean startInclusive,
  153. Object[] endRow,
  154. boolean endInclusive)
  155. throws IOException
  156. {
  157. return IndexCursor.createCursor(table, index, startRow, startInclusive,
  158. endRow, endInclusive);
  159. }
  160. /**
  161. * Convenience method for finding a specific row in a table which matches a
  162. * given row "pattern". See {@link #findFirstRow(Map)} for details on the
  163. * rowPattern.
  164. * <p>
  165. * Warning, this method <i>always</i> starts searching from the beginning of
  166. * the Table (you cannot use it to find successive matches).
  167. *
  168. * @param table the table to search
  169. * @param rowPattern pattern to be used to find the row
  170. * @return the matching row or {@code null} if a match could not be found.
  171. */
  172. public static Map<String,Object> findRow(Table table,
  173. Map<String,?> rowPattern)
  174. throws IOException
  175. {
  176. Cursor cursor = createCursor(table);
  177. if(cursor.findFirstRow(rowPattern)) {
  178. return cursor.getCurrentRow();
  179. }
  180. return null;
  181. }
  182. /**
  183. * Convenience method for finding a specific row in a table which matches a
  184. * given row "pattern". See {@link #findFirstRow(Column,Object)} for
  185. * details on the pattern.
  186. * <p>
  187. * Note, a {@code null} result value is ambiguous in that it could imply no
  188. * match or a matching row with {@code null} for the desired value. If
  189. * distinguishing this situation is important, you will need to use a Cursor
  190. * directly instead of this convenience method.
  191. *
  192. * @param table the table to search
  193. * @param column column whose value should be returned
  194. * @param columnPattern column being matched by the valuePattern
  195. * @param valuePattern value from the columnPattern which will match the
  196. * desired row
  197. * @return the matching row or {@code null} if a match could not be found.
  198. */
  199. public static Object findValue(Table table, Column column,
  200. Column columnPattern, Object valuePattern)
  201. throws IOException
  202. {
  203. Cursor cursor = createCursor(table);
  204. if(cursor.findFirstRow(columnPattern, valuePattern)) {
  205. return cursor.getCurrentRowValue(column);
  206. }
  207. return null;
  208. }
  209. /**
  210. * Convenience method for finding a specific row in an indexed table which
  211. * matches a given row "pattern". See {@link #findFirstRow(Map)} for
  212. * details on the rowPattern.
  213. * <p>
  214. * Warning, this method <i>always</i> starts searching from the beginning of
  215. * the Table (you cannot use it to find successive matches).
  216. *
  217. * @param table the table to search
  218. * @param index index to assist the search
  219. * @param rowPattern pattern to be used to find the row
  220. * @return the matching row or {@code null} if a match could not be found.
  221. */
  222. public static Map<String,Object> findRow(Table table, Index index,
  223. Map<String,?> rowPattern)
  224. throws IOException
  225. {
  226. Cursor cursor = createIndexCursor(table, index);
  227. if(cursor.findFirstRow(rowPattern)) {
  228. return cursor.getCurrentRow();
  229. }
  230. return null;
  231. }
  232. /**
  233. * Convenience method for finding a specific row in a table which matches a
  234. * given row "pattern". See {@link #findFirstRow(Column,Object)} for
  235. * details on the pattern.
  236. * <p>
  237. * Note, a {@code null} result value is ambiguous in that it could imply no
  238. * match or a matching row with {@code null} for the desired value. If
  239. * distinguishing this situation is important, you will need to use a Cursor
  240. * directly instead of this convenience method.
  241. *
  242. * @param table the table to search
  243. * @param index index to assist the search
  244. * @param column column whose value should be returned
  245. * @param columnPattern column being matched by the valuePattern
  246. * @param valuePattern value from the columnPattern which will match the
  247. * desired row
  248. * @return the matching row or {@code null} if a match could not be found.
  249. */
  250. public static Object findValue(Table table, Index index, Column column,
  251. Column columnPattern, Object valuePattern)
  252. throws IOException
  253. {
  254. Cursor cursor = createIndexCursor(table, index);
  255. if(cursor.findFirstRow(columnPattern, valuePattern)) {
  256. return cursor.getCurrentRowValue(column);
  257. }
  258. return null;
  259. }
  260. public Id getId() {
  261. return _id;
  262. }
  263. public Table getTable() {
  264. return _table;
  265. }
  266. public JetFormat getFormat() {
  267. return getTable().getFormat();
  268. }
  269. public PageChannel getPageChannel() {
  270. return getTable().getPageChannel();
  271. }
  272. /**
  273. * Gets the currently configured ErrorHandler (always non-{@code null}).
  274. * This will be used to handle all errors.
  275. */
  276. public ErrorHandler getErrorHandler() {
  277. return _rowState.getErrorHandler();
  278. }
  279. /**
  280. * Sets a new ErrorHandler. If {@code null}, resets to using the
  281. * ErrorHandler configured at the Table level.
  282. */
  283. public void setErrorHandler(ErrorHandler newErrorHandler) {
  284. _rowState.setErrorHandler(newErrorHandler);
  285. }
  286. /**
  287. * Returns the currently configured ColumnMatcher, always non-{@code null}.
  288. */
  289. public ColumnMatcher getColumnMatcher() {
  290. return _columnMatcher;
  291. }
  292. /**
  293. * Sets a new ColumnMatcher. If {@code null}, resets to using the
  294. * default matcher, {@link SimpleColumnMatcher#INSTANCE}.
  295. */
  296. public void setColumnMatcher(ColumnMatcher columnMatcher) {
  297. if(columnMatcher == null) {
  298. columnMatcher = getDefaultColumnMatcher();
  299. }
  300. _columnMatcher = columnMatcher;
  301. }
  302. /**
  303. * Returns the default ColumnMatcher for this Cursor.
  304. */
  305. protected ColumnMatcher getDefaultColumnMatcher() {
  306. return SimpleColumnMatcher.INSTANCE;
  307. }
  308. /**
  309. * Returns the current state of the cursor which can be restored at a future
  310. * point in time by a call to {@link #restoreSavepoint}.
  311. * <p>
  312. * Savepoints may be used across different cursor instances for the same
  313. * table, but they must have the same {@link Id}.
  314. */
  315. public Savepoint getSavepoint() {
  316. return new Savepoint(_id, _curPos, _prevPos);
  317. }
  318. /**
  319. * Moves the cursor to a savepoint previously returned from
  320. * {@link #getSavepoint}.
  321. * @throws IllegalArgumentException if the given savepoint does not have a
  322. * cursorId equal to this cursor's id
  323. */
  324. public void restoreSavepoint(Savepoint savepoint)
  325. throws IOException
  326. {
  327. if(!_id.equals(savepoint.getCursorId())) {
  328. throw new IllegalArgumentException(
  329. "Savepoint " + savepoint + " is not valid for this cursor with id "
  330. + _id);
  331. }
  332. restorePosition(savepoint.getCurrentPosition(),
  333. savepoint.getPreviousPosition());
  334. }
  335. /**
  336. * Returns the first row id (exclusive) as defined by this cursor.
  337. */
  338. protected Position getFirstPosition() {
  339. return _firstPos;
  340. }
  341. /**
  342. * Returns the last row id (exclusive) as defined by this cursor.
  343. */
  344. protected Position getLastPosition() {
  345. return _lastPos;
  346. }
  347. /**
  348. * Resets this cursor for forward traversal. Calls {@link #beforeFirst}.
  349. */
  350. public void reset() {
  351. beforeFirst();
  352. }
  353. /**
  354. * Resets this cursor for forward traversal (sets cursor to before the first
  355. * row).
  356. */
  357. public void beforeFirst() {
  358. reset(MOVE_FORWARD);
  359. }
  360. /**
  361. * Resets this cursor for reverse traversal (sets cursor to after the last
  362. * row).
  363. */
  364. public void afterLast() {
  365. reset(MOVE_REVERSE);
  366. }
  367. /**
  368. * Returns {@code true} if the cursor is currently positioned before the
  369. * first row, {@code false} otherwise.
  370. */
  371. public boolean isBeforeFirst()
  372. throws IOException
  373. {
  374. if(getFirstPosition().equals(_curPos)) {
  375. return !recheckPosition(MOVE_REVERSE);
  376. }
  377. return false;
  378. }
  379. /**
  380. * Returns {@code true} if the cursor is currently positioned after the
  381. * last row, {@code false} otherwise.
  382. */
  383. public boolean isAfterLast()
  384. throws IOException
  385. {
  386. if(getLastPosition().equals(_curPos)) {
  387. return !recheckPosition(MOVE_FORWARD);
  388. }
  389. return false;
  390. }
  391. /**
  392. * Returns {@code true} if the row at which the cursor is currently
  393. * positioned is deleted, {@code false} otherwise (including invalid rows).
  394. */
  395. public boolean isCurrentRowDeleted()
  396. throws IOException
  397. {
  398. // we need to ensure that the "deleted" flag has been read for this row
  399. // (or re-read if the table has been recently modified)
  400. Table.positionAtRowData(_rowState, _curPos.getRowId());
  401. return _rowState.isDeleted();
  402. }
  403. /**
  404. * Resets this cursor for traversing the given direction.
  405. */
  406. protected void reset(boolean moveForward) {
  407. _curPos = getDirHandler(moveForward).getBeginningPosition();
  408. _prevPos = _curPos;
  409. _rowState.reset();
  410. }
  411. /**
  412. * Returns an Iterable whose iterator() method calls <code>afterLast</code>
  413. * on this cursor and returns a modifiable Iterator which will iterate
  414. * through all the rows of this table in reverse order. Use of the Iterator
  415. * follows the same restrictions as a call to <code>getPreviousRow</code>.
  416. * @throws IllegalStateException if an IOException is thrown by one of the
  417. * operations, the actual exception will be contained within
  418. */
  419. public Iterable<Map<String, Object>> reverseIterable() {
  420. return reverseIterable(null);
  421. }
  422. /**
  423. * Returns an Iterable whose iterator() method calls <code>afterLast</code>
  424. * on this table and returns a modifiable Iterator which will iterate
  425. * through all the rows of this table in reverse order, returning only the
  426. * given columns. Use of the Iterator follows the same restrictions as a
  427. * call to <code>getPreviousRow</code>.
  428. * @throws IllegalStateException if an IOException is thrown by one of the
  429. * operations, the actual exception will be contained within
  430. */
  431. public Iterable<Map<String, Object>> reverseIterable(
  432. final Collection<String> columnNames)
  433. {
  434. return new Iterable<Map<String, Object>>() {
  435. public Iterator<Map<String, Object>> iterator() {
  436. return new RowIterator(columnNames, MOVE_REVERSE);
  437. }
  438. };
  439. }
  440. /**
  441. * Calls <code>beforeFirst</code> on this cursor and returns a modifiable
  442. * Iterator which will iterate through all the rows of this table. Use of
  443. * the Iterator follows the same restrictions as a call to
  444. * <code>getNextRow</code>.
  445. * @throws IllegalStateException if an IOException is thrown by one of the
  446. * operations, the actual exception will be contained within
  447. */
  448. public Iterator<Map<String, Object>> iterator()
  449. {
  450. return iterator(null);
  451. }
  452. /**
  453. * Returns an Iterable whose iterator() method returns the result of a call
  454. * to {@link #iterator(Collection)}
  455. * @throws IllegalStateException if an IOException is thrown by one of the
  456. * operations, the actual exception will be contained within
  457. */
  458. public Iterable<Map<String, Object>> iterable(
  459. final Collection<String> columnNames)
  460. {
  461. return new Iterable<Map<String, Object>>() {
  462. public Iterator<Map<String, Object>> iterator() {
  463. return Cursor.this.iterator(columnNames);
  464. }
  465. };
  466. }
  467. /**
  468. * Calls <code>beforeFirst</code> on this table and returns a modifiable
  469. * Iterator which will iterate through all the rows of this table, returning
  470. * only the given columns. Use of the Iterator follows the same
  471. * restrictions as a call to <code>getNextRow</code>.
  472. * @throws IllegalStateException if an IOException is thrown by one of the
  473. * operations, the actual exception will be contained within
  474. */
  475. public Iterator<Map<String, Object>> iterator(Collection<String> columnNames)
  476. {
  477. return new RowIterator(columnNames, MOVE_FORWARD);
  478. }
  479. /**
  480. * Returns an Iterable whose iterator() method returns the result of a call
  481. * to {@link #columnMatchIterable(Column,Object)}
  482. * @throws IllegalStateException if an IOException is thrown by one of the
  483. * operations, the actual exception will be contained within
  484. */
  485. public Iterable<Map<String, Object>> columnMatchIterable(
  486. Column columnPattern, Object valuePattern)
  487. {
  488. return columnMatchIterable(null, columnPattern, valuePattern);
  489. }
  490. /**
  491. * Calls <code>beforeFirst</code> on this cursor and returns a modifiable
  492. * Iterator which will iterate through all the rows of this table which
  493. * match the given column pattern. Use of the Iterator follows the same
  494. * restrictions as a call to <code>getNextRow</code>. See
  495. * {@link #findFirstRow(Column,Object)} for details on the columnPattern.
  496. * @throws IllegalStateException if an IOException is thrown by one of the
  497. * operations, the actual exception will be contained within
  498. */
  499. public Iterator<Map<String, Object>> columnMatchIterator(
  500. Column columnPattern, Object valuePattern)
  501. {
  502. return columnMatchIterator(null, columnPattern, valuePattern);
  503. }
  504. /**
  505. * Returns an Iterable whose iterator() method returns the result of a call
  506. * to {@link #columnMatchIterator(Collection,Column,Object)}
  507. * @throws IllegalStateException if an IOException is thrown by one of the
  508. * operations, the actual exception will be contained within
  509. */
  510. public Iterable<Map<String, Object>> columnMatchIterable(
  511. final Collection<String> columnNames,
  512. final Column columnPattern, final Object valuePattern)
  513. {
  514. return new Iterable<Map<String, Object>>() {
  515. public Iterator<Map<String, Object>> iterator() {
  516. return Cursor.this.columnMatchIterator(
  517. columnNames, columnPattern, valuePattern);
  518. }
  519. };
  520. }
  521. /**
  522. * Calls <code>beforeFirst</code> on this table and returns a modifiable
  523. * Iterator which will iterate through all the rows of this table which
  524. * match the given column pattern, returning only the given columns. Use of
  525. * the Iterator follows the same restrictions as a call to
  526. * <code>getNextRow</code>. See {@link #findFirstRow(Column,Object)} for
  527. * details on the columnPattern.
  528. * @throws IllegalStateException if an IOException is thrown by one of the
  529. * operations, the actual exception will be contained within
  530. */
  531. public Iterator<Map<String, Object>> columnMatchIterator(
  532. Collection<String> columnNames, Column columnPattern, Object valuePattern)
  533. {
  534. return new ColumnMatchIterator(columnNames, columnPattern, valuePattern);
  535. }
  536. /**
  537. * Returns an Iterable whose iterator() method returns the result of a call
  538. * to {@link #rowMatchIterator(Map)}
  539. * @throws IllegalStateException if an IOException is thrown by one of the
  540. * operations, the actual exception will be contained within
  541. */
  542. public Iterable<Map<String, Object>> rowMatchIterable(
  543. Map<String,?> rowPattern)
  544. {
  545. return rowMatchIterable(null, rowPattern);
  546. }
  547. /**
  548. * Calls <code>beforeFirst</code> on this cursor and returns a modifiable
  549. * Iterator which will iterate through all the rows of this table which
  550. * match the given row pattern. Use of the Iterator follows the same
  551. * restrictions as a call to <code>getNextRow</code>. See
  552. * {@link #findFirstRow(Map)} for details on the rowPattern.
  553. * @throws IllegalStateException if an IOException is thrown by one of the
  554. * operations, the actual exception will be contained within
  555. */
  556. public Iterator<Map<String, Object>> rowMatchIterator(
  557. Map<String,?> rowPattern)
  558. {
  559. return rowMatchIterator(null, rowPattern);
  560. }
  561. /**
  562. * Returns an Iterable whose iterator() method returns the result of a call
  563. * to {@link #rowMatchIterator(Collection,Map)}
  564. * @throws IllegalStateException if an IOException is thrown by one of the
  565. * operations, the actual exception will be contained within
  566. */
  567. public Iterable<Map<String, Object>> rowMatchIterable(
  568. final Collection<String> columnNames,
  569. final Map<String,?> rowPattern)
  570. {
  571. return new Iterable<Map<String, Object>>() {
  572. public Iterator<Map<String, Object>> iterator() {
  573. return Cursor.this.rowMatchIterator(
  574. columnNames, rowPattern);
  575. }
  576. };
  577. }
  578. /**
  579. * Calls <code>beforeFirst</code> on this table and returns a modifiable
  580. * Iterator which will iterate through all the rows of this table which
  581. * match the given row pattern, returning only the given columns. Use of
  582. * the Iterator follows the same restrictions as a call to
  583. * <code>getNextRow</code>. See {@link #findFirstRow(Map)} for details on
  584. * the rowPattern.
  585. * @throws IllegalStateException if an IOException is thrown by one of the
  586. * operations, the actual exception will be contained within
  587. */
  588. public Iterator<Map<String, Object>> rowMatchIterator(
  589. Collection<String> columnNames, Map<String,?> rowPattern)
  590. {
  591. return new RowMatchIterator(columnNames, rowPattern);
  592. }
  593. /**
  594. * Delete the current row.
  595. * @throws IllegalStateException if the current row is not valid (at
  596. * beginning or end of table), or already deleted.
  597. */
  598. public void deleteCurrentRow() throws IOException {
  599. _table.deleteRow(_rowState, _curPos.getRowId());
  600. }
  601. /**
  602. * Update the current row.
  603. * @throws IllegalStateException if the current row is not valid (at
  604. * beginning or end of table), or deleted.
  605. */
  606. public void updateCurrentRow(Object... row) throws IOException {
  607. _table.updateRow(_rowState, _curPos.getRowId(), row);
  608. }
  609. /**
  610. * Moves to the next row in the table and returns it.
  611. * @return The next row in this table (Column name -> Column value), or
  612. * {@code null} if no next row is found
  613. */
  614. public Map<String, Object> getNextRow() throws IOException {
  615. return getNextRow(null);
  616. }
  617. /**
  618. * Moves to the next row in the table and returns it.
  619. * @param columnNames Only column names in this collection will be returned
  620. * @return The next row in this table (Column name -> Column value), or
  621. * {@code null} if no next row is found
  622. */
  623. public Map<String, Object> getNextRow(Collection<String> columnNames)
  624. throws IOException
  625. {
  626. return getAnotherRow(columnNames, MOVE_FORWARD);
  627. }
  628. /**
  629. * Moves to the previous row in the table and returns it.
  630. * @return The previous row in this table (Column name -> Column value), or
  631. * {@code null} if no previous row is found
  632. */
  633. public Map<String, Object> getPreviousRow() throws IOException {
  634. return getPreviousRow(null);
  635. }
  636. /**
  637. * Moves to the previous row in the table and returns it.
  638. * @param columnNames Only column names in this collection will be returned
  639. * @return The previous row in this table (Column name -> Column value), or
  640. * {@code null} if no previous row is found
  641. */
  642. public Map<String, Object> getPreviousRow(Collection<String> columnNames)
  643. throws IOException
  644. {
  645. return getAnotherRow(columnNames, MOVE_REVERSE);
  646. }
  647. /**
  648. * Moves to another row in the table based on the given direction and
  649. * returns it.
  650. * @param columnNames Only column names in this collection will be returned
  651. * @return another row in this table (Column name -> Column value), where
  652. * "next" may be backwards if moveForward is {@code false}, or
  653. * {@code null} if there is not another row in the given direction.
  654. */
  655. private Map<String, Object> getAnotherRow(Collection<String> columnNames,
  656. boolean moveForward)
  657. throws IOException
  658. {
  659. if(moveToAnotherRow(moveForward)) {
  660. return getCurrentRow(columnNames);
  661. }
  662. return null;
  663. }
  664. /**
  665. * Moves to the next row as defined by this cursor.
  666. * @return {@code true} if a valid next row was found, {@code false}
  667. * otherwise
  668. */
  669. public boolean moveToNextRow()
  670. throws IOException
  671. {
  672. return moveToAnotherRow(MOVE_FORWARD);
  673. }
  674. /**
  675. * Moves to the previous row as defined by this cursor.
  676. * @return {@code true} if a valid previous row was found, {@code false}
  677. * otherwise
  678. */
  679. public boolean moveToPreviousRow()
  680. throws IOException
  681. {
  682. return moveToAnotherRow(MOVE_REVERSE);
  683. }
  684. /**
  685. * Moves to another row in the given direction as defined by this cursor.
  686. * @return {@code true} if another valid row was found in the given
  687. * direction, {@code false} otherwise
  688. */
  689. private boolean moveToAnotherRow(boolean moveForward)
  690. throws IOException
  691. {
  692. if(_curPos.equals(getDirHandler(moveForward).getEndPosition())) {
  693. // already at end, make sure nothing has changed
  694. return recheckPosition(moveForward);
  695. }
  696. return moveToAnotherRowImpl(moveForward);
  697. }
  698. /**
  699. * Restores a current position for the cursor (current position becomes
  700. * previous position).
  701. */
  702. protected void restorePosition(Position curPos)
  703. throws IOException
  704. {
  705. restorePosition(curPos, _curPos);
  706. }
  707. /**
  708. * Restores a current and previous position for the cursor if the given
  709. * positions are different from the current positions.
  710. */
  711. protected final void restorePosition(Position curPos, Position prevPos)
  712. throws IOException
  713. {
  714. if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
  715. restorePositionImpl(curPos, prevPos);
  716. }
  717. }
  718. /**
  719. * Restores a current and previous position for the cursor.
  720. */
  721. protected void restorePositionImpl(Position curPos, Position prevPos)
  722. throws IOException
  723. {
  724. // make the current position previous, and the new position current
  725. _prevPos = _curPos;
  726. _curPos = curPos;
  727. _rowState.reset();
  728. }
  729. /**
  730. * Rechecks the current position if the underlying data structures have been
  731. * modified.
  732. * @return {@code true} if the cursor ended up in a new position,
  733. * {@code false} otherwise.
  734. */
  735. private boolean recheckPosition(boolean moveForward)
  736. throws IOException
  737. {
  738. if(isUpToDate()) {
  739. // nothing has changed
  740. return false;
  741. }
  742. // move the cursor back to the previous position
  743. restorePosition(_prevPos);
  744. return moveToAnotherRowImpl(moveForward);
  745. }
  746. /**
  747. * Does the grunt work of moving the cursor to another position in the given
  748. * direction.
  749. */
  750. private boolean moveToAnotherRowImpl(boolean moveForward)
  751. throws IOException
  752. {
  753. _rowState.reset();
  754. _prevPos = _curPos;
  755. _curPos = findAnotherPosition(_rowState, _curPos, moveForward);
  756. Table.positionAtRowHeader(_rowState, _curPos.getRowId());
  757. return(!_curPos.equals(getDirHandler(moveForward).getEndPosition()));
  758. }
  759. /**
  760. * @deprecated renamed to {@link #findFirstRow(Column,Object)} to be more clear
  761. */
  762. @Deprecated
  763. public boolean findRow(Column columnPattern, Object valuePattern)
  764. throws IOException
  765. {
  766. return findFirstRow(columnPattern, valuePattern);
  767. }
  768. /**
  769. * Moves to the first row (as defined by the cursor) where the given column
  770. * has the given value. This may be more efficient on some cursors than
  771. * others. If a match is not found (or an exception is thrown), the cursor
  772. * is restored to its previous state.
  773. * <p>
  774. * Warning, this method <i>always</i> starts searching from the beginning of
  775. * the Table (you cannot use it to find successive matches).
  776. *
  777. * @param columnPattern column from the table for this cursor which is being
  778. * matched by the valuePattern
  779. * @param valuePattern value which is equal to the corresponding value in
  780. * the matched row
  781. * @return {@code true} if a valid row was found with the given value,
  782. * {@code false} if no row was found
  783. */
  784. public boolean findFirstRow(Column columnPattern, Object valuePattern)
  785. throws IOException
  786. {
  787. Position curPos = _curPos;
  788. Position prevPos = _prevPos;
  789. boolean found = false;
  790. try {
  791. beforeFirst();
  792. found = findNextRowImpl(columnPattern, valuePattern);
  793. return found;
  794. } finally {
  795. if(!found) {
  796. try {
  797. restorePosition(curPos, prevPos);
  798. } catch(IOException e) {
  799. LOG.error("Failed restoring position", e);
  800. }
  801. }
  802. }
  803. }
  804. /**
  805. * Moves to the next row (as defined by the cursor) where the given column
  806. * has the given value. This may be more efficient on some cursors than
  807. * others. If a match is not found (or an exception is thrown), the cursor
  808. * is restored to its previous state.
  809. *
  810. * @param columnPattern column from the table for this cursor which is being
  811. * matched by the valuePattern
  812. * @param valuePattern value which is equal to the corresponding value in
  813. * the matched row
  814. * @return {@code true} if a valid row was found with the given value,
  815. * {@code false} if no row was found
  816. */
  817. public boolean findNextRow(Column columnPattern, Object valuePattern)
  818. throws IOException
  819. {
  820. Position curPos = _curPos;
  821. Position prevPos = _prevPos;
  822. boolean found = false;
  823. try {
  824. found = findNextRowImpl(columnPattern, valuePattern);
  825. return found;
  826. } finally {
  827. if(!found) {
  828. try {
  829. restorePosition(curPos, prevPos);
  830. } catch(IOException e) {
  831. LOG.error("Failed restoring position", e);
  832. }
  833. }
  834. }
  835. }
  836. /**
  837. * @deprecated renamed to {@link #findFirstRow(Map)} to be more clear
  838. */
  839. @Deprecated
  840. public boolean findRow(Map<String,?> rowPattern)
  841. throws IOException
  842. {
  843. return findFirstRow(rowPattern);
  844. }
  845. /**
  846. * Moves to the first row (as defined by the cursor) where the given columns
  847. * have the given values. This may be more efficient on some cursors than
  848. * others. If a match is not found (or an exception is thrown), the cursor
  849. * is restored to its previous state.
  850. * <p>
  851. * Warning, this method <i>always</i> starts searching from the beginning of
  852. * the Table (you cannot use it to find successive matches).
  853. *
  854. * @param rowPattern column names and values which must be equal to the
  855. * corresponding values in the matched row
  856. * @return {@code true} if a valid row was found with the given values,
  857. * {@code false} if no row was found
  858. */
  859. public boolean findFirstRow(Map<String,?> rowPattern)
  860. throws IOException
  861. {
  862. Position curPos = _curPos;
  863. Position prevPos = _prevPos;
  864. boolean found = false;
  865. try {
  866. beforeFirst();
  867. found = findNextRowImpl(rowPattern);
  868. return found;
  869. } finally {
  870. if(!found) {
  871. try {
  872. restorePosition(curPos, prevPos);
  873. } catch(IOException e) {
  874. LOG.error("Failed restoring position", e);
  875. }
  876. }
  877. }
  878. }
  879. /**
  880. * Moves to the next row (as defined by the cursor) where the given columns
  881. * have the given values. This may be more efficient on some cursors than
  882. * others. If a match is not found (or an exception is thrown), the cursor
  883. * is restored to its previous state.
  884. *
  885. * @param rowPattern column names and values which must be equal to the
  886. * corresponding values in the matched row
  887. * @return {@code true} if a valid row was found with the given values,
  888. * {@code false} if no row was found
  889. */
  890. public boolean findNextRow(Map<String,?> rowPattern)
  891. throws IOException
  892. {
  893. Position curPos = _curPos;
  894. Position prevPos = _prevPos;
  895. boolean found = false;
  896. try {
  897. found = findNextRowImpl(rowPattern);
  898. return found;
  899. } finally {
  900. if(!found) {
  901. try {
  902. restorePosition(curPos, prevPos);
  903. } catch(IOException e) {
  904. LOG.error("Failed restoring position", e);
  905. }
  906. }
  907. }
  908. }
  909. /**
  910. * Returns {@code true} if the current row matches the given pattern.
  911. * @param columnPattern column from the table for this cursor which is being
  912. * matched by the valuePattern
  913. * @param valuePattern value which is tested for equality with the
  914. * corresponding value in the current row
  915. */
  916. public boolean currentRowMatches(Column columnPattern, Object valuePattern)
  917. throws IOException
  918. {
  919. return _columnMatcher.matches(getTable(), columnPattern.getName(),
  920. valuePattern,
  921. getCurrentRowValue(columnPattern));
  922. }
  923. /**
  924. * Returns {@code true} if the current row matches the given pattern.
  925. * @param rowPattern column names and values which must be equal to the
  926. * corresponding values in the current row
  927. */
  928. public boolean currentRowMatches(Map<String,?> rowPattern)
  929. throws IOException
  930. {
  931. Map<String,Object> row = getCurrentRow(rowPattern.keySet());
  932. if(rowPattern.size() != row.size()) {
  933. return false;
  934. }
  935. for(Map.Entry<String,Object> e : row.entrySet()) {
  936. String columnName = e.getKey();
  937. if(!_columnMatcher.matches(getTable(), columnName,
  938. rowPattern.get(columnName), e.getValue())) {
  939. return false;
  940. }
  941. }
  942. return true;
  943. }
  944. /**
  945. * Moves to the next row (as defined by the cursor) where the given column
  946. * has the given value. Caller manages save/restore on failure.
  947. * <p>
  948. * Default implementation scans the table from beginning to end.
  949. *
  950. * @param columnPattern column from the table for this cursor which is being
  951. * matched by the valuePattern
  952. * @param valuePattern value which is equal to the corresponding value in
  953. * the matched row
  954. * @return {@code true} if a valid row was found with the given value,
  955. * {@code false} if no row was found
  956. */
  957. protected boolean findNextRowImpl(Column columnPattern, Object valuePattern)
  958. throws IOException
  959. {
  960. while(moveToNextRow()) {
  961. if(currentRowMatches(columnPattern, valuePattern)) {
  962. return true;
  963. }
  964. }
  965. return false;
  966. }
  967. /**
  968. * Moves to the next row (as defined by the cursor) where the given columns
  969. * have the given values. Caller manages save/restore on failure.
  970. * <p>
  971. * Default implementation scans the table from beginning to end.
  972. *
  973. * @param rowPattern column names and values which must be equal to the
  974. * corresponding values in the matched row
  975. * @return {@code true} if a valid row was found with the given values,
  976. * {@code false} if no row was found
  977. */
  978. protected boolean findNextRowImpl(Map<String,?> rowPattern)
  979. throws IOException
  980. {
  981. while(moveToNextRow()) {
  982. if(currentRowMatches(rowPattern)) {
  983. return true;
  984. }
  985. }
  986. return false;
  987. }
  988. /**
  989. * Moves forward as many rows as possible up to the given number of rows.
  990. * @return the number of rows moved.
  991. */
  992. public int moveNextRows(int numRows)
  993. throws IOException
  994. {
  995. return moveSomeRows(numRows, MOVE_FORWARD);
  996. }
  997. /**
  998. * Moves backward as many rows as possible up to the given number of rows.
  999. * @return the number of rows moved.
  1000. */
  1001. public int movePreviousRows(int numRows)
  1002. throws IOException
  1003. {
  1004. return moveSomeRows(numRows, MOVE_REVERSE);
  1005. }
  1006. /**
  1007. * Moves as many rows as possible in the given direction up to the given
  1008. * number of rows.
  1009. * @return the number of rows moved.
  1010. */
  1011. private int moveSomeRows(int numRows, boolean moveForward)
  1012. throws IOException
  1013. {
  1014. int numMovedRows = 0;
  1015. while((numMovedRows < numRows) && moveToAnotherRow(moveForward)) {
  1016. ++numMovedRows;
  1017. }
  1018. return numMovedRows;
  1019. }
  1020. /**
  1021. * Returns the current row in this cursor (Column name -> Column value).
  1022. */
  1023. public Map<String, Object> getCurrentRow()
  1024. throws IOException
  1025. {
  1026. return getCurrentRow(null);
  1027. }
  1028. /**
  1029. * Returns the current row in this cursor (Column name -> Column value).
  1030. * @param columnNames Only column names in this collection will be returned
  1031. */
  1032. public Map<String, Object> getCurrentRow(Collection<String> columnNames)
  1033. throws IOException
  1034. {
  1035. return _table.getRow(_rowState, _curPos.getRowId(), columnNames);
  1036. }
  1037. /**
  1038. * Returns the given column from the current row.
  1039. */
  1040. public Object getCurrentRowValue(Column column)
  1041. throws IOException
  1042. {
  1043. return _table.getRowValue(_rowState, _curPos.getRowId(), column);
  1044. }
  1045. /**
  1046. * Updates a single value in the current row.
  1047. * @throws IllegalStateException if the current row is not valid (at
  1048. * beginning or end of table), or deleted.
  1049. */
  1050. public void setCurrentRowValue(Column column, Object value)
  1051. throws IOException
  1052. {
  1053. Object[] row = new Object[_table.getColumnCount()];
  1054. Arrays.fill(row, Column.KEEP_VALUE);
  1055. column.setRowValue(row, value);
  1056. _table.updateRow(_rowState, _curPos.getRowId(), row);
  1057. }
  1058. /**
  1059. * Returns {@code true} if this cursor is up-to-date with respect to the
  1060. * relevant table and related table objects, {@code false} otherwise.
  1061. */
  1062. protected boolean isUpToDate() {
  1063. return _rowState.isUpToDate();
  1064. }
  1065. @Override
  1066. public String toString() {
  1067. return getClass().getSimpleName() + " CurPosition " + _curPos +
  1068. ", PrevPosition " + _prevPos;
  1069. }
  1070. /**
  1071. * Finds the next non-deleted row after the given row (as defined by this
  1072. * cursor) and returns the id of the row, where "next" may be backwards if
  1073. * moveForward is {@code false}. If there are no more rows, the returned
  1074. * rowId should equal the value returned by {@link #getLastPosition} if
  1075. * moving forward and {@link #getFirstPosition} if moving backward.
  1076. */
  1077. protected abstract Position findAnotherPosition(RowState rowState,
  1078. Position curPos,
  1079. boolean moveForward)
  1080. throws IOException;
  1081. /**
  1082. * Returns the DirHandler for the given movement direction.
  1083. */
  1084. protected abstract DirHandler getDirHandler(boolean moveForward);
  1085. /**
  1086. * Base implementation of iterator for this cursor, modifiable.
  1087. */
  1088. protected abstract class BaseIterator
  1089. implements Iterator<Map<String, Object>>
  1090. {
  1091. protected final Collection<String> _columnNames;
  1092. protected Boolean _hasNext;
  1093. protected boolean _validRow;
  1094. protected BaseIterator(Collection<String> columnNames)
  1095. {
  1096. _columnNames = columnNames;
  1097. }
  1098. public boolean hasNext() {
  1099. if(_hasNext == null) {
  1100. try {
  1101. _hasNext = findNext();
  1102. _validRow = _hasNext;
  1103. } catch(IOException e) {
  1104. throw new IllegalStateException(e);
  1105. }
  1106. }
  1107. return _hasNext;
  1108. }
  1109. public Map<String, Object> next() {
  1110. if(!hasNext()) {
  1111. throw new NoSuchElementException();
  1112. }
  1113. try {
  1114. Map<String, Object> rtn = getCurrentRow(_columnNames);
  1115. _hasNext = null;
  1116. return rtn;
  1117. } catch(IOException e) {
  1118. throw new IllegalStateException(e);
  1119. }
  1120. }
  1121. public void remove() {
  1122. if(_validRow) {
  1123. try {
  1124. deleteCurrentRow();
  1125. _validRow = false;
  1126. } catch(IOException e) {
  1127. throw new IllegalStateException(e);
  1128. }
  1129. } else {
  1130. throw new IllegalStateException("Not at valid row");
  1131. }
  1132. }
  1133. protected abstract boolean findNext() throws IOException;
  1134. }
  1135. /**
  1136. * Row iterator for this cursor, modifiable.
  1137. */
  1138. private final class RowIterator extends BaseIterator
  1139. {
  1140. private final boolean _moveForward;
  1141. private RowIterator(Collection<String> columnNames, boolean moveForward)
  1142. {
  1143. super(columnNames);
  1144. _moveForward = moveForward;
  1145. reset(_moveForward);
  1146. }
  1147. @Override
  1148. protected boolean findNext() throws IOException {
  1149. return moveToAnotherRow(_moveForward);
  1150. }
  1151. }
  1152. /**
  1153. * Row iterator for this cursor, modifiable.
  1154. */
  1155. private final class ColumnMatchIterator extends BaseIterator
  1156. {
  1157. private final Column _columnPattern;
  1158. private final Object _valuePattern;
  1159. private ColumnMatchIterator(Collection<String> columnNames,
  1160. Column columnPattern, Object valuePattern)
  1161. {
  1162. super(columnNames);
  1163. _columnPattern = columnPattern;
  1164. _valuePattern = valuePattern;
  1165. beforeFirst();
  1166. }
  1167. @Override
  1168. protected boolean findNext() throws IOException {
  1169. return findNextRow(_columnPattern, _valuePattern);
  1170. }
  1171. }
  1172. /**
  1173. * Row iterator for this cursor, modifiable.
  1174. */
  1175. private final class RowMatchIterator extends BaseIterator
  1176. {
  1177. private final Map<String,?> _rowPattern;
  1178. private RowMatchIterator(Collection<String> columnNames,
  1179. Map<String,?> rowPattern)
  1180. {
  1181. super(columnNames);
  1182. _rowPattern = rowPattern;
  1183. beforeFirst();
  1184. }
  1185. @Override
  1186. protected boolean findNext() throws IOException {
  1187. return findNextRow(_rowPattern);
  1188. }
  1189. }
  1190. /**
  1191. * Handles moving the cursor in a given direction. Separates cursor
  1192. * logic from value storage.
  1193. */
  1194. protected abstract class DirHandler
  1195. {
  1196. public abstract Position getBeginningPosition();
  1197. public abstract Position getEndPosition();
  1198. }
  1199. /**
  1200. * Simple un-indexed cursor.
  1201. */
  1202. private static final class TableScanCursor extends Cursor
  1203. {
  1204. /** ScanDirHandler for forward traversal */
  1205. private final ScanDirHandler _forwardDirHandler =
  1206. new ForwardScanDirHandler();
  1207. /** ScanDirHandler for backward traversal */
  1208. private final ScanDirHandler _reverseDirHandler =
  1209. new ReverseScanDirHandler();
  1210. /** Cursor over the pages that this table owns */
  1211. private final UsageMap.PageCursor _ownedPagesCursor;
  1212. private TableScanCursor(Table table) {
  1213. super(new Id(table, null), table,
  1214. FIRST_SCAN_POSITION, LAST_SCAN_POSITION);
  1215. _ownedPagesCursor = table.getOwnedPagesCursor();
  1216. }
  1217. @Override
  1218. protected ScanDirHandler getDirHandler(boolean moveForward) {
  1219. return (moveForward ? _forwardDirHandler : _reverseDirHandler);
  1220. }
  1221. @Override
  1222. protected boolean isUpToDate() {
  1223. return(super.isUpToDate() && _ownedPagesCursor.isUpToDate());
  1224. }
  1225. @Override
  1226. protected void reset(boolean moveForward) {
  1227. _ownedPagesCursor.reset(moveForward);
  1228. super.reset(moveForward);
  1229. }
  1230. @Override
  1231. protected void restorePositionImpl(Position curPos, Position prevPos)
  1232. throws IOException
  1233. {
  1234. if(!(curPos instanceof ScanPosition) ||
  1235. !(prevPos instanceof ScanPosition)) {
  1236. throw new IllegalArgumentException(
  1237. "Restored positions must be scan positions");
  1238. }
  1239. _ownedPagesCursor.restorePosition(curPos.getRowId().getPageNumber(),
  1240. prevPos.getRowId().getPageNumber());
  1241. super.restorePositionImpl(curPos, prevPos);
  1242. }
  1243. @Override
  1244. protected Position findAnotherPosition(RowState rowState, Position curPos,
  1245. boolean moveForward)
  1246. throws IOException
  1247. {
  1248. ScanDirHandler handler = getDirHandler(moveForward);
  1249. // figure out how many rows are left on this page so we can find the
  1250. // next row
  1251. RowId curRowId = curPos.getRowId();
  1252. Table.positionAtRowHeader(rowState, curRowId);
  1253. int currentRowNumber = curRowId.getRowNumber();
  1254. // loop until we find the next valid row or run out of pages
  1255. while(true) {
  1256. currentRowNumber = handler.getAnotherRowNumber(currentRowNumber);
  1257. curRowId = new RowId(curRowId.getPageNumber(), currentRowNumber);
  1258. Table.positionAtRowHeader(rowState, curRowId);
  1259. if(!rowState.isValid()) {
  1260. // load next page
  1261. curRowId = new RowId(handler.getAnotherPageNumber(),
  1262. RowId.INVALID_ROW_NUMBER);
  1263. Table.positionAtRowHeader(rowState, curRowId);
  1264. if(!rowState.isHeaderPageNumberValid()) {
  1265. //No more owned pages. No more rows.
  1266. return handler.getEndPosition();
  1267. }
  1268. // update row count and initial row number
  1269. currentRowNumber = handler.getInitialRowNumber(
  1270. rowState.getRowsOnHeaderPage());
  1271. } else if(!rowState.isDeleted()) {
  1272. // we found a valid, non-deleted row, return it
  1273. return new ScanPosition(curRowId);
  1274. }
  1275. }
  1276. }
  1277. /**
  1278. * Handles moving the table scan cursor in a given direction. Separates
  1279. * cursor logic from value storage.
  1280. */
  1281. private abstract class ScanDirHandler extends DirHandler {
  1282. public abstract int getAnotherRowNumber(int curRowNumber);
  1283. public abstract int getAnotherPageNumber();
  1284. public abstract int getInitialRowNumber(int rowsOnPage);
  1285. }
  1286. /**
  1287. * Handles moving the table scan cursor forward.
  1288. */
  1289. private final class ForwardScanDirHandler extends ScanDirHandler {
  1290. @Override
  1291. public Position getBeginningPosition() {
  1292. return getFirstPosition();
  1293. }
  1294. @Override
  1295. public Position getEndPosition() {
  1296. return getLastPosition();
  1297. }
  1298. @Override
  1299. public int getAnotherRowNumber(int curRowNumber) {
  1300. return curRowNumber + 1;
  1301. }
  1302. @Override
  1303. public int getAnotherPageNumber() {
  1304. return _ownedPagesCursor.getNextPage();
  1305. }
  1306. @Override
  1307. public int getInitialRowNumber(int rowsOnPage) {
  1308. return -1;
  1309. }
  1310. }
  1311. /**
  1312. * Handles moving the table scan cursor backward.
  1313. */
  1314. private final class ReverseScanDirHandler extends ScanDirHandler {
  1315. @Override
  1316. public Position getBeginningPosition() {
  1317. return getLastPosition();
  1318. }
  1319. @Override
  1320. public Position getEndPosition() {
  1321. return getFirstPosition();
  1322. }
  1323. @Override
  1324. public int getAnotherRowNumber(int curRowNumber) {
  1325. return curRowNumber - 1;
  1326. }
  1327. @Override
  1328. public int getAnotherPageNumber() {
  1329. return _ownedPagesCursor.getPreviousPage();
  1330. }
  1331. @Override
  1332. public int getInitialRowNumber(int rowsOnPage) {
  1333. return rowsOnPage;
  1334. }
  1335. }
  1336. }
  1337. /**
  1338. * Identifier for a cursor. Will be equal to any other cursor of the same
  1339. * type for the same table. Primarily used to check the validity of a
  1340. * Savepoint.
  1341. */
  1342. public static final class Id
  1343. {
  1344. private final String _tableName;
  1345. private final String _indexName;
  1346. protected Id(Table table, Index index) {
  1347. _tableName = table.getName();
  1348. _indexName = ((index != null) ? index.getName() : null);
  1349. }
  1350. @Override
  1351. public int hashCode() {
  1352. return _tableName.hashCode();
  1353. }
  1354. @Override
  1355. public boolean equals(Object o) {
  1356. return((this == o) ||
  1357. ((o != null) && (getClass() == o.getClass()) &&
  1358. ObjectUtils.equals(_tableName, ((Id)o)._tableName) &&
  1359. ObjectUtils.equals(_indexName, ((Id)o)._indexName)));
  1360. }
  1361. @Override
  1362. public String toString() {
  1363. return getClass().getSimpleName() + " " + _tableName + ":" + _indexName;
  1364. }
  1365. }
  1366. /**
  1367. * Value object which represents a complete save state of the cursor.
  1368. */
  1369. public static final class Savepoint
  1370. {
  1371. private final Id _cursorId;
  1372. private final Position _curPos;
  1373. private final Position _prevPos;
  1374. private Savepoint(Id cursorId, Position curPos, Position prevPos) {
  1375. _cursorId = cursorId;
  1376. _curPos = curPos;
  1377. _prevPos = prevPos;
  1378. }
  1379. public Id getCursorId() {
  1380. return _cursorId;
  1381. }
  1382. public Position getCurrentPosition() {
  1383. return _curPos;
  1384. }
  1385. private Position getPreviousPosition() {
  1386. return _prevPos;
  1387. }
  1388. @Override
  1389. public String toString() {
  1390. return getClass().getSimpleName() + " " + _cursorId + " CurPosition " +
  1391. _curPos + ", PrevPosition " + _prevPos;
  1392. }
  1393. }
  1394. /**
  1395. * Value object which maintains the current position of the cursor.
  1396. */
  1397. public static abstract class Position
  1398. {
  1399. protected Position() {
  1400. }
  1401. @Override
  1402. public final int hashCode() {
  1403. return getRowId().hashCode();
  1404. }
  1405. @Override
  1406. public final boolean equals(Object o) {
  1407. return((this == o) ||
  1408. ((o != null) && (getClass() == o.getClass()) && equalsImpl(o)));
  1409. }
  1410. /**
  1411. * Returns the unique RowId of the position of the cursor.
  1412. */
  1413. public abstract RowId getRowId();
  1414. /**
  1415. * Returns {@code true} if the subclass specific info in a Position is
  1416. * equal, {@code false} otherwise.
  1417. * @param o object being tested for equality, guaranteed to be the same
  1418. * class as this object
  1419. */
  1420. protected abstract boolean equalsImpl(Object o);
  1421. }
  1422. /**
  1423. * Value object which maintains the current position of a TableScanCursor.
  1424. */
  1425. private static final class ScanPosition extends Position
  1426. {
  1427. private final RowId _rowId;
  1428. private ScanPosition(RowId rowId) {
  1429. _rowId = rowId;
  1430. }
  1431. @Override
  1432. public RowId getRowId() {
  1433. return _rowId;
  1434. }
  1435. @Override
  1436. protected boolean equalsImpl(Object o) {
  1437. return getRowId().equals(((ScanPosition)o).getRowId());
  1438. }
  1439. @Override
  1440. public String toString() {
  1441. return "RowId = " + getRowId();
  1442. }
  1443. }
  1444. }