001: ///////////////////////////////
002: // Makumba, Makumba tag library
003: // Copyright (C) 2000-2003 http://www.makumba.org
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: //
019: // -------------
020: // $Id: QueryExecution.java 1948 2007-10-26 19:35:23Z rosso_nero $
021: // $Name$
022: /////////////////////////////////////
023:
024: package org.makumba.list.engine;
025:
026: import java.util.Dictionary;
027: import java.util.HashMap;
028: import java.util.Stack;
029: import java.util.Vector;
030:
031: import javax.servlet.jsp.PageContext;
032:
033: import org.makumba.Attributes;
034: import org.makumba.LogicException;
035: import org.makumba.commons.ArrayMap;
036: import org.makumba.commons.MakumbaJspAnalyzer;
037: import org.makumba.commons.MultipleKey;
038: import org.makumba.commons.attributes.PageAttributes;
039: import org.makumba.list.tags.GenericListTag;
040: import org.makumba.list.tags.QueryTag;
041: import org.makumba.providers.QueryProvider;
042:
043: /**
044: * This class holds the listData of a mak:list or the valueQuery data of a mak:value. It determines iterationGroups at
045: * every parentIteration, and iterates through the iterationGroupData
046: *
047: * @author Cristian Bogdan
048: * @version $Id: QueryExecution.java 1948 2007-10-26 19:35:23Z rosso_nero $
049: */
050: public class QueryExecution {
051: /** The results of the query associated with the list or queryMak:value */
052: Grouper listData;
053:
054: /** The part of listData iterated for a certain parent iteration */
055: Vector iterationGroupData;
056:
057: /** The index of iteration within the iteration group */
058: public int iteration;
059:
060: /** A reference to the currentDataSet <strong>shared</strong> by all ListQueryExecutions in the listGroup */
061: Stack currentDataSet;
062:
063: /** Stores the data for queryData */
064: public HashMap valueQueryData = new HashMap();
065:
066: static final private String EXECUTIONS = "org.makumba.taglibQueryExecutions";
067:
068: static final private String CURRENT_DATA_SET = "org.makumba.currentDataSet";
069:
070: static final private Dictionary NOTHING = new ArrayMap();
071:
072: /**
073: * Allocates a currentDataSet and a container for the QueryExecutions of the listGroup. Executed by the rootList.
074: *
075: * @param pageContext
076: * The PageContext object of the current page
077: */
078: public static void startListGroup(PageContext pageContext) {
079: pageContext.setAttribute(EXECUTIONS, new HashMap());
080:
081: Stack currentDataSet = new Stack();
082: // org.makumba.view.Grouper requires the stack not be empty
083: currentDataSet.push(NOTHING);
084: pageContext.setAttribute(CURRENT_DATA_SET, currentDataSet);
085: }
086:
087: /**
088: * De-allocates all QueryExecutions in the listGroup, and the currentDataSet. Executed by the rootList.
089: *
090: * @param pageContext
091: * The PageContext object of the current page
092: */
093: public static void endListGroup(PageContext pageContext) {
094: pageContext.removeAttribute(EXECUTIONS);
095: pageContext.removeAttribute(CURRENT_DATA_SET);
096: }
097:
098: /**
099: * Gets the QueryExecution for the given key, builds one if needed. Every list tag (QueryTag) calls this method. A
100: * ListQueryTag will be built only in the first parentIteration and will be returned at next parentIterations.
101: *
102: * @param key
103: * The tag key of the tag calling the QueryExecution
104: * @param pageContext
105: * The PageContext object of the current page
106: * @param offset
107: * Offset at which the iteration should start
108: * @param limit
109: * Limit at which the iteration should stop
110: * @throws LogicException
111: */
112: public static QueryExecution getFor(MultipleKey key,
113: PageContext pageContext, String offset, String limit)
114: throws LogicException {
115: HashMap executions = (HashMap) pageContext
116: .getAttribute(EXECUTIONS);
117:
118: QueryExecution lqe = (QueryExecution) executions.get(key);
119: if (lqe == null)
120: executions.put(key, lqe = new QueryExecution(key,
121: pageContext, offset, limit));
122: return lqe;
123: }
124:
125: /**
126: * Constructs a QueryExection which executes the given query, in the given database, with the given attributes, to
127: * form the listData. Keeps the reference to the currentDataSet for future push and pop operations, finds the nested
128: * valueQueries.
129: *
130: * TODO implement an AttributeProvider to remove the dependency from controller
131: *
132: * @param key
133: * The tag key of the tag calling the QueryExecution
134: * @param pageContext
135: * The PageContext object of the current page
136: * @param offset
137: * Offset at which the iteration should start
138: * @param limit
139: * Limit at which the iteration should stop
140: * @throws LogicException
141: */
142: private QueryExecution(MultipleKey key, PageContext pageContext,
143: String offset, String limit) throws LogicException {
144: currentDataSet = (Stack) pageContext
145: .getAttribute(CURRENT_DATA_SET);
146: ComposedQuery cq = QueryTag.getQuery(GenericListTag
147: .getPageCache(pageContext, MakumbaJspAnalyzer
148: .getInstance()), key);
149: QueryProvider qep = QueryProvider.makeQueryRunner(
150: GenericListTag.getDataSourceName(pageContext),
151: (String) GenericListTag.getPageCache(pageContext,
152: MakumbaJspAnalyzer.getInstance()).retrieve(
153: MakumbaJspAnalyzer.QUERY_LANGUAGE,
154: MakumbaJspAnalyzer.QUERY_LANGUAGE));
155:
156: try {
157: Attributes.MA args = new Attributes.MA(PageAttributes
158: .getAttributes(pageContext));
159: listData = cq.execute(qep, args,
160: new Evaluator(pageContext), computeLimit(
161: pageContext, offset, 0), computeLimit(
162: pageContext, limit, -1));
163: } finally {
164: qep.close();
165: }
166: }
167:
168: /**
169: * Computes the limit from the value passed in the limit tag parameter.
170: *
171: * @param pc
172: * The PageContext object of the current page
173: * @param s
174: * The parameter value passed
175: * @param defa
176: * The default value of the limit
177: * @return The int value of the limit, if a correct one is passed as tag parameter
178: * @throws LogicException
179: */
180: public static int computeLimit(PageContext pc, String s, int defa)
181: throws LogicException {
182: if (s == null)
183: return defa;
184: s = s.trim();
185: Object o = s;
186: if (s.startsWith("$"))
187: o = PageAttributes.getAttributes(pc).getAttribute(
188: s.substring(1));
189:
190: if (o instanceof String) {
191: try {
192: return Integer.parseInt((String) o);
193: } catch (NumberFormatException nfe) {
194: throw new org.makumba.InvalidValueException(
195: "Integer expected for OFFSET and LIMIT: " + s
196: + ", value: " + o + ", type: "
197: + o.getClass().getName());
198: }
199: }
200: if (!(o instanceof Number)
201: || ((Number) o).intValue() != ((Number) o)
202: .doubleValue())
203: throw new org.makumba.InvalidValueException(
204: "Integer expected for OFFSET and LIMIT: " + s
205: + ", value: " + o + ", type: "
206: + o.getClass().getName());
207: return ((Number) o).intValue();
208: }
209:
210: /**
211: * Gets the iterationGroupData from the listData
212: *
213: * @return The size of the current iterationGroupData
214: * @see iterationGroupData
215: * @see listData
216: */
217: public int getIterationGroupData() {
218: iteration = 0;
219: iterationGroupData = listData.getData(currentDataSet);
220: return dataSize();
221: }
222:
223: /**
224: * Computes the size of the iterationGroupData
225: *
226: * @return The int value of the current iterationGroupData size
227: */
228:
229: public int dataSize() {
230: if (iterationGroupData == null
231: || iterationGroupData.size() == 0)
232: return 0;
233: else
234: return iterationGroupData.size();
235: }
236:
237: /**
238: * Computes the iterationGroupData based on listData and currentDataSet. Pushes the first currentListData to the
239: * currentDataSet. Only if the dataSize is 0, nothing is pushed to the stack, and at endIterationGroup, nothing is
240: * popped.
241: *
242: * @return The number of iterations in the iterationGroup, possibly 0.
243: */
244: public int onParentIteration() {
245: getIterationGroupData();
246: int n = dataSize();
247: if (n != 0)
248: currentDataSet.push(currentListData());
249: return n;
250: }
251:
252: /**
253: * Pops the previous currentListData from the currentDataSet and checks if there are more iterations to be made
254: * within the iterationGroup. If there are, adds the currentListData to the currentDataSet; else pushes a DUMMY data
255: * (cfr bug 555).
256: *
257: * @return The current iteration position, relative to the start of the interationGroup or -1 if there are no more
258: * results
259: */
260: public int nextGroupIteration() {
261: valueQueryData.clear();
262: currentDataSet.pop();
263: iteration++;
264: if (iteration == iterationGroupData.size()) {
265: currentDataSet.push(NOTHING); // push a dummy Data; will be removed by endIterationGroup
266: return -1;
267: }
268: currentDataSet.push(currentListData());
269: return iteration;
270: }
271:
272: /**
273: * Pops the the last data of an iterationGroup. In case of an empty-body/simple QueryTag, this will pop the first
274: * element; otherwise it pops the dummy data. cfr Bug 555.
275: */
276: public void endIterationGroup() {
277: if (dataSize() > 0) {
278: currentDataSet.pop();
279: }
280: }
281:
282: /**
283: * Returns the data of the current iteration
284: *
285: * @return The current listData
286: */
287: public ArrayMap currentListData() {
288: return (ArrayMap) iterationGroupData.elementAt(iteration);
289: }
290:
291: }
|