001: package org.apache.ojb.odmg.oql;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.io.StringReader;
019: import java.util.Enumeration;
020: import java.util.Iterator;
021: import java.util.ListIterator;
022: import java.util.Vector;
023: import java.util.List;
024:
025: import antlr.RecognitionException;
026: import antlr.TokenStreamException;
027: import org.apache.ojb.broker.ManageableCollection;
028: import org.apache.ojb.broker.PBKey;
029: import org.apache.ojb.broker.PersistenceBroker;
030: import org.apache.ojb.broker.PersistenceBrokerFactory;
031: import org.apache.ojb.broker.accesslayer.OJBIterator;
032: import org.apache.ojb.broker.query.BetweenCriteria;
033: import org.apache.ojb.broker.query.Criteria;
034: import org.apache.ojb.broker.query.Query;
035: import org.apache.ojb.broker.query.ReportQuery;
036: import org.apache.ojb.broker.query.SelectionCriteria;
037: import org.apache.ojb.broker.util.collections.ManageableArrayList;
038: import org.apache.ojb.broker.util.configuration.Configurable;
039: import org.apache.ojb.broker.util.configuration.Configuration;
040: import org.apache.ojb.broker.util.configuration.ConfigurationException;
041: import org.apache.ojb.broker.util.logging.Logger;
042: import org.apache.ojb.broker.util.logging.LoggerFactory;
043: import org.apache.ojb.odmg.ImplementationImpl;
044: import org.apache.ojb.odmg.OdmgConfiguration;
045: import org.apache.ojb.odmg.PBCapsule;
046: import org.apache.ojb.odmg.RuntimeObject;
047: import org.apache.ojb.odmg.TransactionImpl;
048: import org.odmg.QueryInvalidException;
049: import org.odmg.Transaction;
050:
051: /**
052: * The OQL query interface implementation.
053: *
054: * @version $Id: OQLQueryImpl.java,v 1.18.2.5 2005/12/21 22:29:53 tomdz Exp $
055: */
056: public class OQLQueryImpl implements EnhancedOQLQuery, Configurable {
057: private Logger log = LoggerFactory.getLogger(OQLQueryImpl.class);
058:
059: /**
060: * holds the compiled query object
061: */
062: private Query query = null;
063: private ListIterator bindIterator = null;
064: private ImplementationImpl odmg;
065:
066: public OQLQueryImpl(ImplementationImpl odmg) {
067: this .odmg = odmg;
068: }
069:
070: /**
071: * @deprecated
072: * @param pbKey
073: */
074: public OQLQueryImpl(PBKey pbKey) {
075: }
076:
077: /**
078: * returns the compiled query object
079: */
080: public Query getQuery() {
081: return query;
082: }
083:
084: /**
085: * returns the collection type to be used to contain oql query result sets
086: */
087: protected Class getCollectionClass() {
088: return odmg.getOqlCollectionClass();
089: }
090:
091: /**
092: * Bind a parameter to the query.
093: * A parameter is denoted in the query string passed to <code>create</code> by <i>$i</i>,
094: * where <i>i</i> is the rank of the parameter, beginning with 1.
095: * The parameters are set consecutively by calling this method <code>bind</code>.
096: * The <i>ith</i> variable is set by the <i>ith</i> call to the <code>bind</code> method.
097: * If any of the <i>$i</i> are not set by a call to <code>bind</code> at the point
098: * <code>execute</code> is called, <code>QueryParameterCountInvalidException</code> is thrown.
099: * The parameters must be objects, and the result is an <code>Object</code>.
100: * Objects must be used instead of primitive types (<code>Integer</code> instead
101: * of <code>int</code>) for passing the parameters.
102: * <p>
103: * If the parameter is of the wrong type,
104: * <code>QueryParameterTypeInvalidException</code> is thrown.
105: * After executing a query, the parameter list is reset.
106: * @param parameter A value to be substituted for a query parameter.
107: * @exception org.odmg.QueryParameterCountInvalidException The number of calls to
108: * <code>bind</code> has exceeded the number of parameters in the query.
109: * @exception org.odmg.QueryParameterTypeInvalidException The type of the parameter does
110: * not correspond with the type of the parameter in the query.
111: */
112: public void bind(Object parameter)
113: throws org.odmg.QueryParameterCountInvalidException,
114: org.odmg.QueryParameterTypeInvalidException {
115: try {
116: SelectionCriteria crit = (SelectionCriteria) getBindIterator()
117: .next();
118: crit.bind(parameter);
119:
120: // BRJ: bind is called twice for between
121: if (crit instanceof BetweenCriteria && !crit.isBound()) {
122: getBindIterator().previous();
123: }
124: } catch (Exception e) {
125: throw new org.odmg.QueryParameterCountInvalidException(e
126: .getMessage());
127: }
128: }
129:
130: private Vector flatten(Criteria crit, Vector acc) {
131: Enumeration e = crit.getElements();
132: while (e.hasMoreElements()) {
133: Object o = e.nextElement();
134: if (o instanceof Criteria) {
135: Criteria pc = (Criteria) o;
136: flatten(pc, acc);
137: } else {
138: SelectionCriteria c = (SelectionCriteria) o;
139: // BRJ : only add bindable criteria
140: if (!c.isBound()) {
141: acc.add(c);
142: }
143: }
144: }
145: return acc;
146: }
147:
148: /**
149: * Create an OQL query from the string parameter.
150: * In order to execute a query, an <code>OQLQuery</code> object must be created
151: * by calling <code>Implementation.newOQLQuery</code>, then calling the
152: * <code>create</code> method with the query string.
153: * The <code>create</code> method might throw <code>QueryInvalidException</code>
154: * if the query could not be compiled properly. Some implementations may not want
155: * to compile the query before <code>execute</code> is called. In this case
156: * <code>QueryInvalidException</code> is thrown when <code>execute</code> is called.
157: * @param queryString An OQL query.
158: * @exception QueryInvalidException The query syntax is invalid.
159: */
160: public void create(String queryString)
161: throws org.odmg.QueryInvalidException {
162: create(queryString, Query.NO_START_AT_INDEX,
163: Query.NO_END_AT_INDEX);
164: }
165:
166: public void create(String queryString, int startAtIndex,
167: int endAtIndex) throws QueryInvalidException {
168: if (log.isDebugEnabled())
169: log.debug("create query for query-string: " + queryString);
170: /**
171: * Check preconditions.
172: * End Index cannot be set before start index.
173: * End Index cannot equal StartAtIndex
174: */
175: if ((endAtIndex != Query.NO_END_AT_INDEX)
176: && (endAtIndex < startAtIndex)) {
177: throw new QueryInvalidException(
178: "endAtIndex must be greater than startAtIndex");
179: }
180: if (((endAtIndex != Query.NO_END_AT_INDEX) && (startAtIndex != Query.NO_START_AT_INDEX))
181: && (endAtIndex == startAtIndex)) {
182: throw new QueryInvalidException(
183: "endAtIndex cannot be set equal to startAtIndex");
184: }
185:
186: try {
187: // Query query = QueryPool.getQuery(queryString);
188: // Use the OQL parser to transform a query string to a valid org.apache.ojb.broker.query object
189: Query _query;
190: StringReader reader = new StringReader(queryString);
191: OQLLexer lexer = new OQLLexer(reader);
192: OQLParser parser = new OQLParser(lexer);
193: _query = parser.buildQuery();
194: setBindIterator(flatten(_query.getCriteria(), new Vector())
195: .listIterator());
196: _query.setStartAtIndex(startAtIndex);
197: _query.setEndAtIndex(endAtIndex);
198: setQuery(_query);
199: } catch (RecognitionException e) {
200: throw new QueryInvalidException(e.getMessage());
201: } catch (TokenStreamException e) {
202: throw new QueryInvalidException(e.getMessage());
203: }
204: }
205:
206: /**
207: * Execute the query.
208: * After executing a query, the parameter list is reset.
209: * Some implementations may throw additional exceptions that are also derived
210: * from <code>ODMGException</code>.
211: * @return The object that represents the result of the query.
212: * The returned data, whatever its OQL type, is encapsulated into an object.
213: * For instance, when OQL returns an integer, the result is put into an
214: * <code>Integer</code> object. When OQL returns a collection (literal or object),
215: * the result is always a Java collection object of the same kind
216: * (for instance, a <code>DList</code>).
217: * @exception org.odmg.QueryException An exception has occurred while executing the query.
218: */
219: public Object execute() throws org.odmg.QueryException {
220: if (log.isDebugEnabled())
221: log.debug("Start execute query");
222:
223: //obtain current ODMG transaction
224: TransactionImpl tx = odmg.getTxManager().getTransaction();
225: // create PBCapsule
226: PBCapsule capsule = null;
227: ManageableCollection result = null;
228:
229: try {
230: capsule = new PBCapsule(odmg.getCurrentPBKey(), tx);
231: PersistenceBroker broker = capsule.getBroker();
232:
233: // ask the broker to perfom the query.
234: // the concrete result type is configurable
235:
236: if (!(query instanceof ReportQuery)) {
237: result = broker.getCollectionByQuery(this
238: .getCollectionClass(), query);
239: performLockingIfRequired(tx, broker, result);
240: } else {
241: Iterator iter = null;
242: result = new ManageableArrayList();
243: iter = broker.getReportQueryIteratorByQuery(query);
244: try {
245: while (iter.hasNext()) {
246: Object[] res = (Object[]) iter.next();
247:
248: if (res.length == 1) {
249: if (res[0] != null) // skip null values
250: {
251: result.ojbAdd(res[0]);
252: }
253: } else {
254: // skip null tuples
255: for (int i = 0; i < res.length; i++) {
256: if (res[i] != null) {
257: result.ojbAdd(res);
258: break;
259: }
260: }
261: }
262: }
263: } finally {
264: if (iter instanceof OJBIterator) {
265: ((OJBIterator) iter).releaseDbResources();
266: }
267: }
268: }
269: // reset iterator to start of list so we can reuse this query
270: ListIterator it = getBindIterator();
271: while (it.hasPrevious()) {
272: it.previous();
273: }
274: } finally {
275: if (capsule != null)
276: capsule.destroy();
277: }
278: return result;
279: }
280:
281: protected void performLockingIfRequired(TransactionImpl tx,
282: PersistenceBroker broker, ManageableCollection result) {
283: // if tx is available and implicit locking is required,
284: // we do READ-lock all found objects
285: if ((tx != null) && tx.isImplicitLocking() && tx.isOpen()) {
286: // read-lock all resulting objects to the current transaction
287: Iterator iter = result.ojbIterator();
288: Object toBeLocked = null;
289: try {
290: List regList = tx.getRegistrationList();
291: while (iter.hasNext()) {
292: toBeLocked = iter.next();
293: RuntimeObject rt = new RuntimeObject(toBeLocked,
294: tx, false);
295: tx.lockAndRegister(rt, Transaction.READ, true,
296: regList);
297: }
298: } finally {
299: tx.clearRegistrationList();
300: }
301: }
302: }
303:
304: protected OdmgConfiguration getConfiguration() {
305: OdmgConfiguration config = (OdmgConfiguration) PersistenceBrokerFactory
306: .getConfigurator().getConfigurationFor(null);
307: return config;
308: }
309:
310: /**
311: * Sets the query.
312: * @param query The query to set
313: */
314: private void setQuery(Query query) {
315: this .query = query;
316: }
317:
318: /**
319: * Gets the bindIterator.
320: * @return Returns a ListIterator
321: */
322: protected ListIterator getBindIterator() {
323: return bindIterator;
324: }
325:
326: /**
327: * Sets the bindIterator.
328: * @param bindIterator The bindIterator to set
329: */
330: private void setBindIterator(ListIterator bindIterator) {
331: this .bindIterator = bindIterator;
332: }
333:
334: /**
335: * @see Configurable#configure(Configuration)
336: */
337: public void configure(Configuration pConfig)
338: throws ConfigurationException {
339: }
340:
341: public int fullSize() {
342: return this.query.fullSize();
343: }
344: }
|