001: package com.technoetic.xplanner.db;
002:
003: import java.util.Collection;
004: import java.util.Hashtable;
005: import java.util.Iterator;
006: import java.util.Locale;
007: import javax.servlet.http.HttpServletRequest;
008:
009: import net.sf.hibernate.Hibernate;
010: import net.sf.hibernate.type.Type;
011: import org.apache.log4j.Logger;
012: import org.apache.struts.Globals;
013: import org.apache.struts.util.MessageResources;
014:
015: import com.technoetic.xplanner.db.hibernate.ThreadSession;
016: import com.technoetic.xplanner.domain.Iteration;
017: import com.technoetic.xplanner.domain.Task;
018:
019: public class IterationStatisticsQuery {
020: private Logger log = Logger.getLogger(getClass());
021:
022: private String query;
023: private int iterationId = -1;
024: private Collection tasks = null;
025: private Iteration iteration = null;
026:
027: private Hashtable taskTypeCount = null;
028: private Hashtable taskDispositionCount = null;
029: private Hashtable taskTypeEstimatedHours = null;
030: private Hashtable taskDispositionEstimatedHours = null;
031: private Hashtable taskTypeActualHours = null;
032: private Hashtable taskDispositionActualHours = null;
033:
034: private Locale locale = null;
035: private MessageResources resources = null;
036:
037: /**
038: * Clears any data that this class may be caching.
039: */
040: private void clearCache() {
041: tasks = null;
042: iteration = null;
043:
044: taskTypeCount = null;
045: taskDispositionCount = null;
046: taskTypeEstimatedHours = null;
047: taskDispositionEstimatedHours = null;
048: taskTypeActualHours = null;
049: taskDispositionActualHours = null;
050: }
051:
052: /**
053: * Used to set the HTTP request object. This may be required by other classes to access resource strings.
054: *
055: * @param request the HTTP request object.
056: */
057: public void setRequest(HttpServletRequest request) {
058: try {
059: locale = (Locale) request.getSession().getAttribute(
060: Globals.LOCALE_KEY);
061: } catch (IllegalStateException e) { // Invalidated session
062: locale = null;
063: }
064: if (locale == null) {
065: locale = Locale.getDefault();
066: }
067:
068: resources = (MessageResources) request
069: .getAttribute(Globals.MESSAGES_KEY);
070: }
071:
072: /**
073: * Returns the string, stored using the specified key from the resource bundle. Note, this method should
074: * only be invoked after <code>setRequest()</code> has been used to specify the HTTP request object.
075: *
076: * @param key used to idenfity the string to be returned
077: * @return the string loaded from the resource bundle (in the request locale).
078: */
079: public String getResourceString(String key) {
080: return resources.getMessage(locale, key);
081: }
082:
083: /**
084: * Specifies the iteration for which data should be gathered.
085: *
086: * @param iterationId the iteration for which data is to be gathered.
087: */
088: public synchronized void setIterationId(int iterationId) {
089: clearCache();
090: this .iterationId = iterationId;
091: }
092:
093: /**
094: * Returns the iteration for this class is gathering data.
095: *
096: * @return the iteration id for which data is being gathered.
097: */
098: public synchronized int getIterationId() {
099: return iterationId;
100: }
101:
102: /**
103: * Returns the set of tasks which make up the iteration. Note, this method should only be invoked after
104: * <code>setIterationId(int)</code> has been used to specify the iteration and <code>setHibernateSession(Session)</code>
105: * has been used to set the database session.
106: *
107: * @return all the tasks which make up the iteration.
108: */
109: public synchronized java.util.Collection getIterationTasks() {
110: if (tasks == null) {
111: try {
112: try {
113: if (query == null) {
114: //DEBT: externalize the query
115: query = "select distinct task "
116: + " from task in class com.technoetic.xplanner.domain.Task, "
117: + " iteration in class com.technoetic.xplanner.domain.Iteration, "
118: + " story in class com.technoetic.xplanner.domain.UserStory "
119: + " where "
120: + " task.story.id = story.id and story.iterationId = iteration.id and"
121: + " iteration.id = ?";
122: }
123: tasks = ThreadSession.get().find(query,
124: new Object[] { new Integer(iterationId) },
125: new Type[] { Hibernate.INTEGER });
126: } catch (Exception ex) {
127: log.error("query error", ex);
128: }
129: } catch (Exception ex) {
130: log.error("error in iteration query", ex);
131: }
132: }
133:
134: return tasks;
135: }
136:
137: /**
138: * Returns information about the iteration for which data is being gathered.
139: * Note, this method should only be invoked after <code>setIterationId(int)</code> has been used to
140: * specify the iteration and <code>setHibernateSession(Session)</code> has been used to set the database session.
141: *
142: * @return information about the iteration.
143: */
144: public synchronized Iteration getIteration() {
145: if (iteration == null) {
146: try {
147: iteration = (Iteration) ThreadSession.get().load(
148: Iteration.class, new Integer(iterationId));
149: } catch (Exception ex) {
150: log.error("error loading iteration [" + iterationId
151: + "]", ex);
152: }
153: }
154: return iteration;
155: }
156:
157: public Hashtable getTaskCountByType() {
158: if (taskTypeCount == null) {
159: taskTypeCount = new TypeAggregator() {
160: protected double getValue(Task task) {
161: return 1;
162: }
163: }.aggregateByGroup();
164: }
165: return taskTypeCount;
166: }
167:
168: public Hashtable getTaskCountByDisposition() {
169: if (taskDispositionCount == null) {
170: taskDispositionCount = new DispositionAggregator() {
171: protected double getValue(Task task) {
172: return 1;
173: }
174: }.aggregateByGroup();
175: }
176: return taskDispositionCount;
177: }
178:
179: public Hashtable getTaskEstimatedHoursByType() {
180: if (taskTypeEstimatedHours == null) {
181: taskTypeEstimatedHours = new TypeAggregator(true) {
182: protected double getValue(Task task) {
183: return task.getEstimatedHours();
184: }
185: }.aggregateByGroup();
186: }
187: return taskTypeEstimatedHours;
188: }
189:
190: public Hashtable getTaskEstimatedHoursByDisposition() {
191: if (taskDispositionEstimatedHours == null) {
192: taskDispositionEstimatedHours = new DispositionAggregator(
193: true) {
194: protected double getValue(Task task) {
195: return task.getEstimatedHours();
196: }
197: }.aggregateByGroup();
198: }
199: return taskDispositionEstimatedHours;
200: }
201:
202: public Hashtable getTaskActualHoursByType() {
203: if (taskTypeActualHours == null) {
204: taskTypeActualHours = new TypeAggregator(true) {
205: protected double getValue(Task task) {
206: return task.getActualHours();
207: }
208: }.aggregateByGroup();
209: }
210: return taskTypeActualHours;
211: }
212:
213: public Hashtable getTaskActualHoursByDisposition() {
214: if (taskDispositionActualHours == null) {
215: taskDispositionActualHours = new DispositionAggregator(true) {
216: protected double getValue(Task task) {
217: return task.getActualHours();
218: }
219: }.aggregateByGroup();
220: }
221: return taskDispositionActualHours;
222: }
223:
224: abstract class Aggregator {
225: protected boolean onlyCompletedTask;
226:
227: public Aggregator() {
228: }
229:
230: public Aggregator(boolean onlyCompletedTask) {
231: this .onlyCompletedTask = onlyCompletedTask;
232: }
233:
234: public Hashtable aggregateByGroup() {
235: Hashtable valuesByGroup = new Hashtable();
236:
237: Iterator taskItr = getIterationTasks().iterator();
238:
239: while (taskItr.hasNext()) {
240: Task task = (Task) taskItr.next();
241:
242: if (!apply(task))
243: continue;
244:
245: Double sum = (Double) valuesByGroup.get(getGroup(task));
246: if (sum == null)
247: sum = new Double(0);
248:
249: Double newSum = new Double(sum.intValue()
250: + getValue(task));
251: valuesByGroup.put(getGroup(task), newSum);
252: }
253: return valuesByGroup;
254: }
255:
256: protected boolean apply(Task task) {
257: return !onlyCompletedTask || task.isCompleted();
258: }
259:
260: abstract protected double getValue(Task task);
261:
262: abstract protected String getGroup(Task task);
263: }
264:
265: private abstract class TypeAggregator extends Aggregator {
266:
267: public TypeAggregator() {
268: }
269:
270: public TypeAggregator(boolean onlyCompletedTask) {
271: super (onlyCompletedTask);
272: }
273:
274: protected String getGroup(Task task) {
275: return task.getType();
276: }
277: }
278:
279: private abstract class DispositionAggregator extends Aggregator {
280:
281: public DispositionAggregator() {
282: }
283:
284: public DispositionAggregator(boolean onlyCompletedTask) {
285: super (onlyCompletedTask);
286: }
287:
288: protected String getGroup(Task task) {
289: return getResourceString(task.getDisposition().getNameKey());
290: }
291: }
292: }
|