1 /*
2 Copyright (c) 2007 Health Market Science, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA
18
19 You can contact Health Market Science at info@healthmarketscience.com
20 or at the following address:
21
22 Health Market Science
23 2700 Horizon Drive
24 Suite 200
25 King of Prussia, PA 19406
26 */
27
28 package com.healthmarketscience.jackcess;
29
30 import java.io.IOException;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Iterator;
34 import java.util.List;
35
36 import org.apache.commons.lang.ObjectUtils;
37
38 /**
39 * Builder style class for constructing a Cursor. By default, a cursor is
40 * created at the beginning of the table, and any start/end rows are
41 * inclusive.
42 *
43 * @author James Ahlborn
44 */
45 public class CursorBuilder {
46 /** the table which the cursor will traverse */
47 private final Table _table;
48 /** optional index to use in traversal */
49 private Index _index;
50 /** optional start row for an index cursor */
51 private Object[] _startRow;
52 /** whether or not start row for an index cursor is inclusive */
53 private boolean _startRowInclusive = true;
54 /** optional end row for an index cursor */
55 private Object[] _endRow;
56 /** whether or not end row for an index cursor is inclusive */
57 private boolean _endRowInclusive = true;
58 /** whether to start at beginning or end of cursor */
59 private boolean _beforeFirst = true;
60 /** optional save point to restore to the cursor */
61 private Cursor.Savepoint _savepoint;
62
63 public CursorBuilder(Table table) {
64 _table = table;
65 }
66
67 /**
68 * Sets the cursor so that it will start at the beginning (unless a
69 * savepoint is given).
70 */
71 public CursorBuilder beforeFirst() {
72 _beforeFirst = true;
73 return this;
74 }
75
76 /**
77 * Sets the cursor so that it will start at the end (unless a savepoint is
78 * given).
79 */
80 public CursorBuilder afterLast() {
81 _beforeFirst = false;
82 return this;
83 }
84
85 /**
86 * Sets a savepoint to restore for the initial position of the cursor.
87 */
88 public CursorBuilder restoreSavepoint(Cursor.Savepoint savepoint) {
89 _savepoint = savepoint;
90 return this;
91 }
92
93 /**
94 * Sets an index to use for the cursor.
95 */
96 public CursorBuilder setIndex(Index index) {
97 _index = index;
98 return this;
99 }
100
101 /**
102 * Sets an index to use for the cursor by searching the table for an index
103 * with the given name.
104 * @throws IllegalArgumentException if no index can be found on the table
105 * with the given name
106 */
107 public CursorBuilder setIndexByName(String indexName) {
108 return setIndex(_table.getIndex(indexName));
109 }
110
111 /**
112 * Sets an index to use for the cursor by searching the table for an index
113 * with exactly the given columns.
114 * @throws IllegalArgumentException if no index can be found on the table
115 * with the given name
116 */
117 public CursorBuilder setIndexByColumns(Column... columns) {
118 List<Column> searchColumns = Arrays.asList(columns);
119 boolean found = false;
120 for(Index index : _table.getIndexes()) {
121
122 Collection<Index.ColumnDescriptor> indexColumns = index.getColumns();
123 if(indexColumns.size() != searchColumns.size()) {
124 continue;
125 }
126 Iterator<Column> sIter = searchColumns.iterator();
127 Iterator<Index.ColumnDescriptor> iIter = indexColumns.iterator();
128 boolean matches = true;
129 while(sIter.hasNext()) {
130 Column sCol = sIter.next();
131 Index.ColumnDescriptor iCol = iIter.next();
132 if(!ObjectUtils.equals(sCol.getName(), iCol.getName())) {
133 matches = false;
134 break;
135 }
136 }
137
138 if(matches) {
139 _index = index;
140 found = true;
141 break;
142 }
143 }
144 if(!found) {
145 throw new IllegalArgumentException("Index with columns " +
146 searchColumns +
147 " does not exist in table " + _table);
148 }
149 return this;
150 }
151
152 /**
153 * Sets the starting and ending row for a range based index cursor.
154 * <p>
155 * A valid index must be specified before calling this method.
156 */
157 public CursorBuilder setSpecificRow(Object[] specificRow) {
158 setStartRow(specificRow);
159 setEndRow(specificRow);
160 return this;
161 }
162
163 /**
164 * Sets the starting and ending row for a range based index cursor to the
165 * given entry (where the given values correspond to the index's columns).
166 * <p>
167 * A valid index must be specified before calling this method.
168 */
169 public CursorBuilder setSpecificEntry(Object... specificEntry) {
170 if(specificEntry != null) {
171 setSpecificRow(_index.constructIndexRowFromEntry(specificEntry));
172 }
173 return this;
174 }
175
176
177 /**
178 * Sets the starting row for a range based index cursor.
179 * <p>
180 * A valid index must be specified before calling this method.
181 */
182 public CursorBuilder setStartRow(Object[] startRow) {
183 _startRow = startRow;
184 return this;
185 }
186
187 /**
188 * Sets the starting row for a range based index cursor to the given entry
189 * (where the given values correspond to the index's columns).
190 * <p>
191 * A valid index must be specified before calling this method.
192 */
193 public CursorBuilder setStartEntry(Object... startEntry) {
194 if(startEntry != null) {
195 setStartRow(_index.constructIndexRowFromEntry(startEntry));
196 }
197 return this;
198 }
199
200 /**
201 * Sets whether the starting row for a range based index cursor is inclusive
202 * or exclusive.
203 */
204 public CursorBuilder setStartRowInclusive(boolean inclusive) {
205 _startRowInclusive = inclusive;
206 return this;
207 }
208
209 /**
210 * Sets the ending row for a range based index cursor.
211 * <p>
212 * A valid index must be specified before calling this method.
213 */
214 public CursorBuilder setEndRow(Object[] endRow) {
215 _endRow = endRow;
216 return this;
217 }
218
219 /**
220 * Sets the ending row for a range based index cursor to the given entry
221 * (where the given values correspond to the index's columns).
222 * <p>
223 * A valid index must be specified before calling this method.
224 */
225 public CursorBuilder setEndEntry(Object... endEntry) {
226 if(endEntry != null) {
227 setEndRow(_index.constructIndexRowFromEntry(endEntry));
228 }
229 return this;
230 }
231
232 /**
233 * Sets whether the ending row for a range based index cursor is inclusive
234 * or exclusive.
235 */
236 public CursorBuilder setEndRowInclusive(boolean inclusive) {
237 _endRowInclusive = inclusive;
238 return this;
239 }
240
241 /**
242 * Returns a new cursor for the table, constructed to the given
243 * specifications.
244 */
245 public Cursor toCursor()
246 throws IOException
247 {
248 Cursor cursor = null;
249 if(_index == null) {
250 cursor = Cursor.createCursor(_table);
251 } else {
252 cursor = Cursor.createIndexCursor(_table, _index,
253 _startRow, _startRowInclusive,
254 _endRow, _endRowInclusive);
255 }
256 if(_savepoint == null) {
257 if(!_beforeFirst) {
258 cursor.afterLast();
259 }
260 } else {
261 cursor.restoreSavepoint(_savepoint);
262 }
263 return cursor;
264 }
265
266 }