001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: QueryStatement.java,v 1.6 2003/08/11 16:01:52 pierreg0 Exp $
009: */
010:
011: package com.triactive.jdo.store;
012:
013: import java.util.ArrayList;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import javax.jdo.JDOFatalInternalException;
017:
018: public class QueryStatement {
019: protected final StoreManager storeMgr;
020: protected final DatabaseAdapter dba;
021: protected final SQLIdentifier defaultRangeVar;
022: protected final TableExpression initialTableExpr;
023:
024: protected HashMap tableExprsByRangeVar = new HashMap();
025: protected boolean distinctResults = false;
026: protected ArrayList selected = new ArrayList();
027: protected ArrayList joins = new ArrayList();
028: protected BooleanExpression whereExpr = null;
029: protected StatementText orderByList = null;
030: protected StatementText stmtText = null;
031:
032: public QueryStatement(Table initialTable) {
033: this .storeMgr = initialTable.getStoreManager();
034: this .dba = storeMgr.getDatabaseAdapter();
035:
036: defaultRangeVar = new TableIdentifier(dba, "this");
037:
038: initialTableExpr = newTableExpression(initialTable,
039: defaultRangeVar);
040: tableExprsByRangeVar.put(defaultRangeVar, initialTableExpr);
041: }
042:
043: public QueryStatement(Table initialTable,
044: SQLIdentifier initialRangeVar) {
045: this .storeMgr = initialTable.getStoreManager();
046: this .dba = storeMgr.getDatabaseAdapter();
047:
048: defaultRangeVar = new TableIdentifier(dba, "this");
049:
050: initialTableExpr = newTableExpression(initialTable,
051: initialRangeVar);
052: tableExprsByRangeVar.put(initialRangeVar, initialTableExpr);
053: }
054:
055: protected void assertNotFrozen() {
056: if (stmtText != null)
057: throw new JDOFatalInternalException(
058: "A query statement cannot be modified after being output");
059: }
060:
061: public StoreManager getStoreManager() {
062: return storeMgr;
063: }
064:
065: public TableExpression getTableExpression(SQLIdentifier rangeVar) {
066: return (TableExpression) tableExprsByRangeVar.get(rangeVar);
067: }
068:
069: public TableExpression getDefaultTableExpression() {
070: return getTableExpression(defaultRangeVar);
071: }
072:
073: public TableExpression newTableExpression(Table mainTable,
074: SQLIdentifier rangeVar) {
075: assertNotFrozen();
076:
077: TableExpression te = (TableExpression) tableExprsByRangeVar
078: .get(rangeVar);
079:
080: if (te == null) {
081: te = dba.newTableExpression(this , mainTable, rangeVar);
082:
083: tableExprsByRangeVar.put(rangeVar, te);
084: } else {
085: if (!te.getMainTable().equals(mainTable))
086: throw new JDOFatalInternalException("Range variable "
087: + rangeVar + " already in use in query: "
088: + this );
089: }
090:
091: return te;
092: }
093:
094: public boolean getDistinctResults() {
095: return distinctResults;
096: }
097:
098: public void setDistinctResults(boolean distinctResults) {
099: assertNotFrozen();
100:
101: this .distinctResults = distinctResults;
102: }
103:
104: public int select(Column col) {
105: return select(defaultRangeVar, col);
106: }
107:
108: public int select(SQLIdentifier rangeVar, Column col) {
109: assertNotFrozen();
110:
111: String columnID = getColumn(rangeVar, col).toString();
112:
113: if (!selected.contains(columnID))
114: selected.add(columnID);
115:
116: return selected.indexOf(columnID) + 1;
117: }
118:
119: public int columnsSelected() {
120: return selected.size();
121: }
122:
123: public QueryColumn getColumn(Column col) {
124: return getColumn(defaultRangeVar, col);
125: }
126:
127: public QueryColumn getColumn(SQLIdentifier rangeVar, Column col) {
128: TableExpression te = (TableExpression) tableExprsByRangeVar
129: .get(rangeVar);
130:
131: if (te == null)
132: throw new JDOFatalInternalException(
133: "No such range variable: " + rangeVar);
134:
135: return getColumn(te, col);
136: }
137:
138: public QueryColumn getColumn(TableExpression te, Column col) {
139: return new QueryColumn(te, col);
140: }
141:
142: public void innerJoin(QueryColumn from, QueryColumn to) {
143: assertNotFrozen();
144:
145: joins.add(new Join("INNER JOIN", from, to));
146: }
147:
148: public void leftOuterJoin(QueryColumn from, QueryColumn to) {
149: assertNotFrozen();
150:
151: joins.add(new Join("LEFT OUTER JOIN", from, to));
152: }
153:
154: public void rightOuterJoin(QueryColumn from, QueryColumn to) {
155: assertNotFrozen();
156:
157: joins.add(new Join("RIGHT OUTER JOIN", from, to));
158: }
159:
160: public void andCondition(BooleanExpression condition) {
161: assertNotFrozen();
162:
163: if (whereExpr == null)
164: whereExpr = condition;
165: else
166: whereExpr = whereExpr.and(condition);
167: }
168:
169: public void setOrdering(SQLExpression[] exprs, boolean[] descending) {
170: assertNotFrozen();
171:
172: boolean needsSelect = dba.includeOrderByColumnsInSelect();
173:
174: orderByList = new StatementText();
175:
176: for (int i = 0; i < exprs.length; ++i) {
177: if (i > 0)
178: orderByList.append(',');
179:
180: orderByList.append(exprs[i]);
181:
182: if (descending[i])
183: orderByList.append(" DESC");
184:
185: if (needsSelect) {
186: Iterator j = exprs[i].toStatementText()
187: .getReferencedColumns().iterator();
188:
189: while (j.hasNext()) {
190: String columnRef = j.next().toString();
191:
192: if (!selected.contains(columnRef))
193: selected.add(columnRef);
194: }
195: }
196: }
197: }
198:
199: public StatementText toStatementText() {
200: if (stmtText == null) {
201: stmtText = new StatementText("SELECT ");
202:
203: if (distinctResults)
204: stmtText.append("DISTINCT ");
205:
206: Iterator i = selected.iterator();
207:
208: while (i.hasNext()) {
209: stmtText.append(i.next());
210:
211: if (i.hasNext())
212: stmtText.append(',');
213: }
214:
215: stmtText.append(" FROM ").append(initialTableExpr);
216:
217: i = joins.iterator();
218:
219: while (i.hasNext())
220: stmtText.append(' ').append(i.next());
221:
222: if (whereExpr != null)
223: stmtText.append(" WHERE ").append(whereExpr);
224:
225: if (orderByList != null)
226: stmtText.append(" ORDER BY ").append(orderByList);
227: }
228:
229: return stmtText;
230: }
231:
232: public String toString() {
233: return toStatementText().toString();
234: }
235:
236: protected static class Join {
237: private final String type;
238: private final TableExpression te;
239: private final String condition;
240:
241: public Join(String type, QueryColumn from, QueryColumn to) {
242: this .type = type;
243:
244: /*
245: * Believe it or not, the order of converting things to string here
246: * is important; that's why 'condition' is constructed in advance.
247: * QueryColumn.toString() has a side effect: if its TableExpression
248: * employs a subquery it causes the column to get SELECTed in that
249: * subquery.
250: *
251: * to.te.toString() actually freezes to.te, which means you can no
252: * longer call to.toString(), so they have to be called in the other
253: * order.
254: *
255: * I'm not particularly happy with it either but I haven't yet
256: * figured out a better way to model things.
257: */
258: te = to.te;
259: condition = from + " = " + to;
260: }
261:
262: public String toString() {
263: return type + " " + te + " ON " + condition;
264: }
265: }
266:
267: public class QueryColumn {
268: public final TableExpression te;
269: public final Column column;
270:
271: private QueryColumn(TableExpression te, Column column) {
272: this .te = te;
273: this .column = column;
274: }
275:
276: public QueryStatement getQueryStatement() {
277: return QueryStatement.this ;
278: }
279:
280: public int hashCode() {
281: return te.hashCode() ^ column.hashCode();
282: }
283:
284: public boolean equals(Object o) {
285: if (o == this )
286: return true;
287:
288: if (!(o instanceof QueryColumn))
289: return false;
290:
291: QueryColumn qsc = (QueryColumn) o;
292:
293: return te.equals(qsc.te) && column.equals(qsc.column);
294: }
295:
296: public String toString() {
297: return te.referenceColumn(column);
298: }
299: }
300: }
|