001: /*
002: * Copyright 2003 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package velosurf.context;
018:
019: import java.sql.ResultSet;
020: import java.sql.SQLException;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.ArrayList;
024: import java.util.Set;
025: import java.util.HashSet;
026: import java.util.Map;
027:
028: import velosurf.model.Attribute;
029: import velosurf.model.Entity;
030: import velosurf.sql.PooledStatement;
031: import velosurf.sql.SqlUtil;
032: import velosurf.sql.RowHandler;
033: import velosurf.sql.ReadOnlyMap;
034: import velosurf.util.Logger;
035:
036: //import velosurf.util.UserContext;
037:
038: /** This class is a context wrapper for ResultSets, and provides an iteration mecanism for #foreach loops, as long as getters for values of the current row.
039: *
040: * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
041: */
042: public class RowIterator implements Iterator<Instance>, RowHandler {
043:
044: /** Build a new RowIterator.
045: *
046: * @param pooledStatement the sql statement
047: * @param resultSet the resultset
048: * @param resultEntity the resulting entity (may be null)
049: */
050: public RowIterator(PooledStatement pooledStatement,
051: ResultSet resultSet, Entity resultEntity) {
052: this .pooledStatement = pooledStatement;
053: this .resultSet = resultSet;
054: this .resultEntity = resultEntity;
055: }
056:
057: /** Returns true if the iteration has more elements.
058: *
059: * @return <code>true</code> if the iterator has more elements.
060: */
061: public boolean hasNext() {
062: boolean ret;
063: try {
064: /* always need to prefetch, as some JDBC drivers (like HSQLDB driver) seem buggued to this regard */
065: if (prefetch) {
066: pooledStatement.getConnection().enterBusyState();
067: ret = resultSet.isLast();
068: pooledStatement.getConnection().leaveBusyState();
069: } else {
070: pooledStatement.getConnection().enterBusyState();
071: ret = resultSet.next();
072: pooledStatement.getConnection().leaveBusyState();
073: if (ret) {
074: prefetch = true;
075: }
076: }
077: if (!ret)
078: pooledStatement.notifyOver();
079: return ret;
080: } catch (SQLException e) {
081: Logger.log(e);
082: pooledStatement.notifyOver();
083: return false;
084: }
085: }
086:
087: /** Returns the next element in the iteration.
088: *
089: * @return an Instance.
090: */
091: public Instance next() {
092: try {
093: if (!prefetch && !resultSet.next()) {
094: return null;
095: }
096: prefetch = false;
097: if (resultEntity != null) {
098: Instance row = null;
099: row = resultEntity.newInstance(new ReadOnlyMap(this ),
100: true);
101: return row;
102: } else
103: return new Instance(new ReadOnlyMap(this ));
104: } catch (SQLException sqle) {
105: Logger.log(sqle);
106: pooledStatement.notifyOver();
107: return null;
108: }
109: }
110:
111: // for Iterator interface, but RO (why? -> positionned updates and deletes => TODO)
112: /** not implemented.
113: */
114: public void remove() {
115: Logger.warn("'remove' not implemented");
116: }
117:
118: /** Generic getter for values of the current row. If no column corresponds to the specified name and a resulting entity has been specified, search among this entity's attributes.
119: * Note that this method is the only getter of RowIterator that cares about obfuscation - other specialized getters
120: * won't do any obfuscation.
121: *
122: * @param key the name of an existing column or attribute
123: * @return an entity, an attribute reference, an instance, a string or null
124: */
125: public Object get(Object key) {
126: String property = (String) key;
127: Object result = null;
128: boolean shouldNotifyOver = false;
129: try {
130: if (!dataAvailable())
131: return null;
132: if (resultEntity != null) {
133: property = resultEntity.resolveName(property);
134: Attribute attribute = resultEntity
135: .getAttribute(property);
136: if (attribute != null)
137: switch (attribute.getType()) {
138: case Attribute.ROWSET:
139: result = attribute.query(new ReadOnlyMap(this ));
140: break;
141: case Attribute.ROW:
142: result = attribute.fetch(new ReadOnlyMap(this ));
143: break;
144: case Attribute.SCALAR:
145: result = attribute.evaluate(new ReadOnlyMap(
146: this ));
147: break;
148: default:
149: Logger.error("Unknown attribute type for "
150: + resultEntity.getName() + "."
151: + property + "!");
152: }
153: }
154: if (result == null) {
155: if (resultEntity != null
156: && resultEntity.isObfuscated(property))
157: result = resultEntity.obfuscate(resultSet
158: .getObject(property));
159: else
160: result = resultSet.getObject(property);
161: }
162: } catch (SQLException e) {
163: Logger.log(e);
164: }
165:
166: if (shouldNotifyOver)
167: pooledStatement.notifyOver();
168: return result;
169: }
170:
171: /** Gets all the rows in a list of instances.
172: *
173: * @return a list of all the rows
174: */
175: public List<Instance> getRows() {
176: try {
177: List<Instance> ret = new ArrayList<Instance>();
178: pooledStatement.getConnection().enterBusyState();
179: if (resultEntity != null) {
180: while (!resultSet.isAfterLast() && resultSet.next()) {
181: Instance i = resultEntity.newInstance(
182: new ReadOnlyMap(this ), true);
183: ret.add(i);
184: }
185: } else {
186: while (!resultSet.isAfterLast() && resultSet.next()) {
187: Instance i = new Instance(new ReadOnlyMap(this ));
188: ret.add(i);
189: }
190: }
191: return ret;
192: } catch (SQLException sqle) {
193: Logger.log(sqle);
194: return null;
195: } finally {
196: pooledStatement.getConnection().leaveBusyState();
197: pooledStatement.notifyOver();
198: }
199: }
200:
201: public List getScalars() {
202: try {
203: List ret = new ArrayList();
204: pooledStatement.getConnection().enterBusyState();
205: while (!resultSet.isAfterLast() && resultSet.next()) {
206: ret.add(resultSet.getObject(0));
207: }
208: return ret;
209: } catch (SQLException sqle) {
210: Logger.log(sqle);
211: return null;
212: } finally {
213: pooledStatement.getConnection().leaveBusyState();
214: pooledStatement.notifyOver();
215: }
216: }
217:
218: /* */
219: public Set<String> keySet() {
220: try {
221: return new HashSet<String>(SqlUtil
222: .getColumnNames(resultSet));
223: } catch (SQLException sqle) {
224: Logger.log(sqle);
225: return null;
226: }
227: }
228:
229: /* */
230: public List<String> keyList() {
231: try {
232: return SqlUtil.getColumnNames(resultSet);
233: } catch (SQLException sqle) {
234: Logger.log(sqle);
235: return null;
236: }
237: }
238:
239: /** Check if some data is available.
240: *
241: * @exception SQLException if the internal ResultSet is not happy
242: * @return <code>true</code> if some data is available (ie the internal
243: * ResultSet is not empty, and not before first row neither after last
244: * one)
245: */
246: private boolean dataAvailable() throws SQLException {
247: if (resultSet.isBeforeFirst()) {
248: pooledStatement.getConnection().enterBusyState();
249: boolean hasNext = resultSet.next();
250: pooledStatement.getConnection().leaveBusyState();
251: if (!hasNext) {
252: pooledStatement.notifyOver();
253: return false;
254: }
255: // pooledStatement.notifyOver();
256: }
257: if (resultSet.isAfterLast()) {
258: pooledStatement.notifyOver();
259: return false;
260: }
261: return true;
262: }
263:
264: /** Source statement.
265: */
266: private PooledStatement pooledStatement = null;
267: /** Wrapped result set.
268: */
269: private ResultSet resultSet = null;
270: /** Resulting entity.
271: */
272: private Entity resultEntity = null;
273:
274: /** whether we did prefetch a row */
275: private boolean prefetch = false;
276:
277: }
|