001: package org.tigris.scarab.reports;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2002 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by Collab.Net <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of Collab.Net.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of Collab.Net.
047: */
048:
049: // JDK classes
050: import java.util.ArrayList;
051: import java.util.Date;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.HashMap;
056: import java.io.StringReader;
057:
058: import org.apache.log4j.Logger;
059:
060: // Turbine classes
061: import org.apache.torque.TorqueException;
062:
063: import org.tigris.scarab.tools.localization.L10NKeySet;
064: import org.tigris.scarab.util.word.IssueSearch;
065: import org.tigris.scarab.om.ScarabUserManager;
066: import org.tigris.scarab.om.Module;
067: import org.tigris.scarab.om.IssueType;
068: import org.tigris.scarab.om.ModuleManager;
069: import org.tigris.scarab.om.AttributeValue;
070: import org.tigris.scarab.om.AttributeOptionManager;
071: import org.tigris.scarab.om.AttributeOption;
072: import org.tigris.scarab.om.AttributeManager;
073: import org.tigris.scarab.om.Attribute;
074: import org.tigris.scarab.om.ScarabUser;
075: import org.tigris.scarab.om.Scope;
076: import org.tigris.scarab.om.MITList;
077: import org.tigris.scarab.om.MITListItem;
078: import org.tigris.scarab.util.Log;
079: import org.tigris.scarab.util.ScarabConstants;
080: import org.tigris.scarab.services.security.ScarabSecurity;
081:
082: import org.apache.commons.betwixt.io.BeanReader;
083:
084: /**
085: * This class is a bridge between the xml related classes for defining a
086: * report and the business objects that are used within the screens
087: * to configure the report.
088: */
089: public class ReportBridge implements java.io.Serializable,
090: org.apache.fulcrum.intake.Retrievable // do we want this?
091: {
092:
093: private ScarabUser generatedBy;
094: private Date generatedDate;
095:
096: private org.tigris.scarab.om.Report torqueReport;
097: private ReportDefinition reportDefn;
098: private ReportHeading newHeading;
099:
100: public ReportBridge() {
101: torqueReport = new org.tigris.scarab.om.Report();
102: reportDefn = new ReportDefinition();
103: }
104:
105: public ReportBridge(org.tigris.scarab.om.Report report)
106: throws Exception {
107: torqueReport = report;
108: populate(report.getQueryString());
109: }
110:
111: public ReportDefinition getReportDefinition() {
112: return reportDefn;
113: }
114:
115: public ReportHeading getNewHeading() {
116: if (newHeading == null) {
117: newHeading = new ReportHeading();
118: }
119: return newHeading;
120: }
121:
122: // I'm not sure I want this but for now we will implement the Retrievable
123: // interface
124: public String getQueryKey() {
125: return torqueReport.getQueryKey();
126: }
127:
128: public void setQueryKey(String key) throws TorqueException {
129: torqueReport.setQueryKey(key);
130: }
131:
132: public String getName() {
133: return torqueReport.getName();
134: }
135:
136: public void setName(String name) {
137: torqueReport.setName(name);
138: reportDefn.setName(name);
139: }
140:
141: public String getDescription() {
142: return torqueReport.getDescription();
143: }
144:
145: public void setDescription(String name) {
146: torqueReport.setDescription(name);
147: reportDefn.setDescription(name);
148: }
149:
150: public String getFormat() {
151: return reportDefn.getFormat();
152: }
153:
154: public void setFormat(String format) {
155: reportDefn.setFormat(format);
156: }
157:
158: public boolean getDeleted() {
159: return torqueReport.getDeleted();
160: }
161:
162: public void setDeleted(boolean b) {
163: torqueReport.setDeleted(b);
164: }
165:
166: public Integer getScopeId() {
167: return torqueReport.getScopeId();
168: }
169:
170: public void setScopeId(Integer id) throws TorqueException {
171: torqueReport.setScopeId(id);
172: }
173:
174: public Integer getUserId() {
175: return torqueReport.getUserId();
176: }
177:
178: public void setUserId(Integer id) throws TorqueException {
179: torqueReport.setUserId(id);
180: }
181:
182: public Integer getReportId() {
183: return torqueReport.getReportId();
184: }
185:
186: public void setReportId(Integer id) {
187: torqueReport.setReportId(id);
188: }
189:
190: public Scope getScope() throws TorqueException {
191: return torqueReport.getScope();
192: }
193:
194: /**
195: * Get the value of module.
196: * @return value of module.
197: */
198: public Module getModule() throws TorqueException {
199: Module module = null;
200: if (torqueReport.getModuleId() != null) {
201: module = ModuleManager.getInstance(torqueReport
202: .getModuleId());
203: }
204:
205: return module;
206: }
207:
208: /**
209: * Set the value of module.
210: * @param v Value to assign to module.
211: */
212: public void setModule(Module v) throws TorqueException {
213: reportDefn.setModuleIssueTypes(null);
214: if (v == null) {
215: torqueReport.setModuleId((Integer) null);
216: } else {
217: torqueReport.setModuleId(v.getModuleId());
218: if (torqueReport.getIssueTypeId() != null) {
219: ModuleIssueType mit = new ModuleIssueType();
220: mit.setModuleId(v.getModuleId());
221: mit.setIssueTypeId(torqueReport.getIssueTypeId());
222: reportDefn.addModuleIssueType(mit);
223: }
224: }
225: }
226:
227: /**
228: * Set the value of module.
229: * @param v Value to assign to module.
230: */
231: public void setIssueType(IssueType v) throws TorqueException {
232: reportDefn.setModuleIssueTypes(null);
233: if (v == null) {
234: // issue type id cannot be null
235: torqueReport.setIssueTypeId(ScarabConstants.INTEGER_0);
236: } else {
237: torqueReport.setIssueTypeId(v.getIssueTypeId());
238: if (torqueReport.getModuleId() != null) {
239: ModuleIssueType mit = new ModuleIssueType();
240: mit.setModuleId(torqueReport.getModuleId());
241: mit.setIssueTypeId(v.getIssueTypeId());
242: reportDefn.addModuleIssueType(mit);
243: }
244: }
245: }
246:
247: /**
248: * Checks permission in all modules involved in report
249: */
250: private boolean hasPermission(String permission, ScarabUser user) {
251: boolean result = false;
252: try {
253: MITList mitlist = getMITList();
254: result = mitlist.isSingleModule() ? user.hasPermission(
255: permission, mitlist.getModule()) : user
256: .hasPermission(permission, mitlist.getModules());
257: } catch (Exception e) {
258: result = false;
259: Log.get().error(e);
260: }
261: return result;
262: }
263:
264: public boolean isEditable(ScarabUser user) {
265: return torqueReport.isNew()
266: || (Scope.PERSONAL__PK
267: .equals(torqueReport.getScopeId()) && user
268: .getUserId().equals(torqueReport.getUserId()))
269: || (Scope.MODULE__PK.equals(torqueReport.getScopeId()) && hasPermission(
270: ScarabSecurity.MODULE__EDIT, user));
271: }
272:
273: public boolean isDeletable(ScarabUser user) {
274: return (Scope.PERSONAL__PK.equals(torqueReport.getScopeId()) && user
275: .getUserId().equals(torqueReport.getUserId()))
276: || (Scope.MODULE__PK.equals(torqueReport.getScopeId()) && hasPermission(
277: ScarabSecurity.ITEM__DELETE, user));
278: }
279:
280: public boolean isSavable(ScarabUser user) {
281: return isEditable(user)
282: && hasPermission(ScarabSecurity.USER__EDIT_PREFERENCES,
283: user);
284: }
285:
286: /**
287: * Get the value of generatedBy.
288: * @return value of generatedBy.
289: */
290: public ScarabUser getGeneratedBy() throws Exception {
291: if (generatedBy == null) {
292: if (torqueReport.getUserId() != null) {
293: generatedBy = ScarabUserManager
294: .getInstance(torqueReport.getUserId());
295: }
296: }
297:
298: return generatedBy;
299: }
300:
301: /**
302: * Set the value of generatedBy.
303: * @param v Value to assign to generatedBy.
304: */
305: public void setGeneratedBy(ScarabUser v) throws Exception {
306: this .generatedBy = v;
307: torqueReport.setUserId(v.getUserId());
308: }
309:
310: /**
311: * This is the date that was used in the queries for reports on a
312: * single date. It is not necessarily the same as the date on which the
313: * queries were run.
314: * @return value of generatedDate.
315: */
316: public Date getGeneratedDate() {
317: if (generatedDate == null) {
318: // if no date was set just set this
319: // date to the current time
320: generatedDate = getDefaultDate();
321: if (generatedDate == null) {
322: generatedDate = new Date();
323: }
324: }
325:
326: return generatedDate;
327: }
328:
329: /**
330: * Date used for a single date report.
331: */
332: public Date getDefaultDate() {
333: ReportDate rdate = reportDefn.getDefaultDate();
334: return (rdate != null ? new Date(rdate.getTime()) : null);
335: }
336:
337: /**
338: * Date used for a single date report.
339: */
340: public void setDefaultDate(Date date) {
341: if (date == null) {
342: reportDefn.setDefaultDate(null);
343: } else {
344: ReportDate rdate = reportDefn.getDefaultDate();
345: if (rdate == null) {
346: rdate = new ReportDate();
347: reportDefn.setDefaultDate(rdate);
348: }
349: rdate.setTime(date.getTime());
350: Log.get().debug("Default date set to " + date);
351: }
352: }
353:
354: public MITList getMITList() throws TorqueException {
355: MITList mitList = null;
356: List mits = reportDefn.getModuleIssueTypes();
357: if (mits != null) {
358: Log.get().debug("mits were not null");
359: mitList = new MITList();
360: for (Iterator i = mits.iterator(); i.hasNext();) {
361: ModuleIssueType mit = (ModuleIssueType) i.next();
362: MITListItem item = new MITListItem();
363: item.setModuleId(mit.getModuleId());
364: item.setIssueTypeId(mit.getIssueTypeId());
365: mitList.addMITListItem(item);
366: }
367: }
368:
369: return mitList;
370: }
371:
372: public void setMITList(MITList mitList) throws Exception {
373: if (mitList == null) {
374: reportDefn.setModuleIssueTypes(null);
375: setModule(null);
376: setIssueType(null);
377: } else {
378: boolean isOk = true;
379: // need to check that the changes are compatible with the currently
380: // selected criteria
381: for (Iterator roai = reportDefn
382: .retrieveAllReportOptionAttributes().iterator(); roai
383: .hasNext()
384: && isOk;) {
385: isOk = mitList.isCommon(AttributeOptionManager
386: .getInstance(((ReportOptionAttribute) roai
387: .next()).getOptionId()));
388: }
389: for (Iterator ruai = reportDefn
390: .retrieveAllReportUserAttributes().iterator(); ruai
391: .hasNext()
392: && isOk;) {
393: isOk = mitList
394: .isCommon(AttributeManager
395: .getInstance(((ReportUserAttribute) ruai
396: .next()).getAttributeId()));
397: }
398:
399: if (!isOk) {
400: throw new IncompatibleMITListException(
401: L10NKeySet.ExceptionIncompatibleMITListChanges); //EXCEPTION
402: }
403:
404: reportDefn.setModuleIssueTypes(null);
405: setModule(null);
406: setIssueType(null);
407:
408: for (Iterator i = mitList.getExpandedMITListItems()
409: .iterator(); i.hasNext();) {
410: MITListItem item = (MITListItem) i.next();
411: ModuleIssueType mit = new ModuleIssueType();
412: mit.setModuleId(item.getModuleId());
413: mit.setIssueTypeId(item.getIssueTypeId());
414: reportDefn.addModuleIssueType(mit);
415: }
416: if (mitList.isSingleModule()) {
417: torqueReport.setModule(mitList.getModule());
418: }
419: if (mitList.isSingleIssueType()) {
420: torqueReport.setIssueType(mitList.getIssueType());
421: }
422: }
423: }
424:
425: public boolean isReadyForCalculation() {
426: List mits = reportDefn.getModuleIssueTypes();
427: boolean result = mits != null && !mits.isEmpty();
428: List axes = reportDefn.getReportAxisList();
429: result &= axes != null && axes.size() > 1;
430: if (result) {
431: for (Iterator i = axes.iterator(); i.hasNext() && result;) {
432: ReportAxis axis = (ReportAxis) i.next();
433: List headings = axis.getReportHeadings();
434: result &= headings != null && !headings.isEmpty();
435: if (result) {
436: for (Iterator j = headings.iterator(); j.hasNext()
437: && result;) {
438: ReportHeading heading = (ReportHeading) j
439: .next();
440: result &= heading.size() > 0;
441: }
442: }
443: }
444: }
445:
446: return result;
447: }
448:
449: public boolean removeStaleDefinitions() throws Exception {
450: boolean reportModified = false;
451: MITList mitList = getMITList();
452: List axes = reportDefn.getReportAxisList();
453: if (axes != null) {
454: for (Iterator i = axes.iterator(); i.hasNext();) {
455: ReportAxis axis = (ReportAxis) i.next();
456: List headings = axis.getReportHeadings();
457: if (headings != null) {
458: for (Iterator j = headings.iterator(); j.hasNext();) {
459: ReportHeading heading = (ReportHeading) j
460: .next();
461: reportModified |= removeStaleOptions(heading
462: .getReportOptionAttributes(), mitList);
463: reportModified |= removeStaleUserAttributes(
464: heading.getReportUserAttributes(),
465: mitList);
466: List groups = heading.getReportGroups();
467: if (groups != null && !groups.isEmpty()) {
468: for (Iterator n = groups.iterator(); n
469: .hasNext();) {
470: ReportGroup group = (ReportGroup) n
471: .next();
472: reportModified |= removeStaleOptions(
473: group
474: .getReportOptionAttributes(),
475: mitList);
476: reportModified |= removeStaleUserAttributes(
477: group.getReportUserAttributes(),
478: mitList);
479: }
480: }
481: }
482: }
483: }
484: }
485: return reportModified;
486: }
487:
488: private boolean removeStaleOptions(List options, MITList mitList)
489: throws Exception {
490: boolean anyRemoved = false;
491: if (options != null && !options.isEmpty()) {
492: for (Iterator n = options.iterator(); n.hasNext();) {
493: ReportOptionAttribute rao = (ReportOptionAttribute) n
494: .next();
495: AttributeOption ao = AttributeOptionManager
496: .getInstance(rao.getOptionId());
497: if (!mitList.isCommon(ao, false)) {
498: n.remove();
499: anyRemoved = true;
500: } else if (!mitList.isCommon(ao.getAttribute(), false)) {
501: n.remove();
502: anyRemoved = true;
503: }
504: }
505: }
506: return anyRemoved;
507: }
508:
509: private boolean removeStaleUserAttributes(List attributes,
510: MITList mitList) throws Exception {
511: boolean anyRemoved = false;
512: if (attributes != null && !attributes.isEmpty()) {
513: for (Iterator n = attributes.iterator(); n.hasNext();) {
514: ReportUserAttribute rua = (ReportUserAttribute) n
515: .next();
516: Attribute attr = AttributeManager.getInstance(rua
517: .getAttributeId());
518: if (!mitList.isCommon(attr, false)) {
519: n.remove();
520: anyRemoved = true;
521: }
522: }
523: }
524: return anyRemoved;
525: }
526:
527: public ReportTableModel getModel(ScarabUser searcher)
528: throws Exception {
529: return new ReportTableModel(this , getGeneratedDate(), searcher);
530: }
531:
532: public void save() throws Exception {
533: torqueReport.setQueryString(getQueryString());
534: torqueReport.save();
535: }
536:
537: /**
538: * State of persistence of the report.
539: *
540: * @return true if the report has NOT been saved.
541: */
542: public boolean isNew() {
543: return torqueReport.isNew();
544: }
545:
546: public void populate(String v) throws Exception {
547: if (v == null) {
548: reportDefn = new ReportDefinition();
549: } else {
550: BeanReader reader = new BeanReader();
551: reader.registerBeanClass(ReportDefinition.class);
552: reportDefn = (ReportDefinition) reader
553: .parse(new StringReader(v));
554:
555: Logger log = Log.get();
556: if (log.isDebugEnabled()) {
557: log.debug("Created a new report using:\n " + v
558: + "; and it resulted in:\n "
559: + reportDefn.toXmlString());
560: }
561: }
562: }
563:
564: String getQueryString() throws Exception {
565: return reportDefn.toXmlString();
566: }
567:
568: public void populateSearch(IssueSearch search, ReportHeading heading)
569: throws Exception {
570: List reportOptions = heading.getReportOptionAttributes();
571: if (reportOptions == null || reportOptions.isEmpty()) {
572: List groups = heading.getReportGroups();
573: if (groups != null && !groups.isEmpty()) {
574: reportOptions = new ArrayList();
575: for (Iterator i = groups.iterator(); i.hasNext();) {
576: ReportGroup group = (ReportGroup) i.next();
577: List tmpOptions = group.getReportOptionAttributes();
578: if (tmpOptions != null && !tmpOptions.isEmpty()) {
579: for (Iterator j = tmpOptions.iterator(); j
580: .hasNext();) {
581: reportOptions.add(j.next());
582: }
583: }
584: }
585: }
586: }
587:
588: if (reportOptions != null && !reportOptions.isEmpty()) {
589: Map commonAttributeMap = new HashMap(reportOptions.size());
590: for (Iterator i = reportOptions.iterator(); i.hasNext();) {
591: ReportOptionAttribute roa = (ReportOptionAttribute) i
592: .next();
593: Integer optionId = roa.getOptionId();
594: Integer attId = AttributeOptionManager.getInstance(
595: optionId).getAttributeId();
596: AttributeValue av = AttributeValue.getNewInstance(
597: attId, search);
598: av.setOptionId(optionId);
599: if (commonAttributeMap.containsKey(attId)) {
600: AttributeValue prevAV = (AttributeValue) commonAttributeMap
601: .get(attId);
602: prevAV.setChainedValue(av);
603: } else {
604: search.addAttributeValue(av);
605: commonAttributeMap.put(attId, av);
606: }
607: }
608: }
609: }
610: }
|