KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tigris > scarab > om > Issue


1 package org.tigris.scarab.om;
2
3 /* ================================================================
4  * Copyright (c) 2000-2002 CollabNet. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowlegement: "This product includes
19  * software developed by Collab.Net <http://www.Collab.Net/>."
20  * Alternately, this acknowlegement may appear in the software itself, if
21  * and wherever such third-party acknowlegements normally appear.
22  *
23  * 4. The hosted project names must not be used to endorse or promote
24  * products derived from this software without prior written
25  * permission. For written permission, please contact info@collab.net.
26  *
27  * 5. Products derived from this software may not use the "Tigris" or
28  * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
29  * prior written permission of Collab.Net.
30  *
31  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
32  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34  * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
35  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
39  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  *
43  * ====================================================================
44  *
45  * This software consists of voluntary contributions made by many
46  * individuals on behalf of Collab.Net.
47  */

48
49 // JDK classes
50
import java.io.Serializable JavaDoc;
51 import java.sql.Connection JavaDoc;
52 import java.util.ArrayList JavaDoc;
53 import java.util.Arrays JavaDoc;
54 import java.util.Date JavaDoc;
55 import java.util.HashMap JavaDoc;
56 import java.util.HashSet JavaDoc;
57 import java.util.Iterator JavaDoc;
58 import java.util.List JavaDoc;
59 import java.util.Locale JavaDoc;
60 import java.util.Map JavaDoc;
61 import java.util.Set JavaDoc;
62 import java.util.TreeSet JavaDoc;
63
64 import org.apache.commons.collections.MapIterator;
65 import org.apache.commons.collections.map.LinkedMap;
66 import org.apache.commons.lang.ObjectUtils;
67 import org.apache.commons.lang.StringUtils;
68 import org.apache.fulcrum.localization.Localization;
69 import org.apache.torque.TorqueException;
70 import org.apache.torque.manager.MethodResultCache;
71 import org.apache.torque.map.DatabaseMap;
72 import org.apache.torque.oid.IDBroker;
73 import org.apache.torque.om.Persistent;
74 import org.apache.torque.util.BasePeer;
75 import org.apache.torque.util.Criteria;
76 import org.apache.turbine.Turbine;
77 import org.tigris.scarab.attribute.OptionAttribute;
78 import org.tigris.scarab.attribute.TotalVotesAttribute;
79 import org.tigris.scarab.attribute.UserAttribute;
80 import org.tigris.scarab.services.cache.ScarabCache;
81 import org.tigris.scarab.services.security.ScarabSecurity;
82 import org.tigris.scarab.tools.ScarabGlobalTool;
83 import org.tigris.scarab.tools.localization.L10NKeySet;
84 import org.tigris.scarab.tools.localization.L10NMessage;
85 import org.tigris.scarab.tools.localization.Localizable;
86 import org.tigris.scarab.util.Log;
87 import org.tigris.scarab.util.MutableBoolean;
88 import org.tigris.scarab.util.ScarabConstants;
89 import org.tigris.scarab.util.ScarabException;
90 import org.tigris.scarab.workflow.WorkflowFactory;
91
92 import com.workingdogs.village.Record;
93
94 /**
95  * This class represents an Issue.
96  *
97  * @author <a HREF="mailto:jmcnally@collab.net">John McNally</a>
98  * @author <a HREF="mailto:jon@collab.net">Jon S. Stevens</a>
99  * @author <a HREF="mailto:elicia@collab.net">Elicia David</a>
100  * @version $Id: Issue.java 9724 2005-06-06 16:50:17Z jorgeuriarte $
101  */

102 public class Issue
103     extends BaseIssue
104     implements Persistent
105 {
106     // the following Strings are method names that are used in caching results
107
protected static final String JavaDoc GET_ATTRIBUTE_VALUES_MAP =
108         "getAttributeValuesMap";
109     protected static final String JavaDoc GET_ASSOCIATED_USERS =
110         "getAssociatedUsers";
111     protected static final String JavaDoc GET_MODULE_ATTRVALUES_MAP =
112         "getModuleAttributeValuesMap";
113     protected static final String JavaDoc GET_ATTRVALUE =
114         "getAttributeValue";
115     protected static final String JavaDoc GET_ATTRVALUES =
116         "getAttributeValues";
117     protected static final String JavaDoc GET_ALL_USERS_TO_EMAIL =
118         "getAllUsersToEmail";
119     protected static final String JavaDoc GET_USER_ATTRIBUTEVALUE =
120         "getUserAttributeValue";
121     protected static final String JavaDoc GET_USER_ATTRIBUTEVALUES =
122         "getUserAttributeValues";
123     protected static final String JavaDoc GET_CREATED_DATE =
124         "getCreatedDate";
125     protected static final String JavaDoc GET_CREATED_BY =
126         "getCreatedBy";
127     protected static final String JavaDoc GET_LAST_TRANSACTION =
128         "getLastActivitySet";
129     protected static final String JavaDoc GET_MODIFIED_BY =
130         "getModifiedBy";
131     protected static final String JavaDoc GET_MODIFIED_DATE =
132         "getModifiedDate";
133     protected static final String JavaDoc GET_COMMENTS =
134         "getComments";
135     protected static final String JavaDoc GET_URLS =
136         "getUrls";
137     protected static final String JavaDoc GET_EXISTING_ATTACHMENTS =
138         "getExistingAttachments";
139     protected static final String JavaDoc GET_ACTIVITY =
140         "getActivity";
141     protected static final String JavaDoc GET_TRANSACTIONS =
142         "getActivitySets";
143     protected static final String JavaDoc GET_CHILDREN =
144         "getChildren";
145     protected static final String JavaDoc GET_PARENTS =
146         "getParents";
147     protected static final String JavaDoc GET_ALL_DEPENDENCY_TYPES =
148         "getAllDependencyTypes";
149     protected static final String JavaDoc GET_DEPENDENCY =
150         "getDependency";
151     protected static final String JavaDoc GET_TEMPLATE_TYPES =
152         "getTemplateTypes";
153     protected static final String JavaDoc GET_TEMPLATEINFO =
154         "getTemplateInfo";
155     protected static final String JavaDoc GET_CLOSED_DATE =
156         "getClosedDate";
157     protected static final String JavaDoc GET_ORPHAN_ATTRIBUTEVALUES_LIST =
158         "getNonMatchingAttributeValuesList";
159     protected static final String JavaDoc GET_DEFAULT_TEXT_ATTRIBUTEVALUE =
160         "getDefaultTextAttributeValue";
161     protected static final String JavaDoc GET_DEFAULT_TEXT =
162         "getDefaultText";
163     protected static final String JavaDoc GET_NULL_END_DATE =
164         "getActivitiesWithNullEndDate";
165     protected static final String JavaDoc GET_INITIAL_ACTIVITYSET =
166         "getInitialActivitySet";
167     protected static final String JavaDoc GET_HISTORY_LIMIT =
168         "getHistoryLimit";
169
170     private static final Integer JavaDoc NUMBERKEY_0 = new Integer JavaDoc(0);
171     private static final Integer JavaDoc COPIED = new Integer JavaDoc(1);
172     private static final Integer JavaDoc MOVED = new Integer JavaDoc(2);
173
174     /** storage for any attachments which have not been saved yet */
175     private List JavaDoc unSavedAttachments = null;
176     
177     /**
178      * new issues are created only when the issuetype and module are known
179      * Or by the Peer when retrieving from db
180      */

181     protected Issue()
182     {
183     }
184
185     protected Issue(Module module, IssueType issueType)
186         throws Exception JavaDoc
187     {
188         this();
189         setModule(module);
190         setIssueType(issueType);
191     }
192
193     /**
194      * Gets an issue associated to a Module
195      */

196     public static Issue getNewInstance(Module module,
197                                        IssueType issueType)
198         throws Exception JavaDoc
199     {
200         Issue issue = new Issue(module, issueType);
201         return issue;
202     }
203
204
205     /**
206      * @deprecated use IssueManager.getIssueById
207      */

208     public static Issue getIssueById(String JavaDoc id)
209     {
210         return IssueManager.getIssueById(id);
211     }
212
213     /**
214      * @deprecated use IssueManager.getIssueById
215      */

216     public static Issue getIssueById(Issue.FederatedId fid)
217     {
218         return IssueManager.getIssueByIdImpl(fid);
219     }
220
221
222
223     /**
224      * Gets the UniqueId for this Issue.
225      */

226     public String JavaDoc getUniqueId()
227         throws TorqueException
228     {
229         if (getIdPrefix() == null)
230         {
231             setIdPrefix(getModule().getCode());
232         }
233         return getIdPrefix() + getIdCount();
234     }
235
236     /**
237      * NoOp for intake's benefit
238      */

239     public void setUniqueId(String JavaDoc id)
240     {
241     }
242
243     public String JavaDoc getFederatedId()
244         throws TorqueException
245     {
246         if (getIdDomain() != null)
247         {
248             return getIdDomain() + '-' + getUniqueId();
249         }
250         return getUniqueId();
251     }
252
253     public void setFederatedId(String JavaDoc id)
254     {
255         FederatedId fid = new FederatedId(id);
256         setIdDomain(fid.getDomain());
257         setIdPrefix(fid.getPrefix());
258         setIdCount(fid.getCount());
259     }
260
261     /**
262      * A FederatedId has this format: {Domain}-{Code}{Id}
263      * For example: collab.net-PACS1
264      * The domain can also be null.
265      */

266     public static class FederatedId
267         implements Serializable JavaDoc
268     {
269         private String JavaDoc domainId;
270         private String JavaDoc prefix;
271         private int count;
272
273         public FederatedId(String JavaDoc id)
274         {
275             int dash = id.indexOf('-');
276             if (dash > 0)
277             {
278                 domainId = id.substring(0, dash);
279                 setUniqueId(id.substring(dash+1));
280             }
281             else
282             {
283                 setUniqueId(id);
284             }
285         }
286
287         public FederatedId(String JavaDoc domain, String JavaDoc prefix, int count)
288         {
289             this.domainId = domain;
290             setPrefix(prefix);
291             this.count = count;
292         }
293
294         /**
295          * @param id The unique identifier for this issue, generally a
296          * combination of code and sequence number (e.g. SCB37).
297          */

298         public void setUniqueId(String JavaDoc id)
299         {
300             int codeLength = ScarabGlobalTool.getModuleCodeLength();
301             // we could start at 1 here, if the spec says one char is
302
// required, will keep it safe for now.
303
StringBuffer JavaDoc code = new StringBuffer JavaDoc(codeLength);
304             int max = id.length() < codeLength ? id.length() : codeLength;
305             for (int i = 0; i < max; i++)
306             {
307                 char c = id.charAt(i);
308                 if (c < '0' || c > '9')
309                 {
310                     code.append(c);
311                 }
312             }
313             if (code.length() != 0)
314             {
315                 setPrefix(code.toString());
316             }
317             count = Integer.parseInt(id.substring(code.length()));
318         }
319         
320         /**
321          * @return The domain.
322          */

323         public String JavaDoc getDomain()
324         {
325             return domainId;
326         }
327
328         /**
329          * @return The prefix (upper-cased).
330          */

331         public String JavaDoc getPrefix()
332         {
333             return prefix;
334         }
335                 
336         /**
337          * @return The sequence of this issue within its code.
338          */

339         public int getCount()
340         {
341             return count;
342         }
343         
344         /**
345          * Set the domainId
346          */

347         public void setDomain(String JavaDoc domainId)
348         {
349             this.domainId = domainId;
350         }
351
352         /**
353          * @param prefix The module code.
354          */

355         public void setPrefix(String JavaDoc prefix)
356         {
357             if (prefix != null)
358             {
359                 this.prefix = prefix.toUpperCase();
360             }
361         }
362         
363         /**
364          * @param count The sequence of this issue within its code.
365          */

366         public void setCount(int count)
367         {
368             this.count = count;
369         }
370
371         public boolean equals(Object JavaDoc obj)
372         {
373             boolean b = false;
374             if (obj instanceof FederatedId)
375             {
376                 FederatedId fid = (FederatedId)obj;
377                 b = fid.count == this.count;
378                 b &= ObjectUtils.equals(fid.domainId, domainId);
379                 b &= ObjectUtils.equals(fid.prefix, prefix);
380             }
381             return b;
382         }
383
384         public int hashCode()
385         {
386             int hc = count;
387             if (domainId != null)
388             {
389                 hc += domainId.hashCode();
390             }
391             if (prefix != null)
392             {
393                 hc += prefix.hashCode();
394             }
395             return hc;
396         }
397     }
398
399     /**
400      * @param module The current module.
401      * @param theList A textual representation of the list of issues
402      * to parse.
403      * @param The parsed list of issue identifiers.
404      */

405     public static List JavaDoc parseIssueList(Module module, String JavaDoc theList)
406         throws Exception JavaDoc
407     {
408         String JavaDoc[] issues = StringUtils.split(theList, ",");
409         List JavaDoc results = new ArrayList JavaDoc();
410         for (int i = 0; i < issues.length; i++)
411         {
412             if (issues[i].indexOf('*') != -1)
413             {
414                 // Probably better to use more Torque here, but this
415
// is definitely going to be faster and more
416
// efficient.
417
String JavaDoc sql = "SELECT CONCAT(" + IssuePeer.ID_PREFIX + ',' +
418                     IssuePeer.ID_COUNT + ") FROM " + IssuePeer.TABLE_NAME +
419                     " WHERE " + IssuePeer.ID_PREFIX + " = '" +
420                     module.getCode() + '\'';
421                 List JavaDoc records = BasePeer.executeQuery(sql);
422                 for (Iterator JavaDoc j = records.iterator(); j.hasNext();)
423                 {
424                     Record rec = (Record)j.next();
425                     results.add(rec.getValue(1).asString());
426                 }
427             }
428             // check for a -
429
else if (issues[i].indexOf('-') == -1)
430             {
431                 // Make sure user is not trying to access issues from another
432
// module.
433
FederatedId fid = createFederatedId(module, issues[i]);
434                 if (!fid.getPrefix().equalsIgnoreCase(module.getCode()))
435                 {
436                     String JavaDoc[] args = { fid.getPrefix(), module.getCode() };
437                     throw new Exception JavaDoc(Localization.format
438                                         (ScarabConstants.DEFAULT_BUNDLE_NAME,
439                                          module.getLocale(),
440                                          "IssueIDPrefixNotForModule", args)); //EXCEPTION
441
}
442                 results.add(issues[i]);
443             }
444             else
445             {
446                 String JavaDoc[] issue = StringUtils.split(issues[i], "-");
447                 if (issue.length != 2)
448                 {
449                     throw new Exception JavaDoc(Localization.format
450                                         (ScarabConstants.DEFAULT_BUNDLE_NAME,
451                                          module.getLocale(),
452                                          "IssueIDRangeNotValid", issues[i])); //EXCEPTION
453
}
454                 FederatedId fidStart = createFederatedId(module, issue[0]);
455                 FederatedId fidStop = createFederatedId(module, issue[1]);
456                 if (!fidStart.getPrefix().equalsIgnoreCase(module.getCode()) ||
457                     !fidStop.getPrefix().equalsIgnoreCase(module.getCode()))
458                 {
459                     throw new Exception JavaDoc(Localization.format
460                                         (ScarabConstants.DEFAULT_BUNDLE_NAME,
461                                          module.getLocale(),
462                                          "IssueIDPrefixesNotForModule",
463                                          module.getCode())); //EXCEPTION
464
}
465                 else if (!fidStart.getPrefix()
466                          .equalsIgnoreCase(fidStop.getPrefix()))
467                 {
468                     String JavaDoc[] args = { fidStart.getPrefix(),
469                                       fidStop.getPrefix() };
470                     throw new Exception JavaDoc(Localization.format
471                                         (ScarabConstants.DEFAULT_BUNDLE_NAME,
472                                          module.getLocale(),
473                                          "IssueIDPrefixesDoNotMatch", args)); //EXCEPTION
474
}
475                 else if (fidStart.getCount() > fidStop.getCount())
476                 {
477                     FederatedId swap = fidStart;
478                     fidStart = fidStop;
479                     fidStop = swap;
480                 }
481
482                 for (int j = fidStart.getCount(); j <= fidStop.getCount();j++)
483                 {
484                     results.add(fidStart.getPrefix() + j);
485                 }
486             }
487         }
488         return results;
489     }
490
491     /**
492      * Catches and rethrows parsing errors when creating the federated id.
493      */

494     private static FederatedId createFederatedId(Module module, String JavaDoc id)
495         throws Exception JavaDoc
496     {
497         FederatedId fid = null;
498         try
499         {
500             fid = new FederatedId(id.trim());
501             if (fid.getPrefix() == null || fid.getPrefix().length() == 0)
502             {
503                 fid.setPrefix(module.getCode());
504             }
505         }
506         catch (Exception JavaDoc e)
507         {
508             throw new Exception JavaDoc("Invalid federated id: " + id); //EXCEPTION
509
}
510         return fid;
511     }
512
513     /**
514      * Whether this issue is an enter issue template.
515      */

516     public boolean isTemplate()
517     {
518         boolean isTemplate = false;
519         try
520         {
521             isTemplate = !getIssueType().getParentId().equals(NUMBERKEY_0);
522         }
523         catch (Exception JavaDoc e)
524         {
525             getLog().error("Problem determining whether issue is template");
526         }
527         return isTemplate;
528     }
529
530     /**
531      * Adds a url to an issue and passes null as the activity set
532      * to create a new one.
533      */

534     public ActivitySet addUrl(Attachment attachment, ScarabUser user)
535         throws Exception JavaDoc
536     {
537         return addUrl(null, attachment, user);
538     }
539
540     /**
541      * Adds a url to an issue.
542      */

543     public ActivitySet addUrl(ActivitySet activitySet,
544                            Attachment attachment, ScarabUser user)
545         throws Exception JavaDoc
546     {
547         attachment.setTextFields(user, this, Attachment.URL__PK);
548         attachment.save();
549
550         String JavaDoc nameFieldString = attachment.getName();
551         // Generate description of modification
552
int length = nameFieldString.length() + 12;
553         // strip off the end
554
if (length > 254)
555         {
556             nameFieldString = nameFieldString.substring(0, 238) + "...";
557         }
558         String JavaDoc desc = Localization.format(
559             ScarabConstants.DEFAULT_BUNDLE_NAME,
560             getLocale(),
561             "UrlAddedDesc", nameFieldString);
562
563         // Save activitySet record
564
if (activitySet == null)
565         {
566             activitySet = getActivitySet(user, ActivitySetTypePeer.EDIT_ISSUE__PK);
567             activitySet.save();
568         }
569         // Save activity record
570
ActivityManager
571             .createTextActivity(this, activitySet, desc, attachment);
572         
573         return activitySet;
574     }
575
576     private Locale JavaDoc getLocale()
577         throws TorqueException
578     {
579         return getModule().getLocale();
580     }
581
582     /**
583      * Adds a comment to an issue and passes null as the activity set
584      * to create a new one.
585      */

586     public ActivitySet addComment(Attachment attachment, ScarabUser user)
587         throws Exception JavaDoc
588     {
589         return addComment(null, attachment, user);
590     }
591
592     /**
593      * Adds a comment to an issue.
594      */

595     public ActivitySet addComment(ActivitySet activitySet,
596                                   Attachment attachment, ScarabUser user)
597         throws Exception JavaDoc
598     {
599         String JavaDoc comment = attachment.getData();
600         if (comment == null || comment.length() == 0)
601         {
602             throw new ScarabException(L10NKeySet.NoDataInComment);
603         }
604         if (activitySet == null)
605         {
606             activitySet = getActivitySet(user,
607                             ActivitySetTypePeer.EDIT_ISSUE__PK);
608         }
609         activitySet.save();
610
611         // create the localized string...
612
String JavaDoc desc = Localization.getString(
613             ScarabConstants.DEFAULT_BUNDLE_NAME,
614             getLocale(),
615             "AddedCommentToIssue");
616         int total = 248 - desc.length();
617         if (comment.length() > total)
618         {
619             comment = comment.substring(0,total) + "...";
620         }
621         comment = desc + " '" + comment + "'";
622
623         // populates the attachment with data to be a comment
624
attachment = AttachmentManager
625                         .getComment(attachment, this, user);
626
627         ActivityManager
628             .createTextActivity(this, activitySet,
629                                 comment, attachment);
630
631         try
632         {
633             activitySet.sendEmail(this);
634         }
635         catch (Exception JavaDoc e)
636         { Localizable commentSaved = new L10NMessage(L10NKeySet.CommentSaved);
637             Localizable sendFailed = new L10NMessage(L10NKeySet.CouldNotSendEmail,e);
638             throw new ScarabException(L10NKeySet.SavedButErrors,
639                     commentSaved,
640                     sendFailed);
641         }
642
643         return activitySet;
644     }
645
646     /**
647      * Adds an attachment file to this issue. Does not perform
648      * a save because the issue may not have been created yet.
649      * use the doSaveFileAttachment() to save the attachment
650      * after the issue has been created.
651      */

652     public synchronized void addFile(Attachment attachment,
653                                      ScarabUser user)
654         throws Exception JavaDoc
655     {
656         attachment.setTypeId(Attachment.FILE__PK);
657         attachment.setCreatedBy(user.getUserId());
658         if (unSavedAttachments == null)
659         {
660             unSavedAttachments = new ArrayList JavaDoc();
661         }
662         unSavedAttachments.add(attachment);
663     }
664
665     /**
666      * Overrides the super method in order to allow
667      * us to return the unSavedAttachments if they exist.
668      */

669     public synchronized List JavaDoc getAttachments()
670         throws TorqueException
671     {
672         if (unSavedAttachments != null &&
673             unSavedAttachments.size() > 0)
674         {
675             return unSavedAttachments;
676         }
677         else
678         {
679             return super.getAttachments();
680         }
681     }
682
683     /**
684      * Adds an attachment file to this issue. Does not perform
685      * a save because the issue may not have been created yet.
686      * use the doSaveFileAttachment() to save the attachment
687      * after the issue has been created.
688      */

689     public synchronized ActivitySet doSaveFileAttachments(ScarabUser user)
690         throws Exception JavaDoc
691     {
692         return doSaveFileAttachments(null, user);
693     }
694     
695     /**
696      * Adds an attachment file to this issue. Does not perform
697      * a save because the issue may not have been created yet.
698      * use the doSaveFileAttachment() to save the attachment
699      * after the issue has been created.
700      */

701     public synchronized ActivitySet doSaveFileAttachments(ActivitySet activitySet,
702                                                           ScarabUser user)
703         throws Exception JavaDoc
704     {
705         if (unSavedAttachments == null)
706         {
707             return activitySet;
708         }
709         if (activitySet == null)
710         {
711             // Save activitySet record
712
activitySet = getActivitySet(user, ActivitySetTypePeer.EDIT_ISSUE__PK);
713             activitySet.save();
714         }
715         Iterator JavaDoc itr = unSavedAttachments.iterator();
716         while (itr.hasNext())
717         {
718             Attachment attachment = (Attachment)itr.next();
719             // make sure we set the issue to the newly created issue
720
attachment.setIssue(this);
721             attachment.save();
722
723             // Generate description of modification
724
String JavaDoc name = attachment.getFileName();
725             Object JavaDoc[] args = {name};
726             String JavaDoc description = Localization.format(
727                 ScarabConstants.DEFAULT_BUNDLE_NAME,
728                 getLocale(),
729                 "FileAddedDesc", args);
730
731             // Save activity record
732
ActivityManager
733                 .createTextActivity(this, activitySet, description, attachment);
734
735            
736         }
737         // reset the super method so that the query has to hit the database again
738
// so that all of the information is cleaned up and reset.
739
super.collAttachments = null;
740         // we don't need this one anymore either.
741
this.unSavedAttachments = null;
742         return activitySet;
743     }
744
745     /**
746      * Remove an attachment file
747      * @param index starts with 1 because velocityCount start from 1
748      * but ArrayList starts from 0
749      */

750     public void removeFile(String JavaDoc index)
751         throws Exception JavaDoc
752     {
753         int indexInt = Integer.parseInt(index) - 1;
754         if (indexInt >= 0)
755         {
756             if (unSavedAttachments != null && unSavedAttachments.size() > 0)
757             {
758                 unSavedAttachments.remove(indexInt);
759             }
760             else
761             {
762                 List JavaDoc attachList = getAttachments();
763                 if (attachList != null && attachList.size() > 0)
764                 {
765                     attachList.remove(indexInt);
766                 }
767             }
768         }
769     }
770
771     /**
772      * Throws UnsupportedOperationException. Use
773      * <code>getModule()</code> instead.
774      *
775      * @return a <code>ScarabModule</code> value
776      */

777     public ScarabModule getScarabModule()
778     {
779         throw new UnsupportedOperationException JavaDoc(
780             "Should use getModule"); //EXCEPTION
781
}
782
783     /**
784      * Throws UnsupportedOperationException. Use
785      * <code>setModule(Module)</code> instead.
786      *
787      */

788     public void setScarabModule(ScarabModule module)
789     {
790         throw new UnsupportedOperationException JavaDoc(
791             "Should use setModule(Module). Note module cannot be new."); //EXCEPTION
792
}
793
794     /**
795      * Use this instead of setScarabModule. Note: module cannot be new.
796      */

797     public void setModule(Module me)
798         throws TorqueException
799     {
800         Integer JavaDoc id = me.getModuleId();
801         if (id == null)
802         {
803             throw new TorqueException("Modules must be saved prior to " +
804                                       "being associated with other objects."); //EXCEPTION
805
}
806         setModuleId(id);
807     }
808
809     /**
810      * Module getter. Use this method instead of getScarabModule().
811      *
812      * @return a <code>Module</code> value
813      */

814     public Module getModule()
815         throws TorqueException
816     {
817         Module module = null;
818         Integer JavaDoc id = getModuleId();
819         if ( id != null )
820         {
821             module = ModuleManager.getInstance(id);
822         }
823         
824         return module;
825     }
826
827     /**
828      * The RModuleIssueType related to this issue's module and issue type.
829      *
830      * @return a <code>RModuleIssueType</code> if this issue's module and
831      * issue type are not null, otherwise return null.
832      */

833     public RModuleIssueType getRModuleIssueType()
834         throws Exception JavaDoc
835     {
836         RModuleIssueType rmit = null;
837         Module module = getModule();
838         IssueType issueType = getIssueType();
839         if (module != null && issueType != null)
840         {
841             rmit = module.getRModuleIssueType(issueType);
842         }
843         return rmit;
844     }
845
846     /**
847      * Calls the overloaded version by passing 'true' so that only active
848      * attributes will be considered.
849      * @see #getModuleAttributeValuesMap(boolean)
850      */

851     public LinkedMap getModuleAttributeValuesMap()
852         throws Exception JavaDoc
853     {
854         return getModuleAttributeValuesMap(true);
855     }
856
857     /**
858      * AttributeValues that are relevant to the issue's current module.
859      * Empty AttributeValues that are relevant for the module, but have
860      * not been set for the issue are included. The values are ordered
861      * according to the module's preference
862      *
863      * @param isActive TRUE if only active attributes need to be considered
864      * and FALSE if both active and inactive attributes need to be considered
865      */

866     public LinkedMap getModuleAttributeValuesMap(boolean isActive)
867         throws Exception JavaDoc
868     {
869         LinkedMap result = null;
870         Object JavaDoc obj = getCachedObject(GET_MODULE_ATTRVALUES_MAP, isActive ? Boolean.TRUE : Boolean.FALSE);
871         if (obj == null)
872         {
873             List JavaDoc attributes = null;
874             if (isActive)
875             {
876                 attributes = getIssueType().getActiveAttributes(getModule());
877             }
878             else
879             {
880                 attributes = getModule().getAttributes(getIssueType());
881             }
882             Map JavaDoc siaValuesMap = getAttributeValuesMap();
883             result = new LinkedMap((int)(1.25*attributes.size() + 1));
884             for (int i=0; i<attributes.size(); i++)
885             {
886                 String JavaDoc key = ((Attribute)attributes.get(i)).getName().toUpperCase();
887                 if (siaValuesMap.containsKey(key))
888                 {
889                     result.put(key, siaValuesMap.get(key));
890                 }
891                 else
892                 {
893                     AttributeValue aval = AttributeValue
894                         .getNewInstance(((Attribute)attributes.get(i)), this);
895                     addAttributeValue(aval);
896                     siaValuesMap.put(
897                         aval.getAttribute().getName().toUpperCase(), aval);
898                     result.put(key, aval);
899                 }
900             }
901             putCachedObject(result, GET_MODULE_ATTRVALUES_MAP, isActive ? Boolean.TRUE : Boolean.FALSE);
902         }
903         else
904         {
905             result = (LinkedMap)obj;
906         }
907         return result;
908     }
909
910     public void addAttributeValue(AttributeValue aval)
911        throws TorqueException
912     {
913         List JavaDoc avals = getAttributeValues();
914         if (!avals.contains(aval))
915         {
916             super.addAttributeValue(aval);
917         }
918     }
919
920     /**
921      * This method is currently experimental.
922      * It allows to retrieve the current status
923      * of the issue regarding the options "new", "started","closed"
924      * and so on. If for any reason the status
925      * @return
926      * @throws Exception
927      */

928     public String JavaDoc getEmailShortInfo()
929     throws Exception JavaDoc
930     {
931         String JavaDoc result = "";
932
933         String JavaDoc key = "status_attribute_"+this.getTypeId();
934
935         String JavaDoc statusId = GlobalParameterManager.getString(key,this.getModule());
936         if(!statusId.equals(""))
937         {
938             int sid = Integer.parseInt(statusId);
939             AttributeValue av = getAttributeValue(sid);
940             if(av != null)
941             {
942                 result=av.getValue();
943             }
944         }
945         return result;
946     }
947     
948     public AttributeValue getAttributeValue(String JavaDoc attributeName)
949         throws Exception JavaDoc
950     {
951         Attribute attribute = Attribute.getInstance(attributeName);
952         return getAttributeValue(attribute);
953     }
954
955     public AttributeValue getAttributeValue(int id)
956         throws Exception JavaDoc
957     {
958         Attribute attribute = Attribute.getInstance(id);
959         return getAttributeValue(attribute);
960     }
961
962     public AttributeValue getAttributeValue(Attribute attribute)
963        throws Exception JavaDoc
964     {
965         AttributeValue result = null;
966         Object JavaDoc obj = ScarabCache.get(this, GET_ATTRVALUE, attribute);
967         if (obj == null)
968         {
969             if (isNew())
970             {
971                 List JavaDoc avals = getAttributeValues();
972                 if (avals != null)
973                 {
974                     Iterator JavaDoc i = avals.iterator();
975                     while (i.hasNext())
976                     {
977                         AttributeValue tempAval = (AttributeValue)i.next();
978                         if (tempAval.getAttribute().equals(attribute))
979                         {
980                             result = tempAval;
981                             break;
982                         }
983                     }
984                 }
985             }
986             else
987             {
988                 Criteria crit = new Criteria(2)
989                     .add(AttributeValuePeer.ISSUE_ID, getIssueId())
990                     .add(AttributeValuePeer.DELETED, false)
991                     .add(AttributeValuePeer.ATTRIBUTE_ID,
992                          attribute.getAttributeId());
993                 
994                 List JavaDoc avals = getAttributeValues(crit);
995                 if (avals.size() > 0)
996                 {
997                     result = (AttributeValue)avals.get(0);
998                 }
999                 if (avals.size() > 1)
1000                {
1001                    getLog().error("getAttributeValue(): Error when retrieving attribute values of attribute. Expected 1 and found " + avals.size() + ". List follows: " + avals);
1002                }
1003            }
1004            ScarabCache.put(result, this, GET_ATTRVALUE, attribute);
1005        }
1006        else
1007        {
1008            result = (AttributeValue)obj;
1009        }
1010        return result;
1011    }
1012
1013    /**
1014     * Returns the (undeleted) AttributeValues for the Attribute.
1015     */

1016    public List JavaDoc getAttributeValues(Attribute attribute)
1017       throws Exception JavaDoc
1018    {
1019        List JavaDoc result = null;
1020        Object JavaDoc obj = ScarabCache.get(this, GET_ATTRVALUES, attribute);
1021        if (obj == null)
1022        {
1023            if (isNew())
1024            {
1025                List JavaDoc avals = getAttributeValues();
1026                result = new ArrayList JavaDoc();
1027                if (avals != null)
1028                {
1029                    Iterator JavaDoc i = avals.iterator();
1030                    while (i.hasNext())
1031                    {
1032                        AttributeValue tempAval = (AttributeValue)i.next();
1033                        if (tempAval.getAttribute().equals(attribute))
1034                        {
1035                            result.add(tempAval);
1036                        }
1037                    }
1038                }
1039            }
1040            else
1041            {
1042                Criteria crit = new Criteria(2)
1043                    .add(AttributeValuePeer.DELETED, false)
1044                    .add(AttributeValuePeer.ATTRIBUTE_ID,
1045                         attribute.getAttributeId());
1046                
1047                result = getAttributeValues(crit);
1048                ScarabCache.put(result, this, GET_ATTRVALUES, attribute);
1049            }
1050        }
1051        else
1052        {
1053            result = (List JavaDoc)obj;
1054        }
1055        return result;
1056    }
1057
1058    public boolean isAttributeValue(AttributeValue attVal)
1059       throws Exception JavaDoc
1060    {
1061        boolean isValue = false;
1062        List JavaDoc attValues = getAttributeValues(attVal.getAttribute());
1063        if (attValues.contains(attVal))
1064        {
1065            isValue = true;
1066        }
1067        return isValue;
1068    }
1069    
1070    /**
1071     * Returns the attributevalue of the attribute with the value passed
1072     * (as String or Number). This is needed, because some attributes might
1073     * have MULTIPLE VALUES for the same issue (user attributes at least)
1074     * @param att Attribute for with to get the attributevalue
1075     * @param strVal String value to test
1076     * @param numVal Integer value to test
1077     * @return the attributevalue or null if not found
1078     * @throws Exception
1079     */

1080    private AttributeValue getAttributeValueWithValue(Attribute att, String JavaDoc strVal, Integer JavaDoc numVal)
1081        throws Exception JavaDoc
1082    {
1083        AttributeValue val = null;
1084        boolean bFound = false;
1085        List JavaDoc attValues = getAttributeValues(att);
1086        for (Iterator JavaDoc it = attValues.iterator(); !bFound && it.hasNext(); )
1087        {
1088            val = (AttributeValue)it.next();
1089            if (strVal != null)
1090                bFound = val.getValue().equals(strVal);
1091            else if (!bFound && numVal != null)
1092                bFound = val.getNumericValue().equals(numVal);
1093        }
1094        return val;
1095    }
1096
1097
1098    /**
1099     * AttributeValues that are set for this Issue
1100     */

1101    public Map JavaDoc getAttributeValuesMap() throws Exception JavaDoc
1102    {
1103        Map JavaDoc result = null;
1104        Object JavaDoc obj = ScarabCache.get(this, GET_ATTRIBUTE_VALUES_MAP);
1105        if (obj == null)
1106        {
1107            Criteria crit = new Criteria(2)
1108                .add(AttributeValuePeer.DELETED, false);
1109            List JavaDoc siaValues = getAttributeValues(crit);
1110            result = new HashMap JavaDoc((int)(1.25*siaValues.size() + 1));
1111            for (Iterator JavaDoc i = siaValues.iterator(); i.hasNext(); )
1112            {
1113                AttributeValue att = (AttributeValue) i.next();
1114                result.put(att.getAttribute().getName().toUpperCase(), att);
1115            }
1116
1117            ScarabCache.put(result, this, GET_ATTRIBUTE_VALUES_MAP);
1118        }
1119        else
1120        {
1121            result = (Map JavaDoc)obj;
1122        }
1123        return result;
1124    }
1125
1126    /**
1127     * AttributeValues that are set for this issue and
1128     * Empty AttributeValues that are relevant for the module, but have
1129     * not been set for the issue are included.
1130     */

1131    public Map JavaDoc getAllAttributeValuesMap()
1132        throws Exception JavaDoc
1133    {
1134        Map JavaDoc moduleAtts = getModuleAttributeValuesMap();
1135        Map JavaDoc issueAtts = getAttributeValuesMap();
1136        Map JavaDoc allValuesMap = new HashMap JavaDoc((int)(1.25*(moduleAtts.size() +
1137                                            issueAtts.size())+1));
1138
1139        allValuesMap.putAll(moduleAtts);
1140        allValuesMap.putAll(issueAtts);
1141        return allValuesMap;
1142    }
1143
1144    /**
1145     * Describe <code>containsMinimumAttributeValues</code> method here.
1146     *
1147     * @return a <code>boolean</code> value
1148     * @exception Exception if an error occurs
1149     */

1150    public boolean containsMinimumAttributeValues()
1151        throws Exception JavaDoc
1152    {
1153        List JavaDoc attributes = getIssueType()
1154            .getRequiredAttributes(getModule());
1155
1156        boolean result = true;
1157        LinkedMap avMap = getModuleAttributeValuesMap();
1158        MapIterator i = avMap.mapIterator();
1159        while (i.hasNext())
1160        {
1161            AttributeValue aval = (AttributeValue)avMap.get(i.next());
1162            
1163            if (aval.getOptionId() == null && aval.getValue() == null)
1164            {
1165                for (int j=attributes.size()-1; j>=0; j--)
1166                {
1167                    if (aval.getAttribute().getPrimaryKey().equals(
1168                         ((Attribute)attributes.get(j)).getPrimaryKey()))
1169                    {
1170                        result = false;
1171                        break;
1172                    }
1173                }
1174                if (!result)
1175                {
1176                    break;
1177                }
1178            }
1179        }
1180        return result;
1181    }
1182
1183    /**
1184     * Users who are valid values to the attribute this issue.
1185     * if a user has already
1186     * been assigned to this issue, they will not show up in this list.
1187     * use module.getEligibleUsers(Attribute) to get a complete list.
1188     *
1189     * @return a <code>List</code> value
1190     */

1191    public List JavaDoc getEligibleUsers(Attribute attribute)
1192        throws Exception JavaDoc
1193    {
1194        ScarabUser[] users = getModule().getEligibleUsers(attribute);
1195        // remove those already assigned
1196
List JavaDoc assigneeAVs = getAttributeValues(attribute);
1197        if (users != null && assigneeAVs != null)
1198        {
1199            for (int i=users.length-1; i>=0; i--)
1200            {
1201                for (int j=assigneeAVs.size()-1; j>=0; j--)
1202                {
1203                    AttributeValue av = (AttributeValue)assigneeAVs.get(j);
1204                    Integer JavaDoc avUserId = av.getUserId();
1205                    Integer JavaDoc userUserId = users[i].getUserId();
1206                    if ( av != null && avUserId != null &&
1207                         userUserId != null &&
1208                         avUserId.equals(userUserId))
1209                    {
1210                        users[i] = null;
1211                        break;
1212                    }
1213                }
1214            }
1215        }
1216
1217        List JavaDoc eligibleUsers = new ArrayList JavaDoc(users.length);
1218        for (int i=0; i<users.length; i++)
1219        {
1220            if (users[i] != null)
1221            {
1222                eligibleUsers.add(users[i]);
1223            }
1224        }
1225
1226        return eligibleUsers;
1227    }
1228
1229    /**
1230     * Returns the users which should be notified when this issue is
1231     * modified. The set contains those users associated with user
1232     * attributes for this issue, plus the creator of the issue.
1233     *
1234     * @param action
1235     * @param issue Usually a reference to this or a dependent issue.
1236     * @param users The list of users to append to, or
1237     * <code>null</code> to create a new list.
1238     */

1239    protected Set JavaDoc getUsersToEmail(String JavaDoc action, Issue issue, Set JavaDoc users)
1240        throws Exception JavaDoc
1241    {
1242        if (users == null)
1243        {
1244            users = new HashSet JavaDoc(1);
1245        }
1246
1247        Module module = getModule();
1248
1249        ScarabUser createdBy = issue.getCreatedBy();
1250        if (createdBy != null && !users.contains(createdBy) &&
1251            AttributePeer.EMAIL_TO.equals(action) &&
1252               createdBy.hasPermission(ScarabSecurity.ISSUE__ENTER, module))
1253        {
1254            users.add(createdBy);
1255        }
1256
1257        Criteria crit = new Criteria()
1258            .add(AttributeValuePeer.ISSUE_ID, issue.getIssueId())
1259            .addJoin(AttributeValuePeer.ATTRIBUTE_ID,
1260                     AttributePeer.ATTRIBUTE_ID)
1261            .add(AttributePeer.ACTION, action)
1262            .add(RModuleAttributePeer.MODULE_ID, getModuleId())
1263            .add(RModuleAttributePeer.ISSUE_TYPE_ID, getTypeId())
1264            .add(AttributeValuePeer.DELETED, 0)
1265            .add(RModuleAttributePeer.ACTIVE, true)
1266            .addJoin(RModuleAttributePeer.ATTRIBUTE_ID,
1267                     AttributeValuePeer.ATTRIBUTE_ID);
1268        List JavaDoc userAttVals = AttributeValuePeer.doSelect(crit);
1269        for (Iterator JavaDoc i = userAttVals.iterator(); i.hasNext(); )
1270        {
1271            AttributeValue attVal = (AttributeValue) i.next();
1272            try
1273            {
1274                ScarabUser su = ScarabUserManager
1275                    .getInstance(attVal.getUserId());
1276                if (!users.contains(su)
1277                    && su.hasPermission(attVal.getAttribute().getPermission(),
1278                                        module))
1279                {
1280                    users.add(su);
1281                }
1282            }
1283            catch (Exception JavaDoc e)
1284            {
1285                throw new Exception JavaDoc("Error retrieving users to email"); //EXCEPTION
1286
}
1287        }
1288        return users;
1289    }
1290
1291    /**
1292     * Returns users assigned to user attributes that get emailed
1293     * When issue is modified. Plus creating user.
1294     * Adds users to email for dependant issues as well.
1295     *
1296     * @see #getUsersToEmail
1297     */

1298    public Set JavaDoc getAllUsersToEmail(String JavaDoc action) throws Exception JavaDoc
1299    {
1300        Set JavaDoc result = null;
1301        Object JavaDoc obj = ScarabCache.get(this, GET_ALL_USERS_TO_EMAIL, action);
1302        if (obj == null)
1303        {
1304            Set JavaDoc users = new HashSet JavaDoc();
1305            try
1306            {
1307                users = getUsersToEmail(action, this, users);
1308                List JavaDoc children = getChildren();
1309                for (int i=0;i<children.size();i++)
1310                {
1311                    Issue depIssue = IssueManager.getInstance
1312                        (((Depend) children.get(i)).getObserverId());
1313                    users = getUsersToEmail(action, depIssue, users);
1314                }
1315                result = users;
1316            }
1317            catch (Exception JavaDoc e)
1318            {
1319                getLog().error("Issue.getUsersToEmail(): ", e);
1320                throw new Exception JavaDoc("Error in retrieving users."); //EXCEPTION
1321
}
1322            ScarabCache.put(result, this, GET_ALL_USERS_TO_EMAIL, action);
1323        }
1324        else
1325        {
1326            result = (Set JavaDoc)obj;
1327        }
1328        return result;
1329    }
1330
1331    /**
1332     * Returns the specific user's attribute value.
1333     */

1334    public AttributeValue getUserAttributeValue(ScarabUser user, Attribute attribute)
1335        throws Exception JavaDoc
1336    {
1337        AttributeValue result = null;
1338        Object JavaDoc obj = getCachedObject(GET_USER_ATTRIBUTEVALUE,
1339            attribute.getAttributeId(), user.getUserId());
1340        if (obj == null)
1341        {
1342            Criteria crit = new Criteria()
1343                .add(AttributeValuePeer.ATTRIBUTE_ID, attribute.getAttributeId())
1344                .add(AttributeValuePeer.ISSUE_ID, getIssueId())
1345                .add(AttributeValuePeer.USER_ID, user.getUserId())
1346                .add(AttributeValuePeer.DELETED, 0);
1347            List JavaDoc resultList = AttributeValuePeer.doSelect(crit);
1348            if (resultList != null && resultList.size() == 1)
1349            {
1350                result = (AttributeValue)resultList.get(0);
1351            }
1352            putCachedObject(result, GET_USER_ATTRIBUTEVALUE,
1353                attribute.getAttributeId(), user.getUserId());
1354        }
1355        else
1356        {
1357            result = (AttributeValue)obj;
1358        }
1359        return result;
1360    }
1361
1362    /**
1363     * Returns attribute values for user attributes.
1364     */

1365    public List JavaDoc getUserAttributeValues() throws Exception JavaDoc
1366    {
1367        List JavaDoc result = null;
1368        Object JavaDoc obj = getCachedObject(GET_USER_ATTRIBUTEVALUES);
1369        if (obj == null)
1370        {
1371            List JavaDoc attributeList = getModule().getUserAttributes(getIssueType(), true);
1372            List JavaDoc attributeIdList = new ArrayList JavaDoc();
1373            
1374            for (int i=0; i<attributeList.size(); i++)
1375            {
1376                Attribute att = (Attribute) attributeList.get(i);
1377                attributeIdList.add(att.getAttributeId());
1378            }
1379            
1380            if(!attributeIdList.isEmpty())
1381            {
1382                Criteria crit = new Criteria()
1383                    .addIn(AttributeValuePeer.ATTRIBUTE_ID, attributeIdList)
1384                    .add(AttributeValuePeer.ISSUE_ID, getIssueId())
1385                    .add(AttributeValuePeer.DELETED, 0);
1386                result = AttributeValuePeer.doSelect(crit);
1387            }
1388            else
1389            {
1390                result = new ArrayList JavaDoc(0);
1391            }
1392            putCachedObject(result, GET_USER_ATTRIBUTEVALUES);
1393        }
1394        else
1395        {
1396            result = (List JavaDoc)obj;
1397        }
1398        return result;
1399    }
1400
1401     
1402    /**
1403     * The initial activity set from issue creation.
1404     *
1405     * @return a <code>ActivitySet</code> value
1406     * @exception Exception if an error occurs
1407     */

1408    public ActivitySet getInitialActivitySet()
1409        throws Exception JavaDoc
1410    {
1411        ActivitySet activitySet = getActivitySet();
1412        if (activitySet == null)
1413        {
1414            Log.get().warn("Creation ActivitySet is null for " + this);
1415        }
1416        
1417        return activitySet;
1418    }
1419
1420    /**
1421     * The date the issue was created.
1422     *
1423     * @return a <code>Date</code> value
1424     * @exception TorqueException if an error occurs
1425     */

1426    public Date JavaDoc getCreatedDate()
1427        throws TorqueException
1428    {
1429        ActivitySet creationSet = getActivitySet();
1430        Date JavaDoc result = null;
1431        if (creationSet == null)
1432        {
1433            getLog().warn("Issue " + getUniqueId() + " (pk=" + getIssueId() +
1434                           ") does not have a creation ActivitySet");
1435        }
1436        else
1437        {
1438            result = creationSet.getCreatedDate();
1439        }
1440        return result;
1441    }
1442
1443    /**
1444     * The user that created the issue.
1445     * @return a <code>ScarabUser</code> value
1446     */

1447    public ScarabUser getCreatedBy()
1448        throws TorqueException
1449    {
1450        ActivitySet creationSet = getActivitySet();
1451        ScarabUser result = null;
1452        if (creationSet == null)
1453        {
1454            getLog().warn("Issue " + getUniqueId() + " (pk=" + getIssueId() +
1455                           ") does not have a creation ActivitySet");
1456        }
1457        else
1458        {
1459            result = creationSet.getScarabUser();
1460        }
1461        return result;
1462    }
1463
1464    public boolean isCreatingUser(ScarabUser user)
1465         throws Exception JavaDoc
1466    {
1467        ActivitySet creationSet = getActivitySet();
1468        boolean result = false;
1469        if (creationSet == null)
1470        {
1471            getLog().warn("Issue " + getUniqueId() + " (pk=" + getIssueId() +
1472                           ") does not have a creation ActivitySet");
1473        }
1474        else
1475        {
1476            result = creationSet.getCreatedBy().equals(user.getUserId());
1477        }
1478        return result;
1479    }
1480
1481    /**
1482     * The last modification made to the issue.
1483     *
1484     * @return a <code>ScarabUser</code> value
1485     */

1486    public ActivitySet getLastActivitySet()
1487        throws Exception JavaDoc
1488    {
1489        ActivitySet t = null;
1490        if (!isNew())
1491        {
1492            Object JavaDoc obj = ScarabCache.get(this, GET_LAST_TRANSACTION);
1493            if (obj == null)
1494            {
1495                Criteria crit = new Criteria();
1496                crit.addJoin(ActivitySetPeer.TRANSACTION_ID,
1497                         ActivityPeer.TRANSACTION_ID);
1498                crit.add(ActivityPeer.ISSUE_ID, getIssueId());
1499                Integer JavaDoc[] typeIds = {ActivitySetTypePeer.EDIT_ISSUE__PK,
1500                                       ActivitySetTypePeer.MOVE_ISSUE__PK};
1501                crit.addIn(ActivitySetPeer.TYPE_ID, typeIds);
1502                // there could be multiple attributes modified during the
1503
// creation which will lead to duplicates
1504
crit.setDistinct();
1505                crit.addDescendingOrderByColumn(ActivitySetPeer.CREATED_DATE);
1506                List JavaDoc activitySets = ActivitySetPeer.doSelect(crit);
1507                if (activitySets.size() > 0)
1508                {
1509                    t = (ActivitySet)activitySets.get(0);
1510                }
1511                ScarabCache.put(t, this, GET_LAST_TRANSACTION);
1512            }
1513            else
1514            {
1515                t = (ActivitySet)obj;
1516            }
1517        }
1518        return t;
1519    }
1520
1521    /**
1522     * The date issue was last modified.
1523     *
1524     * @return a <code>ScarabUser</code> value
1525     */

1526    public Date JavaDoc getModifiedDate()
1527        throws Exception JavaDoc
1528    {
1529        Date JavaDoc result = null;
1530        if (!isNew())
1531        {
1532            ActivitySet t = getLastActivitySet();
1533            if (t == null)
1534            {
1535                result = getCreatedDate();
1536            }
1537            else
1538            {
1539                result = t.getCreatedDate();
1540            }
1541        }
1542        return result;
1543    }
1544
1545    /**
1546     * The last user to modify the issue.
1547     *
1548     * @return a <code>ScarabUser</code> value
1549     */

1550    public ScarabUser getModifiedBy()
1551        throws Exception JavaDoc
1552    {
1553        ScarabUser result = null;
1554        if (!isNew())
1555        {
1556            ActivitySet t = getLastActivitySet();
1557            if (t == null)
1558            {
1559                result = getCreatedBy();
1560            }
1561            else
1562            {
1563                result = ScarabUserManager
1564                    .getInstance(t.getCreatedBy());
1565            }
1566        }
1567        return result;
1568    }
1569
1570
1571    /**
1572     * Returns the total number of comments.
1573     */

1574    public int getCommentsCount() throws Exception JavaDoc
1575    {
1576        return getComments(true).size();
1577    }
1578
1579    /**
1580     * Determines whether the comments list is longer than
1581     * The default limit.
1582     */

1583    public boolean isCommentsLong() throws Exception JavaDoc
1584    {
1585        return (getCommentsCount() > getCommentsLimit());
1586    }
1587
1588    /**
1589     * Gets default comments limit for this module-issue type.
1590     */

1591    public int getCommentsLimit() throws Exception JavaDoc
1592    {
1593        int limit=0;
1594        try
1595        {
1596            limit = getModule().getRModuleIssueType(getIssueType())
1597                    .getComments();
1598        }
1599        catch (Exception JavaDoc e)
1600        {
1601            // ignored (return 0 by default)
1602
}
1603        return limit;
1604    }
1605
1606    /**
1607     * Returns a list of Attachment objects with type "Comment"
1608     * That are associated with this issue.
1609     */

1610    public List JavaDoc getComments(boolean full) throws Exception JavaDoc
1611    {
1612        List JavaDoc result = null;
1613        Boolean JavaDoc fullBool = (full ? Boolean.TRUE : Boolean.FALSE);
1614        Object JavaDoc obj = getCachedObject(GET_COMMENTS, fullBool);
1615        if (obj == null)
1616        {
1617            Criteria crit = new Criteria()
1618                .add(AttachmentPeer.ISSUE_ID, getIssueId())
1619                .addJoin(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1620                         AttachmentPeer.ATTACHMENT_TYPE_ID)
1621                .add(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1622                     Attachment.COMMENT__PK)
1623                .addDescendingOrderByColumn(AttachmentPeer.CREATED_DATE);
1624            if (!full)
1625            {
1626                crit.setLimit(getCommentsLimit());
1627            }
1628            result = AttachmentPeer.doSelect(crit);
1629            putCachedObject(result, GET_COMMENTS, fullBool);
1630        }
1631        else
1632        {
1633            result = (List JavaDoc)obj;
1634        }
1635        return result;
1636    }
1637
1638
1639    /**
1640     * Returns a list of Attachment objects with type "URL"
1641     * That are associated with this issue.
1642     */

1643    public List JavaDoc getUrls() throws Exception JavaDoc
1644    {
1645        List JavaDoc result = null;
1646        Object JavaDoc obj = getCachedObject(GET_URLS);
1647        if (obj == null)
1648        {
1649            Criteria crit = new Criteria()
1650                .add(AttachmentPeer.ISSUE_ID, getIssueId())
1651                .addJoin(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1652                         AttachmentPeer.ATTACHMENT_TYPE_ID)
1653                .add(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1654                     Attachment.URL__PK)
1655                .add(AttachmentPeer.DELETED, 0);
1656            result = AttachmentPeer.doSelect(crit);
1657            putCachedObject(result, GET_URLS);
1658        }
1659        else
1660        {
1661            result = (List JavaDoc)obj;
1662        }
1663        return result;
1664    }
1665    
1666
1667    /**
1668     * Get attachments that are not deleted
1669     */

1670    public List JavaDoc getExistingAttachments() throws Exception JavaDoc
1671    {
1672        List JavaDoc result = null;
1673        Object JavaDoc obj = getCachedObject(GET_EXISTING_ATTACHMENTS);
1674        if (obj == null)
1675        {
1676            Criteria crit = new Criteria()
1677                .add(AttachmentPeer.ISSUE_ID, getIssueId())
1678                .addJoin(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1679                         AttachmentPeer.ATTACHMENT_TYPE_ID)
1680                .add(AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1681                     Attachment.FILE__PK)
1682                .add(AttachmentPeer.DELETED, 0);
1683            result = AttachmentPeer.doSelect(crit);
1684            putCachedObject(result, GET_EXISTING_ATTACHMENTS);
1685        }
1686        else
1687        {
1688            result = (List JavaDoc)obj;
1689        }
1690        return result;
1691    }
1692
1693    public List JavaDoc getActivitiesWithNullEndDate(Attribute attribute)
1694        throws TorqueException
1695    {
1696        List JavaDoc result = null;
1697        Object JavaDoc obj = ScarabCache.get(this, GET_NULL_END_DATE, attribute);
1698        if (obj == null)
1699        {
1700            Criteria crit = new Criteria();
1701            crit.add(ActivityPeer.ISSUE_ID, this.getIssueId());
1702            crit.add(ActivityPeer.ATTRIBUTE_ID, attribute.getAttributeId());
1703            crit.add(ActivityPeer.END_DATE, null);
1704            result = ActivityPeer.doSelect(crit);
1705            ScarabCache.put(result, this, GET_NULL_END_DATE, attribute);
1706        }
1707        else
1708        {
1709            result = (List JavaDoc)obj;
1710        }
1711        return result;
1712    }
1713
1714    /**
1715     * Gets default history limit for this module-issue type.
1716     * The default is 5.
1717     */

1718    public int getHistoryLimit() throws Exception JavaDoc
1719    {
1720        RModuleIssueType rmit = getModule().getRModuleIssueType(getIssueType());
1721        if (rmit != null)
1722        {
1723            return rmit.getHistory();
1724        }
1725        else
1726        {
1727            return 5;
1728        }
1729    }
1730
1731    /**
1732     * Determines whether the history list is longer than
1733     * The default limit.
1734     */

1735    public boolean isHistoryLong() throws Exception JavaDoc
1736    {
1737        return isHistoryLong(getHistoryLimit());
1738    }
1739
1740    /**
1741     * Determines whether the history list is longer than
1742     * The limit.
1743     */

1744    public boolean isHistoryLong(int limit) throws Exception JavaDoc
1745    {
1746        return (getActivity(true).size() > limit);
1747    }
1748
1749    /**
1750     * Returns list of Activity objects associated with this Issue.
1751     */

1752    public List JavaDoc getActivity() throws Exception JavaDoc
1753    {
1754        return getActivity(false, getHistoryLimit());
1755    }
1756
1757    /**
1758     * Returns limited list of Activity objects associated with this Issue.
1759     */

1760    public List JavaDoc getActivity(int limit) throws Exception JavaDoc
1761    {
1762        return getActivity(false, limit);
1763    }
1764
1765    /**
1766     * Returns limited list of Activity objects associated with this Issue.
1767     * If fullHistory is false, it limits it,
1768     * (this is the default)
1769     */

1770    public List JavaDoc getActivity(boolean fullHistory) throws Exception JavaDoc
1771    {
1772        return getActivity(fullHistory, getHistoryLimit());
1773    }
1774
1775    /**
1776     * Returns full list of Activity objects associated with this Issue.
1777     */

1778    private List JavaDoc getActivity(boolean fullHistory, int limit) throws Exception JavaDoc
1779    {
1780        List JavaDoc result = null;
1781        Boolean JavaDoc fullHistoryObj = fullHistory ? Boolean.TRUE : Boolean.FALSE;
1782        Object JavaDoc obj = getCachedObject(GET_ACTIVITY, fullHistoryObj,
1783                                     new Integer JavaDoc(limit));
1784        if (obj == null)
1785        {
1786            Criteria crit = new Criteria()
1787                .add(ActivityPeer.ISSUE_ID, getIssueId())
1788                .addAscendingOrderByColumn(ActivityPeer.TRANSACTION_ID);
1789            if (!fullHistory)
1790            {
1791                crit.setLimit(limit);
1792            }
1793            result = ActivityPeer.doSelect(crit);
1794            putCachedObject(result, GET_ACTIVITY,
1795                            fullHistoryObj, new Integer JavaDoc(limit));
1796        }
1797        else
1798        {
1799            result = (List JavaDoc)obj;
1800        }
1801        return result;
1802    }
1803
1804    /**
1805     * Returns limited list of Activity objects associated with this Issue.
1806     */

1807    public void addActivity(Activity activity) throws TorqueException
1808    {
1809        List JavaDoc activityList = null;
1810        try
1811        {
1812            activityList = getActivity(true);
1813        }
1814        catch (Exception JavaDoc e)
1815        {
1816            throw new TorqueException(e); //EXCEPTION
1817
}
1818        super.addActivity(activity);
1819        if (!activityList.contains(activity))
1820        {
1821            activityList.add(activity);
1822        }
1823    }
1824
1825    /**
1826     * Returns a list of ActivitySet objects associated to this issue.
1827     */

1828    public List JavaDoc getActivitySets()
1829        throws Exception JavaDoc
1830    {
1831        List JavaDoc result = null;
1832        Object JavaDoc obj = ScarabCache.get(this, GET_TRANSACTIONS);
1833        if (obj == null)
1834        {
1835            Criteria crit = new Criteria();
1836            crit.add(ActivityPeer.ISSUE_ID, getIssueId());
1837            crit.addJoin(ActivitySetPeer.TRANSACTION_ID, ActivityPeer.TRANSACTION_ID);
1838            crit.setDistinct();
1839            result = ActivitySetPeer.doSelect(crit);
1840            ScarabCache.put(result, this, GET_TRANSACTIONS);
1841        }
1842        else
1843        {
1844            result = (List JavaDoc)obj;
1845        }
1846        return result;
1847    }
1848
1849    /**
1850     * Creates a new ActivitySet object for the issue.
1851     */

1852    public ActivitySet getActivitySet(ScarabUser user, Attachment attachment,
1853                                      Integer JavaDoc type)
1854        throws Exception JavaDoc
1855    {
1856        ActivitySet activitySet = null;
1857        if (attachment == null)
1858        {
1859            activitySet = ActivitySetManager
1860                .getInstance(type, user);
1861        }
1862        else
1863        {
1864            activitySet = ActivitySetManager
1865                .getInstance(type, user, attachment);
1866        }
1867        return activitySet;
1868    }
1869
1870    /**
1871     * Creates a new ActivitySet object for the issue.
1872     */

1873    public ActivitySet getActivitySet(ScarabUser user, Integer JavaDoc type)
1874        throws Exception JavaDoc
1875    {
1876        return getActivitySet(user, null, type);
1877    }
1878
1879    /**
1880     * Returns the combined output from getChildren() and getParents()
1881     */

1882    public List JavaDoc getAllDependencies()
1883        throws Exception JavaDoc
1884    {
1885        List JavaDoc dependencies = new ArrayList JavaDoc();
1886        dependencies.addAll(getChildren());
1887        dependencies.addAll(getParents());
1888        return dependencies;
1889    }
1890
1891    /**
1892     * Returns list of child dependencies
1893     * i.e., related to this issue through the DEPEND table.
1894     */

1895    public List JavaDoc getChildren() throws Exception JavaDoc
1896    {
1897        return getChildren(true);
1898    }
1899
1900    /**
1901     * Returns list of child dependencies
1902     * i.e., related to this issue through the DEPEND table.
1903     */

1904    public List JavaDoc getChildren(boolean hideDeleted) throws Exception JavaDoc
1905    {
1906        List JavaDoc result = null;
1907        Boolean JavaDoc hide = hideDeleted ? Boolean.TRUE : Boolean.FALSE;
1908        Object JavaDoc obj = getCachedObject(GET_CHILDREN, hide);
1909        if (obj == null)
1910        {
1911            Criteria crit = new Criteria()
1912                .add(DependPeer.OBSERVED_ID, getIssueId());
1913            if (hideDeleted)
1914            {
1915                crit.add(DependPeer.DELETED, false);
1916            }
1917            result = DependPeer.doSelect(crit);
1918            putCachedObject(result, GET_CHILDREN, hide);
1919        }
1920        else
1921        {
1922            result = (List JavaDoc)obj;
1923        }
1924        return result;
1925    }
1926
1927    /**
1928     * Returns list of parent dependencies
1929     * i.e., related to this issue through the DEPEND table.
1930     */

1931    public List JavaDoc getParents() throws Exception JavaDoc
1932    {
1933        return getParents(true);
1934    }
1935
1936    /**
1937     * Returns list of parent dependencies
1938     * i.e., related to this issue through the DEPEND table.
1939     */

1940    public List JavaDoc getParents(boolean hideDeleted) throws Exception JavaDoc
1941    {
1942        List JavaDoc result = null;
1943        Boolean JavaDoc hide = hideDeleted ? Boolean.TRUE : Boolean.FALSE;
1944        Object JavaDoc obj = getCachedObject(GET_PARENTS, hide);
1945        if (obj == null)
1946        {
1947            Criteria crit = new Criteria()
1948                .add(DependPeer.OBSERVER_ID, getIssueId());
1949            if (hideDeleted)
1950            {
1951                crit.add(DependPeer.DELETED, false);
1952            }
1953            result = DependPeer.doSelect(crit);
1954            putCachedObject(result, GET_PARENTS, hide);
1955        }
1956        else
1957        {
1958            result = (List JavaDoc)obj;
1959        }
1960        return result;
1961    }
1962        
1963
1964    /**
1965     * Returns list of all types of dependencies an issue can have
1966     * On another issue.
1967     * @deprecated use DependencyTypeManager.getAll();
1968     */

1969    public List JavaDoc getAllDependencyTypes() throws Exception JavaDoc
1970    {
1971        return DependTypeManager.getAll();
1972    }
1973
1974    public ActivitySet doAddDependency(ActivitySet activitySet, Depend depend,
1975                                       Issue childIssue, ScarabUser user)
1976        throws Exception JavaDoc
1977    {
1978        // Check whether the entered issue is already dependent on this
1979
// Issue. If so, then throw an exception because we don't want
1980
// to add it again.
1981
Depend prevDepend = this.getDependency(childIssue, true);
1982        if (prevDepend != null)
1983        {
1984            throw new ScarabException(L10NKeySet.DependencyExists);
1985        }
1986
1987        // we definitely want to do an insert here so force it.
1988
depend.setNew(true);
1989        depend.setDeleted(false);
1990        depend.save();
1991
1992        if (activitySet == null)
1993        {
1994            // deal with user comments
1995
Attachment comment = depend.getDescriptionAsAttachment(user, this);
1996            // Save activitySet record
1997
activitySet = getActivitySet(user, comment,
1998                              ActivitySetTypePeer.EDIT_ISSUE__PK);
1999            activitySet.save();
2000        }
2001
2002        Object JavaDoc[] args = {
2003            this.getUniqueId(),
2004            depend.getAction(),
2005            childIssue.getUniqueId()
2006        };
2007
2008        String JavaDoc desc = Localization.format(
2009            ScarabConstants.DEFAULT_BUNDLE_NAME,
2010            getLocale(),
2011            "AddDependency", args);
2012
2013        // Save activity record for the parent issue
2014
ActivityManager
2015            .createAddDependencyActivity(this, activitySet, depend, desc);
2016
2017        // Save activity record for the child issue
2018
ActivityManager
2019            .createAddDependencyActivity(childIssue, activitySet, depend, desc);
2020
2021        return activitySet;
2022    }
2023    /**
2024     * Checks to see if this issue has a dependency on the passed in issue.
2025     * or if the passed in issue has a dependency on this issue.
2026     */

2027    public Depend getDependency(Issue potentialDependency) throws Exception JavaDoc
2028    {
2029        return getDependency(potentialDependency, true);
2030    }
2031
2032    /**
2033     * Checks to see if this issue has a dependency on the passed in issue.
2034     * or if the passed in issue has a dependency on this issue.
2035     *
2036     * @param potentialDependency the issue for which we are determining if there is a
2037     * parent or child dependency to this issue
2038     * @param hideDeleted true if deleted issues are omitted from the search
2039     * @returns the dependency object or null
2040     */

2041    public Depend getDependency(Issue potentialDependency, boolean hideDeleted) throws Exception JavaDoc
2042    {
2043        Depend result = null;
2044        Object JavaDoc obj = ScarabCache.get(this, GET_DEPENDENCY, potentialDependency);
2045        if (obj == null)
2046        {
2047
2048            // Determine if this issue is a parent to the potentialDependency
2049
Criteria crit = new Criteria(2)
2050                .add(DependPeer.OBSERVED_ID, getIssueId())
2051                .add(DependPeer.OBSERVER_ID, potentialDependency.getIssueId());
2052            if (hideDeleted)
2053            {
2054                crit.add(DependPeer.DELETED, false);
2055            }
2056
2057            List JavaDoc childIssues = DependPeer.doSelect(crit);
2058            // A system invariant is that we will get one and only one
2059
// record back.
2060
if (!childIssues.isEmpty())
2061            {
2062                result = (Depend)childIssues.get(0);
2063            }
2064            else
2065            {
2066                // Determine if this issue is a child to the potentialDependency
2067
Criteria crit2 = new Criteria(2)
2068                    .add(DependPeer.OBSERVER_ID, getIssueId())
2069                    .add(DependPeer.OBSERVED_ID, potentialDependency.getIssueId());
2070                if (hideDeleted)
2071                {
2072                    crit2.add(DependPeer.DELETED, false);
2073                }
2074                List JavaDoc parentIssues = DependPeer.doSelect(crit2);
2075                if (!parentIssues.isEmpty())
2076                {
2077                    result = (Depend)parentIssues.get(0);
2078                }
2079            }
2080
2081            if (result != null)
2082            {
2083                ScarabCache.put(result, this, GET_DEPENDENCY, potentialDependency);
2084            }
2085        }
2086        else
2087        {
2088            result = (Depend)obj;
2089        }
2090        return result;
2091    }
2092
2093    /**
2094     * Removes any unset attributes and sets the issue # prior to saving
2095     * for the first time. Calls super.save()
2096     *
2097     * @param dbCon a <code>DBConnection</code> value
2098     * @exception TorqueException if an error occurs
2099     */

2100    public void save(Connection JavaDoc dbCon)
2101        throws TorqueException
2102    {
2103        Module module = getModule();
2104        if (!module.allowsIssues() || (isNew() && !module.allowsNewIssues()))
2105        {
2106            throw new UnsupportedOperationException JavaDoc(module.getName() +
2107                " does not allow issues."); //EXCEPTION
2108
}
2109        
2110        // remove unset AttributeValues before saving
2111
List JavaDoc attValues = getAttributeValues();
2112        // reverse order since removing from list
2113
for (int i=attValues.size()-1; i>=0; i--)
2114        {
2115            AttributeValue attVal = (AttributeValue) attValues.get(i);
2116            if (!attVal.isSet())
2117            {
2118                attValues.remove(i);
2119            }
2120        }
2121
2122        if (isNew())
2123        {
2124            // set the issue id
2125
setIdDomain(module.getScarabInstanceId());
2126            setIdPrefix(module.getCode());
2127
2128            // for an enter issue template, do not give issue id
2129
// set id count to -1 so does not show up as an issue
2130
if (isTemplate())
2131            {
2132                setIdCount(-1);
2133            }
2134            else
2135            {
2136                try
2137                {
2138                    setIdCount(getNextIssueId(dbCon));
2139                }
2140                catch (Exception JavaDoc e)
2141                {
2142                    throw new TorqueException(e); //EXCEPTION
2143
}
2144            }
2145        }
2146        super.save(dbCon);
2147    }
2148
2149
2150    private int getNextIssueId(Connection JavaDoc con)
2151        throws Exception JavaDoc
2152    {
2153        int id = -1;
2154        String JavaDoc key = getIdTableKey();
2155        DatabaseMap dbMap = IssuePeer.getTableMap().getDatabaseMap();
2156        IDBroker idbroker = dbMap.getIDBroker();
2157        try
2158        {
2159            id = idbroker.getIdAsInt(con, key);
2160        }
2161        catch (Exception JavaDoc e)
2162        {
2163            synchronized (idbroker)
2164            {
2165                try
2166                {
2167                    id = idbroker.getIdAsInt(con, key);
2168                }
2169                catch (Exception JavaDoc idRetrievalErr)
2170                {
2171                    // a module code entry in the id_table was likely not
2172
// entered, insert a row into the id_table and try again.
2173
try
2174                    {
2175                        saveIdTableKey(con);
2176                        id = 1;
2177                    }
2178                    catch (Exception JavaDoc badException)
2179                    {
2180                        getLog().error("Could not get an id, even after "
2181                            +"trying to add a module entry into the ID_TABLE",
2182                            e);
2183                        getLog()
2184                            .error("Error trying to create ID_TABLE entry for "
2185                                   + getIdTableKey(), badException);
2186                        // throw the original
2187
throw new ScarabException(
2188                            L10NKeySet.ExceptionRetrievingIssueId,
2189                            badException);
2190                    }
2191                }
2192            }
2193        }
2194        return id;
2195    }
2196
2197    private String JavaDoc getIdTableKey()
2198        throws Exception JavaDoc
2199    {
2200        Module module = getModule();
2201        String JavaDoc prefix = module.getCode();
2202
2203        String JavaDoc domain = module.getScarabInstanceId();
2204        if (domain != null && domain.length() > 0)
2205        {
2206            prefix = domain + "-" + prefix;
2207        }
2208        return prefix;
2209    }
2210
2211    private void saveIdTableKey(Connection JavaDoc dbCon)
2212        throws Exception JavaDoc
2213    {
2214        int id = 0;
2215        DatabaseMap dbMap = IssuePeer.getTableMap().getDatabaseMap();
2216        IDBroker idbroker = dbMap.getIDBroker();
2217        String JavaDoc idTable = IDBroker.TABLE_NAME.substring(0,
2218             IDBroker.TABLE_NAME.indexOf('.'));
2219        id = idbroker.getIdAsInt(dbCon, idTable);
2220
2221        String JavaDoc key = getIdTableKey();
2222
2223        // FIXME: UGLY! IDBroker doesn't have a Peer yet.
2224
String JavaDoc sql = "insert into " + idTable
2225         + " (ID_TABLE_ID,TABLE_NAME,NEXT_ID,QUANTITY) "
2226         + " VALUES (" + id + ",'" + key + "',2,1)" ;
2227        BasePeer.executeStatement(sql, dbCon);
2228    }
2229
2230    /**
2231     * Returns list of issue template types.
2232    public List getTemplateTypes() throws Exception
2233    {
2234        List result = null;
2235        Object obj = ScarabCache.get(this, GET_TEMPLATE_TYPES);
2236        if (obj == null)
2237        {
2238            Criteria crit = new Criteria()
2239                .add(IssueTypePeer.ISSUE_TYPE_ID,
2240                     IssueType.ISSUE__PK, Criteria.NOT_EQUAL);
2241            result = IssueTypePeer.doSelect(crit);
2242            ScarabCache.put(result, this, GET_TEMPLATE_TYPES);
2243        }
2244        else
2245        {
2246            result = (List)obj;
2247        }
2248        return result;
2249    }
2250     */

2251
2252
2253    /**
2254     * Get IssueTemplateInfo by Issue Id.
2255     */

2256    public IssueTemplateInfo getTemplateInfo()
2257          throws Exception JavaDoc
2258    {
2259        IssueTemplateInfo result = null;
2260        Object JavaDoc obj = ScarabCache.get(this, GET_TEMPLATEINFO);
2261        if (obj == null)
2262        {
2263            Criteria crit = new Criteria(1);
2264            crit.add(IssueTemplateInfoPeer.ISSUE_ID, getIssueId());
2265            result = (IssueTemplateInfo)IssueTemplateInfoPeer
2266                .doSelect(crit).get(0);
2267            ScarabCache.put(result, this, GET_TEMPLATEINFO);
2268        }
2269        else
2270        {
2271            result = (IssueTemplateInfo)obj;
2272        }
2273        return result;
2274    }
2275
2276    /**
2277     * Get Unset required attributes in destination module / issue type.
2278     */

2279    public List JavaDoc getUnsetRequiredAttrs(Module newModule, IssueType newIssueType)
2280        throws Exception JavaDoc
2281    {
2282        List JavaDoc attrs = new ArrayList JavaDoc();
2283        if (!getIssueType().getIssueTypeId()
2284            .equals(newIssueType.getIssueTypeId())
2285            || !getModule().getModuleId().equals(newModule.getModuleId()))
2286        {
2287            List JavaDoc requiredAttributes =
2288                newIssueType.getRequiredAttributes(newModule);
2289            Map JavaDoc attrValues = getAttributeValuesMap();
2290
2291            for (Iterator JavaDoc i = requiredAttributes.iterator(); i.hasNext(); )
2292            {
2293                Attribute attr = (Attribute)i.next();
2294                if (!attrValues.containsKey(attr.getName().toUpperCase()))
2295                {
2296                    attrs.add(attr);
2297                }
2298            }
2299        }
2300        return attrs;
2301    }
2302
2303    /**
2304     * Checks if the 'nonmatching' list contains the given 'value', treating the
2305     * UserAttributes as an special case, in which the UserName is used to make
2306     * the comparison.
2307     * @param nonmatching
2308     * @param value
2309     * @return
2310     */

2311    private boolean isNonMatchingAttribute(List JavaDoc nonmatching, AttributeValue value)
2312    {
2313        boolean bRdo = false;
2314        if (value instanceof UserAttribute)
2315        {
2316            for (Iterator JavaDoc it = nonmatching.iterator(); !bRdo && it.hasNext(); )
2317            {
2318                UserAttribute userAttr = (UserAttribute)it.next();
2319                bRdo = userAttr.getUserName().equals(((UserAttribute)value).getUserName());
2320            }
2321        }
2322        else
2323        {
2324            bRdo = nonmatching.contains(value);
2325        }
2326        return bRdo;
2327    }
2328    
2329    /**
2330     * Move or copy issue to destination module.
2331     */

2332    public Issue move(Module newModule, IssueType newIssueType,
2333                      String JavaDoc action, ScarabUser user, String JavaDoc reason,
2334                      List JavaDoc commentAttrs, List JavaDoc commentUserValues)
2335          throws Exception JavaDoc
2336    {
2337        Issue newIssue;
2338
2339        Attachment attachment = new Attachment();
2340
2341        Module oldModule = getModule();
2342
2343        // If moving to a new issue type, just change the issue type id
2344
// otherwise, create fresh issue
2345
if (getModule().getModuleId().equals(newModule.getModuleId())
2346            && !getIssueType().getIssueTypeId().equals(newIssueType.getIssueTypeId())
2347            && action.equals("move"))
2348        {
2349            newIssue = this;
2350            newIssue.setIssueType(newIssueType);
2351        }
2352        else
2353        {
2354            newIssue = newModule.getNewIssue(newIssueType);
2355        }
2356        newIssue.save();
2357
2358        if (newIssue != this)
2359        {
2360            // If moving issue to new module, delete original
2361
if (action.equals("move"))
2362            {
2363                setDeleted(true);
2364                save();
2365            }
2366
2367            ActivitySet createActivitySet = ActivitySetManager.getInstance(
2368                    ActivitySetTypePeer.CREATE_ISSUE__PK, getCreatedBy());
2369            createActivitySet.setCreatedDate(getCreatedDate());
2370            createActivitySet.save();
2371            newIssue.setCreatedTransId(createActivitySet.getActivitySetId());
2372            newIssue.save();
2373
2374
2375            // Adjust dependencies if its a new issue id
2376
// (i.e.. moved to new module)
2377
List JavaDoc children = getChildren();
2378            for (Iterator JavaDoc i = children.iterator(); i.hasNext();)
2379            {
2380                 Depend depend = (Depend)i.next();
2381                 if (action.equals("move"))
2382                 {
2383                     doDeleteDependency(null, depend, user);
2384                 }
2385                 Issue child = IssueManager.getInstance(depend.getObserverId());
2386                 Depend newDepend = new Depend();
2387                 newDepend.setObserverId(child.getIssueId());
2388                 newDepend.setObservedId(newIssue.getIssueId());
2389                 newDepend.setTypeId(depend.getTypeId());
2390                 newIssue.doAddDependency(null, newDepend, child, user);
2391            }
2392            List JavaDoc parents = getParents();
2393            for (Iterator JavaDoc j = parents.iterator(); j.hasNext();)
2394            {
2395                 Depend depend = (Depend)j.next();
2396                 if (action.equals("move"))
2397                 {
2398                     doDeleteDependency(null, depend, user);
2399                 }
2400                 Issue parent = IssueManager.getInstance(depend.getObservedId());
2401                 Depend newDepend = new Depend();
2402                 newDepend.setObserverId(newIssue.getIssueId());
2403                 newDepend.setObservedId(parent.getIssueId());
2404                 newDepend.setTypeId(depend.getTypeId());
2405                 parent.doAddDependency(null, newDepend, newIssue, user);
2406            }
2407
2408            // copy attachments: comments/files etc.
2409
Iterator JavaDoc attachments = getAttachments().iterator();
2410            while (attachments.hasNext())
2411            {
2412                Attachment oldA = (Attachment)attachments.next();
2413                Attachment newA = oldA.copy();
2414                newA.setIssueId(newIssue.getIssueId());
2415                newA.save();
2416                Activity oldAct = oldA.getActivity();
2417                if (oldAct != null)
2418                {
2419                    ActivitySet activitySet = getActivitySet(
2420                        user, ActivitySetTypePeer.EDIT_ISSUE__PK);
2421                    activitySet.save();
2422                    ActivityManager.createTextActivity(newIssue, activitySet,
2423                        oldA.getActivity().getDescription(), newA);
2424                }
2425                if (Attachment.FILE__PK.equals(newA.getTypeId()))
2426                {
2427                    oldA.copyFileTo(newA.getFullPath());
2428                }
2429            }
2430
2431            // Copy over activity sets for the source issue's previous
2432
// Transactions
2433
List JavaDoc activitySets = getActivitySets();
2434            List JavaDoc nonMatchingAttributes = getNonMatchingAttributeValuesList
2435                                               (newModule, newIssueType);
2436            List JavaDoc alreadyAssociatedUsers = new ArrayList JavaDoc();
2437            for (Iterator JavaDoc i = activitySets.iterator(); i.hasNext();)
2438            {
2439                ActivitySet as = (ActivitySet)i.next();
2440                ActivitySet newAS = null;
2441                Attachment newAtt = null;
2442                // If activity set has an attachment, make a copy for new issue
2443
if (as.getAttachmentId() != null)
2444                {
2445                    newAtt = as.getAttachment().copy();
2446                    newAtt.save();
2447                }
2448                // Copy over activities with sets
2449
List JavaDoc activities = as.getActivityListForIssue(this);
2450                for (Iterator JavaDoc j = activities.iterator(); j.hasNext();)
2451                {
2452                    Activity a = (Activity)j.next();
2453                    // Only copy transactions that are records of previous move/copies
2454
// Or transactions relating to attributes.
2455
// Other transactions (attachments, dependencies)
2456
// Will be saved when attachments and dependencies are copied
2457
if (as.getTypeId().equals((ActivitySetTypePeer.MOVE_ISSUE__PK))
2458                        || !a.getAttributeId().equals(new Integer JavaDoc("0")))
2459                    {
2460                        newAS = new ActivitySet();
2461                        newAS.setTypeId(as.getTypeId());
2462                        if (newAtt != null)
2463                        {
2464                            newAS.setAttachmentId(newAtt.getAttachmentId());
2465                        }
2466                        newAS.setCreatedBy(as.getCreatedBy());
2467                        newAS.setCreatedDate(as.getCreatedDate());
2468                        newAS.save();
2469
2470                        // iterate over and copy transaction's activities
2471
Activity newA = a.copy(newIssue, newAS);
2472                        newIssue.getActivity(true).add(newA);
2473
2474                        // If this is an activity relating to setting an attribute value
2475
// And the final value is in the issue right now, we'll copy
2476
// over the attribute value
2477
AttributeValue attVal = getAttributeValueWithValue(a.getAttribute(),
2478                                a.getNewValue(), a.getNewNumericValue());
2479                        if (a.getEndDate() == null && attVal != null)
2480                        {
2481                            List JavaDoc values = getAttributeValues(a.getAttribute());
2482                            for (Iterator JavaDoc it = values.iterator(); it.hasNext(); )
2483                            {
2484                                AttributeValue att = (AttributeValue)it.next();
2485                                // Only copy if the target artifact type contains this
2486
// Attribute
2487
if (attVal != null && !isNonMatchingAttribute(nonMatchingAttributes, att))
2488                                {
2489                                    boolean isUser = (att instanceof UserAttribute);
2490                                    if (!isUser || !alreadyAssociatedUsers.contains(((UserAttribute)att).getUserName()+att.getAttribute().getName()))
2491                                    {
2492                                        AttributeValue newAttVal = att.copy();
2493                                        newAttVal.setIssueId(newIssue.getIssueId());
2494                                        newAttVal.setActivity(newA);
2495                                        newAttVal.startActivitySet(newAS);
2496                                        newAttVal.save();
2497                                        if (isUser)
2498                                        {
2499                                            alreadyAssociatedUsers.add(((UserAttribute)att).getUserName()+att.getAttribute().getName());
2500                                        }
2501                                    }
2502                                }
2503                            }
2504                        }
2505                    }
2506                }
2507            }
2508        }
2509
2510        // Generate comment to deal with attributes that do not
2511
// Exist in destination module, as well as the user attributes.
2512
StringBuffer JavaDoc attachmentBuf = new StringBuffer JavaDoc();
2513        StringBuffer JavaDoc delAttrsBuf = new StringBuffer JavaDoc();
2514        if (reason != null && reason.length() > 0)
2515        {
2516            attachmentBuf.append(reason).append(". ");
2517        }
2518        if (commentAttrs.size() > 0 || commentUserValues.size() > 0 )
2519        {
2520            attachmentBuf.append(Localization.format(
2521               ScarabConstants.DEFAULT_BUNDLE_NAME,
2522               getLocale(), "DidNotCopyAttributes", newIssueType.getName() + "/" + newModule.getName()));
2523            attachmentBuf.append("\n");
2524            for (int i = 0; i < commentAttrs.size(); i++)
2525            {
2526                List JavaDoc attVals = getAttributeValues((Attribute) commentAttrs
2527                        .get(i));
2528                for (int j = 0; j < attVals.size(); j++)
2529                {
2530                    AttributeValue attVal = (AttributeValue) attVals.get(j);
2531                    String JavaDoc field = null;
2532                    delAttrsBuf.append(attVal.getAttribute().getName());
2533                    field = attVal.getValue();
2534                    delAttrsBuf.append("=").append(field).append(". ").append(
2535                            "\n");
2536                }
2537            }
2538            for (int i=0; i < commentUserValues.size(); i++)
2539            {
2540                UserAttribute useratt = (UserAttribute)commentUserValues.get(i);
2541                delAttrsBuf.append(useratt.getAttribute().getName() + ": " +
2542                        useratt.getUserName() + "\n");
2543            }
2544           String JavaDoc delAttrs = delAttrsBuf.toString();
2545           attachmentBuf.append(delAttrs);
2546
2547           // Also create a regular comment with non-matching attribute info
2548
Attachment comment = new Attachment();
2549           comment.setTextFields(user, newIssue, Attachment.COMMENT__PK);
2550
2551           Object JavaDoc[] args = {this.getUniqueId(), newIssueType.getName() + " / " + newModule.getName()};
2552           StringBuffer JavaDoc commentBuf = new StringBuffer JavaDoc(Localization.format(
2553              ScarabConstants.DEFAULT_BUNDLE_NAME,
2554              getLocale(),
2555              "DidNotCopyAttributesFromArtifact", args));
2556           commentBuf.append("\n").append(delAttrs);
2557           comment.setData(commentBuf.toString());
2558           comment.setName(Localization.getString(
2559               ScarabConstants.DEFAULT_BUNDLE_NAME,
2560               getLocale(),
2561               "Comment"));
2562           comment.save();
2563        }
2564        else
2565        {
2566            attachmentBuf.append(Localization.getString(
2567               ScarabConstants.DEFAULT_BUNDLE_NAME,
2568               getLocale(),
2569               "AllCopied"));
2570        }
2571        attachment.setData(attachmentBuf.toString());
2572
2573        if (action.equals("move"))
2574        {
2575            attachment.setName(Localization.getString(
2576               ScarabConstants.DEFAULT_BUNDLE_NAME,
2577               getLocale(),
2578               "MovedIssueNote"));
2579        }
2580        else
2581        {
2582            attachment.setName(Localization.getString(
2583               ScarabConstants.DEFAULT_BUNDLE_NAME,
2584               getLocale(),
2585               "CopiedIssueNote"));
2586        }
2587        attachment.setTextFields(user, newIssue, Attachment.MODIFICATION__PK);
2588        attachment.save();
2589
2590
2591        // Create activitySet for the MoveIssue activity
2592
ActivitySet activitySet2 = ActivitySetManager
2593            .getInstance(ActivitySetTypePeer.MOVE_ISSUE__PK, user, attachment);
2594        activitySet2.save();
2595
2596        // Generate comment
2597
Integer JavaDoc actionChoice = (action.equals("copy")) ? COPIED : MOVED;
2598        Object JavaDoc[] args = {
2599            actionChoice,
2600            getUniqueId(),
2601            oldModule.getName(),
2602            getIssueType().getName()
2603        };
2604        String JavaDoc desc = Localization.format(
2605            ScarabConstants.DEFAULT_BUNDLE_NAME,
2606            getLocale(),
2607            "MovedFromIssueDescription", args);
2608
2609        // Save activity record
2610
Attribute zeroAttribute = AttributeManager
2611            .getInstance(NUMBERKEY_0);
2612        ActivityManager
2613            .createTextActivity(newIssue, zeroAttribute, activitySet2,
2614                                desc, null,
2615                                getUniqueId(), newIssue.getUniqueId());
2616
2617        // Save activity record for old issue
2618
if (newIssue != this)
2619        {
2620            args[1] = newIssue.getUniqueId();
2621            args[2] = newModule.getName();
2622            args[3] = newIssueType.getName();
2623            desc = Localization.format(
2624                ScarabConstants.DEFAULT_BUNDLE_NAME,
2625                getLocale(),
2626                "MovedToIssueDescription", args);
2627
2628            ActivityManager
2629                .createTextActivity(this, zeroAttribute, activitySet2,
2630                                    desc, null,
2631                                    getUniqueId(), newIssue.getUniqueId());
2632        }
2633
2634
2635        return newIssue;
2636    }
2637
2638    public void addVote(ScarabUser user)
2639        throws ScarabException, Exception JavaDoc
2640    {
2641        // check to see if the user has voted for this issue
2642
int previousVotes = 0;
2643        IssueVote issueVote = null;
2644        Criteria crit = new Criteria()
2645            .add(IssueVotePeer.ISSUE_ID, getIssueId())
2646            .add(IssueVotePeer.USER_ID, user.getUserId());
2647        List JavaDoc votes = IssueVotePeer.doSelect(crit);
2648        if (votes != null && votes.size() != 0)
2649        {
2650            issueVote = (IssueVote)votes.get(0);
2651            previousVotes = issueVote.getVotes();
2652        }
2653        else
2654        {
2655            issueVote = new IssueVote();
2656            issueVote.setIssueId(getIssueId());
2657            issueVote.setUserId(user.getUserId());
2658        }
2659
2660        // check if the module accepts multiple votes
2661
if (!getModule().allowsMultipleVoting() && previousVotes > 0)
2662        {
2663            throw new ScarabException(L10NKeySet.ExceptionMultipleVoteForUnallowed,
2664                                      user.getUserName(),
2665                                      getUniqueId());
2666        }
2667        
2668        // save the user's vote
2669
issueVote.setVotes(previousVotes+1);
2670        issueVote.save();
2671
2672        // update the total votes for the issue
2673
crit = new Criteria()
2674            .add(AttributeValuePeer.ATTRIBUTE_ID,
2675                 AttributePeer.TOTAL_VOTES__PK);
2676        List JavaDoc voteValues = getAttributeValues(crit);
2677        TotalVotesAttribute voteValue = null;
2678        if (voteValues.size() == 0)
2679        {
2680            voteValue = new TotalVotesAttribute();
2681            voteValue.setIssue(this);
2682            voteValue.setAttributeId(AttributePeer.TOTAL_VOTES__PK);
2683        }
2684        else
2685        {
2686            voteValue = (TotalVotesAttribute)voteValues.get(0);
2687        }
2688        // Updating attribute values requires a activitySet
2689
ActivitySet activitySet = ActivitySetManager
2690            .getInstance(ActivitySetTypePeer.RETOTAL_ISSUE_VOTE__PK, user);
2691        activitySet.save();
2692        voteValue.startActivitySet(activitySet);
2693        voteValue.addVote();
2694        voteValue.save();
2695    }
2696
2697    /**
2698     * Gets a list of non-user AttributeValues which match a given Module.
2699     * It is used in the MoveIssue2.vm template
2700     */

2701    public List JavaDoc getMatchingAttributeValuesList(Module newModule,
2702                                               IssueType newIssueType)
2703          throws Exception JavaDoc
2704    {
2705        List JavaDoc matchingAttributes = new ArrayList JavaDoc();
2706        Map JavaDoc setMap = this.getAttributeValuesMap();
2707        for (Iterator JavaDoc iter = setMap.keySet().iterator(); iter.hasNext();)
2708        {
2709            AttributeValue aval = (AttributeValue)setMap.get(iter.next());
2710            List JavaDoc values = getAttributeValues(aval.getAttribute());
2711            // loop thru the values for this attribute
2712
for (int i = 0; i<values.size(); i++)
2713            {
2714                AttributeValue attVal = (AttributeValue)values.get(i);
2715                RModuleAttribute modAttr = newModule.
2716                    getRModuleAttribute(aval.getAttribute(), newIssueType);
2717
2718                // If this attribute is active for the destination module,
2719
// Add to matching attributes list
2720
if (modAttr != null && modAttr.getActive())
2721                {
2722                    // If attribute is an option attribute,
2723
// Check if attribute option is active for destination module.
2724
if (aval instanceof OptionAttribute)
2725                    {
2726                        // FIXME: Use select count
2727
Criteria crit2 = new Criteria(4)
2728                            .add(RModuleOptionPeer.ACTIVE, true)
2729                            .add(RModuleOptionPeer.OPTION_ID, attVal.getOptionId())
2730                            .add(RModuleOptionPeer.MODULE_ID, newModule.getModuleId())
2731                            .add(RModuleOptionPeer.ISSUE_TYPE_ID, newIssueType.getIssueTypeId());
2732                        List JavaDoc modOpt = RModuleOptionPeer.doSelect(crit2);
2733
2734                        if (!modOpt.isEmpty())
2735                        {
2736                            matchingAttributes.add(attVal);
2737                        }
2738                    }
2739                    else if (attVal instanceof UserAttribute)
2740                    {
2741                        ScarabUser user = null;
2742                        try
2743                        {
2744                            user = ScarabUserManager.getInstance(attVal.getUserId());
2745                        }
2746                        catch (Exception JavaDoc e)
2747                        {
2748                            getLog().error(e);
2749                            e.printStackTrace();
2750                        }
2751                        Attribute attr = attVal.getAttribute();
2752                        ScarabUser[] userArray = newModule.getUsers(attr.getPermission());
2753                        // If user exists in destination module with this permission,
2754
// Add as matching value
2755
if (Arrays.asList(userArray).contains(user))
2756                        {
2757                            matchingAttributes.add(attVal);
2758                        }
2759                    }
2760                    else
2761                    {
2762                        matchingAttributes.add(attVal);
2763                    }
2764                }
2765            }
2766        }
2767        return matchingAttributes;
2768    }
2769
2770    public List JavaDoc getMatchingAttributeValuesList(String JavaDoc moduleId, String JavaDoc issueTypeId)
2771          throws Exception JavaDoc
2772    {
2773         Module module = ModuleManager.getInstance(new Integer JavaDoc(moduleId));
2774         IssueType issueType = IssueTypeManager.getInstance(new Integer JavaDoc(issueTypeId));
2775         return getMatchingAttributeValuesList(module, issueType);
2776    }
2777
2778    /**
2779     * Gets a list AttributeValues which the source module has,
2780     * But the destination module does not have, when doing a copy.
2781     * It is used in the MoveIssue2.vm template
2782     */

2783    public List JavaDoc getNonMatchingAttributeValuesList(Module newModule,
2784                                             IssueType newIssueType)
2785          throws Exception JavaDoc
2786    {
2787        List JavaDoc nonMatchingAttributes = new ArrayList JavaDoc();
2788        AttributeValue aval = null;
2789
2790        Map JavaDoc setMap = this.getAttributeValuesMap();
2791        for (Iterator JavaDoc iter = setMap.values().iterator(); iter.hasNext();)
2792        {
2793            aval = (AttributeValue) iter.next();
2794            List JavaDoc values = getAttributeValues(aval.getAttribute());
2795            // loop thru the values for this attribute
2796
for (Iterator JavaDoc i = values.iterator(); i.hasNext(); )
2797            {
2798                AttributeValue attVal = (AttributeValue) i.next();
2799                RModuleAttribute modAttr = newModule.
2800                    getRModuleAttribute(aval.getAttribute(), newIssueType);
2801
2802                // If this attribute is not active for the destination module,
2803
// Add to nonMatchingAttributes list
2804
if (modAttr == null || !modAttr.getActive())
2805                {
2806                    nonMatchingAttributes.add(attVal);
2807                }
2808                else
2809                {
2810                    // If attribute is an option attribute, Check if
2811
// attribute option is active for destination module.
2812
if (attVal instanceof OptionAttribute)
2813                    {
2814                        Criteria crit2 = new Criteria(1)
2815                            .add(RModuleOptionPeer.ACTIVE, true)
2816                            .add(RModuleOptionPeer.OPTION_ID, attVal.getOptionId())
2817                            .add(RModuleOptionPeer.MODULE_ID, newModule.getModuleId())
2818                            .add(RModuleOptionPeer.ISSUE_TYPE_ID, newIssueType.getIssueTypeId());
2819                        List JavaDoc modOpt = RModuleOptionPeer.doSelect(crit2);
2820
2821                        if ( modOpt.isEmpty())
2822                        {
2823                                nonMatchingAttributes.add(attVal);
2824                        }
2825                    }
2826                    else if (attVal instanceof UserAttribute)
2827                    {
2828                        ScarabUser user = null;
2829                        try
2830                        {
2831                            user = ScarabUserManager.getInstance(attVal.getUserId());
2832                        }
2833                        catch (Exception JavaDoc e)
2834                        {
2835                            Log.get().error("Unable to retrieve user for "
2836                                            + "attribute", e);
2837                        }
2838                        Attribute attr = attVal.getAttribute();
2839                        ScarabUser[] userArray =
2840                            newModule.getUsers(attr.getPermission());
2841                        // If user exists in destination module with
2842
// this permission, add as matching value.
2843
if (!Arrays.asList(userArray).contains(user))
2844                        {
2845                            nonMatchingAttributes.add(attVal);
2846                        }
2847                    }
2848                }
2849            }
2850        }
2851        return nonMatchingAttributes;
2852    }
2853
2854
2855    public List JavaDoc getNonMatchingAttributeValuesList(String JavaDoc moduleId, String JavaDoc issueTypeId)
2856          throws Exception JavaDoc
2857    {
2858         Module module = ModuleManager.getInstance(new Integer JavaDoc(moduleId));
2859         IssueType issueType = IssueTypeManager.getInstance(new Integer JavaDoc(issueTypeId));
2860         return getNonMatchingAttributeValuesList(module, issueType);
2861    }
2862
2863    /**
2864     * Checks if user has permission to delete issue template.
2865     * Only the creating user can delete a personal template.
2866     * Only project owner or admin can delete a project-wide template.
2867     */

2868    public void delete(ScarabUser user)
2869         throws Exception JavaDoc, ScarabException
2870    {
2871        Module module = getModule();
2872        if (user.hasPermission(ScarabSecurity.ITEM__DELETE, module)
2873            || (user.getUserId().equals(getCreatedBy().getUserId()) && isTemplate()))
2874        {
2875            setDeleted(true);
2876            save();
2877        }
2878        else
2879        {
2880            throw new ScarabException(L10NKeySet.YouDoNotHavePermissionToAction);
2881        }
2882    }
2883
2884
2885    /**
2886     * This method will return the AttributeValue which represents
2887     * the default text attribute.
2888     *
2889     * @return the AttributeValue to use as the email subject, or null
2890     * or null if no suitable AttributeValue could be found.
2891     */

2892    public AttributeValue getDefaultTextAttributeValue()
2893        throws Exception JavaDoc
2894    {
2895        AttributeValue result = null;
2896        Object JavaDoc obj = ScarabCache.get(this, GET_DEFAULT_TEXT_ATTRIBUTEVALUE);
2897        if (obj == null)
2898        {
2899            Attribute defaultTextAttribute =
2900                getIssueType().getDefaultTextAttribute(getModule());
2901            if (defaultTextAttribute != null)
2902            {
2903                result = getAttributeValue(defaultTextAttribute);
2904            }
2905            ScarabCache.put(result, this, GET_DEFAULT_TEXT_ATTRIBUTEVALUE);
2906        }
2907        else
2908        {
2909            result = (AttributeValue)obj;
2910        }
2911        return result;
2912    }
2913
2914    /**
2915     * This calls getDefaultTextAttributeValue() and then returns the
2916     * String value of the Attribute. This method is used to get the
2917     * subject of an email. if no text attribute value is found it
2918     * will use the first ActivitySet comment.
2919     */

2920    public String JavaDoc getDefaultText()
2921        throws Exception JavaDoc
2922    {
2923        String JavaDoc result = null;
2924        Object JavaDoc obj = ScarabCache.get(this, GET_DEFAULT_TEXT);
2925        if (obj == null)
2926        {
2927            AttributeValue emailAV = getDefaultTextAttributeValue();
2928            if (emailAV != null)
2929            {
2930                result = emailAV.getValue();
2931            }
2932            if (result == null)
2933            {
2934                ActivitySet activitySet = getInitialActivitySet();
2935                if (activitySet != null)
2936                {
2937                    Attachment reason = activitySet.getAttachment();
2938                    if (reason != null && reason.getData() != null
2939                        && reason.getData().trim().length() > 0)
2940                    {
2941                        result = reason.getData();
2942                    }
2943                }
2944            }
2945            result = (result == null) ?
2946                Localization.getString(ScarabConstants.DEFAULT_BUNDLE_NAME,
2947                                       getLocale(), "NoIssueSummaryAvailable")
2948                      : result;
2949            ScarabCache.put(result, this, GET_DEFAULT_TEXT);
2950        }
2951        else
2952        {
2953            result = (String JavaDoc)obj;
2954        }
2955        return result;
2956    }
2957
2958
2959    private MethodResultCache getMethodResult()
2960    {
2961        return IssueManager.getMethodResult();
2962    }
2963
2964    /**
2965     * gets an object from the appropriate cache, based on whether this is
2966     * a saved issue. if you know the object should only be in ScarabCache
2967     * do not use this method.
2968     */

2969    private Object JavaDoc getCachedObject(String JavaDoc methodName)
2970    {
2971        Object JavaDoc obj = null;
2972        // Cache Note:
2973
// we check for issue id, so that we only (JCS) cache for saved issues
2974
// if we decide to cache results for new issues we should replace
2975
// this conditional with (this instanceof IssueSearch) because
2976
// we definitely do not want to cache those.
2977
if (getIssueId() == null)
2978        {
2979            obj = ScarabCache.get(this, methodName);
2980        }
2981        else
2982        {
2983            obj = getMethodResult().get(this, methodName);
2984        }
2985        return obj;
2986    }
2987
2988    /**
2989     * puts an object into the appropriate cache, based on whether this is
2990     * a saved issue. if you know the object should only be in ScarabCache
2991     * do not use this method.
2992     */

2993    private void putCachedObject(Object JavaDoc obj, String JavaDoc methodName)
2994    {
2995        // see Cache Note above
2996
if (getIssueId() == null)
2997        {
2998            ScarabCache.put(obj, this, methodName);
2999        }
3000        else
3001        {
3002            getMethodResult().put(obj, this, methodName);
3003        }
3004    }
3005
3006    /**
3007     * gets an object from the appropriate cache, based on whether this is
3008     * a saved issue. if you know the object should only be in ScarabCache
3009     * do not use this method.
3010     */

3011    private Object JavaDoc getCachedObject(String JavaDoc methodName, Serializable JavaDoc arg1)
3012    {
3013        Object JavaDoc obj = null;
3014        // Cache Note:
3015
// we check for issue id, so that we only (JCS) cache for saved issues
3016
// if we decide to cache results for new issues we should replace
3017
// this conditional with (this instanceof IssueSearch) because
3018
// we definitely do not want to cache those.
3019
if (getIssueId() == null)
3020        {
3021            obj = ScarabCache.get(this, methodName, arg1);
3022        }
3023        else
3024        {
3025            obj = getMethodResult().get(this, methodName, arg1);
3026        }
3027        return obj;
3028    }
3029
3030    /**
3031     * puts an object into the appropriate cache, based on whether this is
3032     * a saved issue. if you know the object should only be in ScarabCache
3033     * do not use this method.
3034     */

3035    private void putCachedObject(Object JavaDoc obj, String JavaDoc methodName,
3036                                 Serializable JavaDoc arg1)
3037    {
3038        // see Cache Note above
3039
if (getIssueId() == null)
3040        {
3041            ScarabCache.put(obj, this, methodName, arg1);
3042        }
3043        else
3044        {
3045            getMethodResult().put(obj, this, methodName, arg1);
3046        }
3047    }
3048
3049    /**
3050     * gets an object from the appropriate cache, based on whether this is
3051     * a saved issue. if you know the object should only be in ScarabCache
3052     * do not use this method.
3053     */

3054    private Object JavaDoc getCachedObject(String JavaDoc methodName,
3055                                   Serializable JavaDoc arg1, Serializable JavaDoc arg2)
3056    {
3057        Object JavaDoc obj = null;
3058        // Cache Note:
3059
// we check for issue id, so that we only (JCS) cache for saved issues
3060
// if we decide to cache results for new issues we should replace
3061
// this conditional with (this instanceof IssueSearch) because
3062
// we definitely do not want to cache those.
3063
if (getIssueId() == null)
3064        {
3065            obj = ScarabCache.get(this, methodName, arg1, arg2);
3066        }
3067        else
3068        {
3069            obj = getMethodResult().get(this, methodName, arg1, arg2);
3070        }
3071        return obj;
3072    }
3073
3074    /**
3075     * puts an object into the appropriate cache, based on whether this is
3076     * a saved issue. if you know the object should only be in ScarabCache
3077     * do not use this method.
3078     */

3079    private void putCachedObject(Object JavaDoc obj, String JavaDoc methodName,
3080                                 Serializable JavaDoc arg1, Serializable JavaDoc arg2)
3081    {
3082        // see Cache Note above
3083
if (getIssueId() == null)
3084        {
3085            ScarabCache.put(obj, this, methodName, arg1, arg2);
3086        }
3087        else
3088        {
3089            getMethodResult().put(obj, this, methodName, arg1, arg2);
3090        }
3091    }
3092
3093
3094    // *******************************************************************
3095
// Permissions methods - these are deprecated
3096
// *******************************************************************
3097

3098    /**
3099     * Checks if user has permission to enter issue.
3100     * @deprecated user.hasPermission(ScarabSecurity.ISSUE__ENTER, module)
3101     */

3102    public boolean hasEnterPermission(ScarabUser user, Module module)
3103        throws Exception JavaDoc
3104    {
3105        boolean hasPerm = false;
3106
3107        if (user.hasPermission(ScarabSecurity.ISSUE__ENTER, module))
3108        {
3109             hasPerm = true;
3110        }
3111        return hasPerm;
3112    }
3113
3114
3115    /**
3116     * Checks if user has permission to edit issue.
3117     * @deprecated user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
3118     */

3119    public boolean hasEditPermission(ScarabUser user, Module module)
3120        throws Exception JavaDoc
3121    {
3122        boolean hasPerm = false;
3123
3124        if (user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
3125            || user.equals(getCreatedBy()))
3126        {
3127            hasPerm = true;
3128        }
3129        return hasPerm;
3130    }
3131
3132    /**
3133     * Checks if user has permission to move issue to destination module.
3134     * @deprecated user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
3135     */

3136    public boolean hasMovePermission(ScarabUser user, Module module)
3137        throws Exception JavaDoc
3138    {
3139        boolean hasPerm = false;
3140
3141        if (user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
3142            || user.equals(getCreatedBy()))
3143        {
3144            hasPerm = true;
3145        }
3146        return hasPerm;
3147    }
3148
3149    /**
3150     * Assigns user to issue. Give description.
3151     */

3152    public ActivitySet assignUser(ActivitySet activitySet, String JavaDoc description,
3153                                  ScarabUser assignee, ScarabUser assigner,
3154                                  Attribute attribute, Attachment attachment)
3155        throws Exception JavaDoc
3156    {
3157        UserAttribute attVal = new UserAttribute();
3158
3159        // Save activitySet if it has not been already
3160
if (activitySet == null)
3161        {
3162            activitySet = ActivitySetManager
3163                .getInstance(ActivitySetTypePeer.EDIT_ISSUE__PK, assigner,
3164                             attachment);
3165            activitySet.save();
3166            attVal.startActivitySet(activitySet);
3167        }
3168
3169        if (description == null)
3170        {
3171            // Save activity record
3172
description = getAssignUserChangeString(assigner, assignee,
3173                                                            attribute);
3174        }
3175        ActivityManager
3176            .createUserActivity(this, attribute, activitySet,
3177                                description, null,
3178                                null, assignee.getUserId());
3179
3180        // Save user attribute values
3181
attVal.setIssue(this);
3182        attVal.setAttributeId(attribute.getAttributeId());
3183        attVal.setUserId(assignee.getUserId());
3184        attVal.setValue(assignee.getUserName());
3185        attVal.save();
3186
3187        return activitySet;
3188    }
3189
3190    /**
3191     * Assigns user to issue.
3192     */

3193    public ActivitySet assignUser(ActivitySet activitySet,
3194                                  ScarabUser assignee, ScarabUser assigner,
3195                                  Attribute attribute, Attachment attachment)
3196        throws Exception JavaDoc
3197    {
3198        return assignUser(activitySet, null,
3199                          assignee, assigner,
3200                          attribute, attachment);
3201    }
3202
3203    /**
3204     * Get the message that is emailed to associated users,
3205     * And that is saved in the activity description,
3206     * When a user is assigned.
3207     */

3208    private String JavaDoc getAssignUserChangeString(ScarabUser assigner,
3209                                            ScarabUser assignee,
3210                                            Attribute attr)
3211        throws Exception JavaDoc
3212    {
3213        String JavaDoc attrDisplayName = getModule()
3214              .getRModuleAttribute(attr, getIssueType()).getDisplayValue();
3215        Object JavaDoc[] args = {
3216            assigner.getUserName(),
3217            assignee.getUserName(),
3218            attrDisplayName
3219        };
3220        String JavaDoc actionString = Localization.format(
3221            ScarabConstants.DEFAULT_BUNDLE_NAME,
3222            getLocale(),
3223            "AssignIssueEmailAddedUserAction", args);
3224        return actionString;
3225    }
3226
3227
3228    /**
3229     * Used to change a user attribute value from one user attribute
3230     * to a new one.
3231     */

3232    public ActivitySet changeUserAttributeValue(ActivitySet activitySet,
3233                                                ScarabUser assignee,
3234                                                ScarabUser assigner,
3235                                                AttributeValue oldAttVal,
3236                                                Attribute newAttr,
3237                                                Attachment attachment)
3238        throws Exception JavaDoc
3239    {
3240        
3241        String JavaDoc actionString = null;
3242        // Save activitySet if it has not been already
3243
if (activitySet == null)
3244        {
3245            activitySet = ActivitySetManager
3246                .getInstance(ActivitySetTypePeer.EDIT_ISSUE__PK, assigner, attachment);
3247            activitySet.save();
3248            oldAttVal.startActivitySet(activitySet);
3249        }
3250
3251        // Save activity record for deletion of old assignment
3252
actionString = getUserDeleteString(assigner, assignee,
3253                                           oldAttVal.getAttribute());
3254        if (actionString == null)
3255        {
3256            Log.get().debug("User attribute '"+oldAttVal.getAttribute()
3257                    .getName()+"' removed from the artifact type");
3258        }
3259        ActivityManager
3260            .createUserActivity(this, oldAttVal.getAttribute(),
3261                                activitySet,
3262                                actionString, null,
3263                                assignee.getUserId(), null);
3264
3265
3266        // Save activity record for new assignment
3267
actionString = getAssignUserChangeString(assigner, assignee,
3268                                                 newAttr);
3269        ActivityManager
3270            .createUserActivity(this, newAttr, activitySet,
3271                                actionString, null,
3272                                null, assignee.getUserId());
3273
3274        // Save assignee value
3275
oldAttVal.setAttributeId(newAttr.getAttributeId());
3276        oldAttVal.save();
3277        
3278        return activitySet;
3279    }
3280
3281    /**
3282     * Get the message that is emailed to associated users,
3283     * And that is saved in the activity description,
3284     * When a user is changed from one user attribute to another.
3285     */

3286    private String JavaDoc getUserAttributeChangeString(ScarabUser assigner,
3287                                               ScarabUser assignee,
3288                                               Attribute oldAttr,
3289                                               Attribute newAttr)
3290        throws Exception JavaDoc
3291    {
3292        String JavaDoc oldAttrDisplayName = getModule()
3293             .getRModuleAttribute(oldAttr, getIssueType()).getDisplayValue();
3294        String JavaDoc newAttrDisplayName = getModule()
3295             .getRModuleAttribute(newAttr, getIssueType()).getDisplayValue();
3296        Object JavaDoc[] args = {
3297            assignee.getUserName(), assigner.getUserName(),
3298            oldAttrDisplayName, newAttrDisplayName
3299        };
3300        String JavaDoc actionString = Localization.format(
3301            ScarabConstants.DEFAULT_BUNDLE_NAME,
3302            getLocale(),
3303            "AssignIssueEmailChangedUserAttributeAction", args);
3304        return actionString;
3305    }
3306
3307
3308    /**
3309     * Used to delete a user attribute value.
3310     */

3311    public ActivitySet deleteUser(ActivitySet activitySet, ScarabUser assignee,
3312                                  ScarabUser assigner,
3313                                  AttributeValue attVal, Attachment attachment)
3314        throws Exception JavaDoc
3315    {
3316        Attribute attr = attVal.getAttribute();
3317        String JavaDoc actionString = null;
3318        // Save activitySet record if it has not been already
3319
if (activitySet == null)
3320        {
3321            activitySet = ActivitySetManager
3322                .getInstance(ActivitySetTypePeer.EDIT_ISSUE__PK, assigner, attachment);
3323            activitySet.save();
3324            attVal.startActivitySet(activitySet);
3325        }
3326
3327        // Save activity record
3328
actionString = getUserDeleteString(assigner, assignee, attr);
3329        if (actionString == null)
3330        {
3331            Log.get().debug("User attribute '"+attr.getName()+
3332                            "' removed from the artifact type." );
3333        }
3334
3335        ActivityManager
3336            .createUserActivity(this, attVal.getAttribute(),
3337                                activitySet,
3338                                actionString, null,
3339                                assignee.getUserId(), null);
3340
3341        // Save assignee value
3342
attVal.setDeleted(true);
3343        attVal.save();
3344
3345        return activitySet;
3346    }
3347
3348    /**
3349     * Get the message that is emailed to associated users,
3350     * And that is saved in the activity description,
3351     * When a user is removed from a user attribute.
3352     */

3353    private String JavaDoc getUserDeleteString(ScarabUser assigner,
3354                                      ScarabUser assignee,
3355                                      Attribute attr)
3356        throws Exception JavaDoc
3357    {
3358        String JavaDoc actionString = null;
3359        RModuleAttribute rma = getModule()
3360            .getRModuleAttribute(attr, getIssueType());
3361        if (rma != null)
3362        {
3363            String JavaDoc attrDisplayName = rma.getDisplayValue();
3364            Object JavaDoc[] args = {
3365                assigner.getUserName(), assignee.getUserName(),
3366                attrDisplayName
3367            };
3368            actionString = Localization.format(
3369                ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
3370                "AssignIssueEmailRemovedUserAction", args);
3371        }
3372        return actionString;
3373    }
3374
3375    /**
3376     * Deletes a specific dependency on this issue.
3377     */

3378    public ActivitySet doDeleteDependency(ActivitySet activitySet,
3379                                          Depend oldDepend, ScarabUser user)
3380        throws Exception JavaDoc
3381    {
3382        Issue otherIssue = IssueManager
3383                        .getInstance(oldDepend.getObserverId(), false);
3384/* Why can a child not delete a dependency??
3385        if (otherIssue.equals(this))
3386        {
3387            throw new ScarabException("CannotDeleteDependency");
3388        }
3389*/

3390        Issue thisIssue = IssueManager
3391                        .getInstance(oldDepend.getObservedId(), false);
3392
3393        Object JavaDoc[] args = {
3394            oldDepend.getDependType().getName(),
3395            thisIssue.getUniqueId(),
3396            otherIssue.getUniqueId()
3397        };
3398        String JavaDoc desc = Localization.format(
3399            ScarabConstants.DEFAULT_BUNDLE_NAME,
3400            getLocale(),
3401            "DependencyDeletedDesc", args);
3402
3403        // get the original object so that we do an update
3404
oldDepend = thisIssue.getDependency(otherIssue);
3405        oldDepend.setNew(false);
3406        oldDepend.setDeleted(true);
3407        oldDepend.save();
3408
3409        // need to null out the cache entry so that Issue.getDependency()
3410
// does not try to return the item from the cache
3411
ScarabCache.put(null, thisIssue, GET_DEPENDENCY, otherIssue);
3412
3413        if (activitySet == null)
3414        {
3415            // deal with user comments
3416
Attachment comment = oldDepend.getDescriptionAsAttachment(user, thisIssue);
3417
3418            activitySet = getActivitySet(user, comment,
3419                              ActivitySetTypePeer.EDIT_ISSUE__PK);
3420            // Save activitySet record
3421
activitySet.save();
3422        }
3423
3424        ActivityManager
3425            .createDeleteDependencyActivity(thisIssue, activitySet, oldDepend,
3426                                desc);
3427        ActivityManager
3428            .createDeleteDependencyActivity(otherIssue, activitySet, oldDepend,
3429                                desc);
3430
3431
3432        return activitySet;
3433    }
3434
3435    /**
3436     * Given a specific attachment object allow us to update
3437     * the information in it. If the old matches the new, then
3438     * nothing is modified.
3439     */

3440    public ActivitySet doChangeUrlDescription(ActivitySet activitySet,
3441                                              ScarabUser user,
3442                                              Attachment attachment,
3443                                              String JavaDoc oldDescription)
3444        throws Exception JavaDoc
3445    {
3446        String JavaDoc newDescription = attachment.getName();
3447        if (!oldDescription.equals(newDescription))
3448        {
3449            Object JavaDoc[] args = {
3450                oldDescription,
3451                newDescription,
3452            };
3453            String JavaDoc desc = Localization.format(
3454                ScarabConstants.DEFAULT_BUNDLE_NAME,
3455                getLocale(),
3456                "UrlDescChangedDesc", args);
3457
3458            if (desc.length() > 248)
3459            {
3460                desc = desc.substring(0,248) + "...";
3461            }
3462            if (activitySet == null)
3463            {
3464                // Save activitySet record
3465
activitySet = getActivitySet(user, ActivitySetTypePeer.EDIT_ISSUE__PK);
3466                activitySet.save();
3467            }
3468            // Save activity record
3469
ActivityManager
3470                .createTextActivity(this, activitySet,
3471                                    desc, attachment,
3472                                    oldDescription, newDescription);
3473            try
3474            {
3475                activitySet.sendEmail(this);
3476            }
3477            catch (Exception JavaDoc e)
3478            {
3479                Localizable urlDescSaved = new L10NMessage(L10NKeySet.UrlDescChangedDesc);
3480                Localizable emailError = new L10NMessage(L10NKeySet.CouldNotSendEmail,e);
3481                throw new ScarabException(L10NKeySet.SavedButErrors,
3482                    urlDescSaved,
3483                    emailError);
3484            }
3485        }
3486        return activitySet;
3487    }
3488
3489    /**
3490     * Given a specific attachment object allow us to update
3491     * the information in it. If the old matches the new, then
3492     * nothing is modified.
3493     */

3494    public ActivitySet doChangeUrlUrl(ActivitySet activitySet, ScarabUser user,
3495                                              Attachment attachment, String JavaDoc oldUrl)
3496        throws Exception JavaDoc
3497    {
3498        String JavaDoc newUrl = attachment.getData();
3499        if (!oldUrl.equals(newUrl))
3500        {
3501            Object JavaDoc[] args = {
3502                oldUrl, newUrl
3503            };
3504            String JavaDoc desc = Localization.format(
3505                ScarabConstants.DEFAULT_BUNDLE_NAME,
3506                getLocale(),
3507                "UrlChangedDesc", args);
3508
3509            if (desc.length() > 248)
3510            {
3511                desc = desc.substring(0,248) + "...";
3512            }
3513            if (activitySet == null)
3514            {
3515                // Save activitySet record
3516
activitySet = getActivitySet(user, ActivitySetTypePeer.EDIT_ISSUE__PK);
3517                activitySet.save();
3518            }
3519            // Save activity record
3520
ActivityManager
3521                .createTextActivity(this, activitySet,
3522                                    desc, attachment,
3523                                    oldUrl, newUrl);
3524            try
3525            {
3526                activitySet.sendEmail(this);
3527            }
3528            catch (Exception JavaDoc e)
3529            {
3530                Localizable urlChanged = new L10NMessage(L10NKeySet.UrlChangedDesc, oldUrl, newUrl);
3531                Localizable emailError = new L10NMessage(L10NKeySet.CouldNotSendEmail, e);
3532                throw new ScarabException(L10NKeySet.SavedButErrors,
3533                    urlChanged,
3534                    emailError);
3535            }
3536        }
3537        return activitySet;
3538    }
3539
3540    /**
3541     * changes the dependency type as well as. will not change deptype
3542     * for deleted deps
3543     */

3544    public ActivitySet doChangeDependencyType(ActivitySet activitySet,
3545                                              Depend oldDepend,
3546                                              Depend newDepend,
3547                                              ScarabUser user)
3548        throws Exception JavaDoc
3549    {
3550        String JavaDoc oldName = oldDepend.getDependType().getName();
3551        String JavaDoc newName = newDepend.getDependType().getName();
3552        // check to see if something changed
3553
// only change dependency type for non-deleted deps
3554
if (!newName.equals(oldName) && !newDepend.getDeleted())
3555        {
3556            Issue otherIssue = IssueManager
3557                            .getInstance(newDepend.getObserverId(), false);
3558
3559            // always delete an old dependency
3560
oldDepend.setDeleted(true);
3561            oldDepend.save();
3562            // always create a new dependency
3563
newDepend.setNew(true);
3564            newDepend.save();
3565
3566            Object JavaDoc[] args = {
3567                this.getUniqueId(),
3568                otherIssue.getUniqueId(),
3569                oldName,
3570                newName
3571            };
3572            String JavaDoc desc = Localization.format(
3573                ScarabConstants.DEFAULT_BUNDLE_NAME,
3574                getLocale(),
3575                "DependencyTypeChangedDesc", args);
3576
3577            // need to null out the cache entry so that Issue.getDependency()
3578
// does not try to return the item from the cache
3579
ScarabCache.put(null, this, GET_DEPENDENCY, otherIssue);
3580
3581            if (activitySet == null)
3582            {
3583                // deal with user comments
3584
Attachment comment = newDepend.getDescriptionAsAttachment(user, this);
3585    
3586                activitySet = getActivitySet(user, comment,
3587                                  ActivitySetTypePeer.EDIT_ISSUE__PK);
3588                // Save activitySet record
3589
activitySet.save();
3590            }
3591            
3592            ActivityManager
3593                .createChangeDependencyActivity(this, activitySet, newDepend,
3594                                    desc, oldName, newName);
3595            ActivityManager
3596                .createChangeDependencyActivity(otherIssue, activitySet, newDepend,
3597                                    desc, oldName, newName);
3598        }
3599        return activitySet;
3600    }
3601
3602    /**
3603     * Sets original AttributeValues for an new issue based on a hashmap of values
3604     * This is data is saved to the database and the proper ActivitySet is
3605     * also recorded.
3606     *
3607     * @throws Exception when the workflow has an error to report
3608     */

3609    public ActivitySet setInitialAttributeValues(ActivitySet activitySet,
3610            Attachment attachment, HashMap JavaDoc newValues, ScarabUser user)
3611        throws Exception JavaDoc
3612    {
3613        // Check new values for workflow
3614
String JavaDoc msg = doCheckInitialAttributeValueWorkflow(newValues, user);
3615        if (msg != null)
3616        {
3617            throw new Exception JavaDoc(msg); //EXCEPTION
3618
}
3619        
3620        if (activitySet == null)
3621        {
3622            // Save activitySet record
3623
activitySet = ActivitySetManager
3624                .getInstance(ActivitySetTypePeer.CREATE_ISSUE__PK, user);
3625            activitySet.save();
3626        }
3627        setActivitySet(activitySet);
3628
3629        // enter the values into the activitySet
3630
LinkedMap avMap = getModuleAttributeValuesMap();
3631        MapIterator iter = avMap.mapIterator();
3632        while (iter.hasNext())
3633        {
3634            AttributeValue aval = (AttributeValue)avMap.get(iter.next());
3635            try
3636            {
3637                aval.startActivitySet(activitySet);
3638            }
3639            catch (ScarabException se)
3640            {
3641                throw new Exception JavaDoc("Fatal Error: " +
3642                    se.getMessage() + " Please start over."); //EXCEPTION
3643
}
3644        }
3645        this.save();
3646
3647        // create initial issue creation activity
3648
ActivityManager.createReportIssueActivity(this, activitySet,
3649                Localization.getString(
3650                    ScarabConstants.DEFAULT_BUNDLE_NAME,
3651                    getLocale(),
3652                    "IssueCreated"));
3653
3654        // this needs to be done after the issue is created.
3655
// check to make sure the attachment has data before submitting it.
3656
String JavaDoc attachmentData = attachment.getData();
3657        if (attachmentData != null &&
3658            attachmentData.length() > 0)
3659        {
3660            attachment = AttachmentManager.getReason(attachment, this, user);
3661            activitySet.setAttachment(attachment);
3662        }
3663        activitySet.save();
3664        
3665        // need to clear the cache since this is after the
3666
// issue is saved. for some reason, things don't
3667
// show up properly right away.
3668
ScarabCache.clear();
3669        return activitySet;
3670    }
3671
3672    /**
3673     * Sets AttributeValues for an issue based on a hashmap of attribute values
3674     * This is data is saved to the database and the proper ActivitySet is
3675     * also recorded.
3676     * @param activitySet ActivitySet instance
3677     * @param newAttVals A map of attribute Id's vs new AttributeValues
3678     * @param attachment Attachment to the issue
3679     * @param user User responsible for this activity
3680     * @return ActivitySet object containing the changes made to the issue
3681     * @throws Exception when the workflow has an error to report
3682     */

3683    public ActivitySet setAttributeValues(ActivitySet activitySet,
3684                                          HashMap JavaDoc newAttVals,
3685                                          Attachment attachment,
3686                                          ScarabUser user)
3687        throws Exception JavaDoc
3688    {
3689        if (!isTemplate())
3690        {
3691            String JavaDoc msg = doCheckAttributeValueWorkflow(newAttVals, user);
3692            if (msg != null)
3693            {
3694                throw new Exception JavaDoc(msg); //EXCEPTION
3695
}
3696        }
3697        // save the attachment if it exists.
3698
if (attachment != null)
3699        {
3700            attachment.setTextFields(user, this,
3701                                     Attachment.MODIFICATION__PK);
3702            attachment.save();
3703        }
3704
3705        // Create the ActivitySet
3706
if (activitySet == null)
3707        {
3708            activitySet = getActivitySet(user, attachment,
3709                                      ActivitySetTypePeer.EDIT_ISSUE__PK);
3710            activitySet.save();
3711            ScarabCache.clear();
3712        }
3713
3714        LinkedMap avMap = getModuleAttributeValuesMap();
3715        AttributeValue oldAttVal = null;
3716        AttributeValue newAttVal = null;
3717        Iterator JavaDoc iter = newAttVals.keySet().iterator();
3718        boolean attValDeleted = false;
3719        while (iter.hasNext())
3720        {
3721            Integer JavaDoc attrId = (Integer JavaDoc)iter.next();
3722            Attribute attr = AttributeManager.getInstance(attrId);
3723            oldAttVal = (AttributeValue)avMap.get(attr.getName().toUpperCase());
3724            newAttVal = (AttributeValue)newAttVals.get(attrId);
3725            String JavaDoc newAttValValue = newAttVal.getValue();
3726            if (Log.get().isDebugEnabled())
3727            {
3728                Log.get().debug("Attribute: " + attr.getName() +
3729                                " has newAttValValue = " + newAttValValue);
3730            }
3731            if (newAttValValue != null && newAttValValue.length() > 0)
3732            {
3733                oldAttVal.setProperties(newAttVal);
3734            }
3735            else
3736            {
3737                oldAttVal.setDeleted(true);
3738                Log.get().debug("setDeleted(true)");
3739                attValDeleted = true;
3740            }
3741            oldAttVal.startActivitySet(activitySet);
3742            oldAttVal.save();
3743        }
3744        if (attValDeleted)
3745        {
3746             //Remove attribute value map from cache
3747
getMethodResult().remove(this, GET_MODULE_ATTRVALUES_MAP,
3748                                          Boolean.TRUE);
3749        }
3750        return activitySet;
3751    }
3752
3753    /**
3754     * This method is used with the setInitialAttributeValues() method to
3755     * Make sure that workflow is valid for the initial values of a new issue.
3756     * It will return a non-null String
3757     * which is the workflow error message otherwise it will return null.
3758     */

3759    public String JavaDoc doCheckInitialAttributeValueWorkflow(HashMap JavaDoc newValues,
3760                                                       ScarabUser user)
3761        throws Exception JavaDoc
3762    {
3763        String JavaDoc msg = null;
3764        Iterator JavaDoc iter = newValues.keySet().iterator();
3765        while (iter.hasNext())
3766        {
3767            Integer JavaDoc attrId = (Integer JavaDoc)iter.next();
3768            Attribute attr = AttributeManager.getInstance(attrId);
3769            if (attr.isOptionAttribute())
3770            {
3771                AttributeOption toOption = AttributeOptionManager
3772                     .getInstance(new Integer JavaDoc((String JavaDoc)newValues.get(attrId)));
3773                msg = WorkflowFactory.getInstance().checkInitialTransition(
3774                                                    toOption, this,
3775                                                    newValues, user);
3776            }
3777            if (msg != null)
3778            {
3779                break;
3780            }
3781        }
3782        return msg;
3783    }
3784
3785    /**
3786     * This method is used with the setAttributeValues() method to
3787     * Make sure that workflow is valid. It will return a non-null String
3788     * which is the workflow error message otherwise it will return null.
3789     */

3790    public String JavaDoc doCheckAttributeValueWorkflow(HashMap JavaDoc newAttVals,
3791                                                ScarabUser user)
3792        throws Exception JavaDoc
3793    {
3794        LinkedMap avMap = getModuleAttributeValuesMap();
3795        AttributeValue oldAttVal = null;
3796        AttributeValue newAttVal = null;
3797        String JavaDoc msg = null;
3798        Iterator JavaDoc iter = newAttVals.keySet().iterator();
3799        while (iter.hasNext())
3800        {
3801            Integer JavaDoc attrId = (Integer JavaDoc)iter.next();
3802            Attribute attr = AttributeManager.getInstance(attrId);
3803            oldAttVal = (AttributeValue)avMap.get(attr.getName().toUpperCase());
3804            newAttVal = (AttributeValue)newAttVals.get(attrId);
3805            AttributeOption fromOption = null;
3806            AttributeOption toOption = null;
3807
3808            if (newAttVal.getValue() != null)
3809            {
3810                if (newAttVal.getAttribute().isOptionAttribute())
3811                {
3812                    if (oldAttVal.getOptionId() == null)
3813                    {
3814                        fromOption = AttributeOptionManager.getInstance(ScarabConstants.INTEGER_0);
3815                    }
3816                    else
3817                    {
3818                        fromOption = oldAttVal.getAttributeOption();
3819                    }
3820                    toOption = newAttVal.getAttributeOption();
3821                    msg = WorkflowFactory.getInstance().checkTransition(
3822                                                        fromOption,
3823                                                        toOption, this,
3824                                                        newAttVals, user);
3825                }
3826                if (msg != null)
3827                {
3828                    break;
3829                }
3830            }
3831        }
3832        return msg;
3833    }
3834    
3835    /**
3836     * This method is used with the setAttributeValues() method to
3837     * Make sure that workflow is valid. It will return a non-null String
3838     * which is the workflow error message otherwise it will return null.
3839     *
3840     * @deprecated The attachment doesn't need to be passed into this method.
3841     */

3842    public String JavaDoc doCheckAttributeValueWorkflow(HashMap JavaDoc newAttVals,
3843                                                Attachment attachment,
3844                                                ScarabUser user)
3845        throws Exception JavaDoc
3846    {
3847        return doCheckAttributeValueWorkflow(newAttVals, user);
3848    }
3849
3850
3851
3852    /**
3853     * If the comment hasn't changed, it will return a valid ActivitySet
3854     * otherwise it returns null.
3855     */

3856    public ActivitySet doEditComment(ActivitySet activitySet, String JavaDoc newComment,
3857                                     Attachment attachment, ScarabUser user)
3858        throws Exception JavaDoc
3859    {
3860        String JavaDoc oldComment = attachment.getData();
3861        if (!newComment.equals(oldComment))
3862        {
3863            attachment.setData(newComment);
3864            attachment.save();
3865           
3866            // Generate description of modification
3867
Object JavaDoc[] args = {
3868                oldComment,
3869                newComment
3870            };
3871            String JavaDoc desc = Localization.format(
3872                ScarabConstants.DEFAULT_BUNDLE_NAME,
3873                getLocale(),
3874                "ChangedComment", args);
3875
3876            if (activitySet == null)
3877            {
3878                 // Save activitySet record
3879
activitySet = getActivitySet(user,
3880                                          ActivitySetTypePeer.EDIT_ISSUE__PK);
3881                activitySet.save();
3882            }
3883            // Save activity record
3884
ActivityManager
3885                .createTextActivity(this, null, activitySet,
3886                                    desc, attachment,
3887                                    oldComment, newComment);
3888             
3889            try
3890            {
3891                activitySet.sendEmail(this);
3892            }
3893            catch (Exception JavaDoc e)
3894            {
3895                Localizable commentSaved = new L10NMessage( L10NKeySet.CommentSaved,oldComment,newComment);
3896                Localizable emailError = new L10NMessage( L10NKeySet.CouldNotSendEmail,e);
3897                throw new ScarabException(L10NKeySet.SavedButErrors,
3898                    commentSaved,
3899                    emailError);
3900            }
3901        }
3902        return activitySet;
3903    }
3904
3905    /**
3906     * If the URL hasn't changed, it will return a valid ActivitySet
3907     * otherwise it returns null.
3908     */

3909    public ActivitySet doDeleteUrl(ActivitySet activitySet,
3910                                     Attachment attachment, ScarabUser user)
3911        throws Exception JavaDoc
3912    {
3913        String JavaDoc oldUrl = attachment.getData();
3914        attachment.setDeleted(true);
3915        attachment.save();
3916
3917        // Generate description of modification
3918
String JavaDoc name = attachment.getName();
3919        String JavaDoc desc = Localization.format(
3920            ScarabConstants.DEFAULT_BUNDLE_NAME,
3921            getLocale(),
3922            "UrlDeletedDesc", name);
3923
3924        if (activitySet == null)
3925        {
3926             // Save activitySet record
3927
activitySet = getActivitySet(user,
3928                                      ActivitySetTypePeer.EDIT_ISSUE__PK);
3929            activitySet.save();
3930        }
3931        // Save activity record
3932
ActivityManager
3933            .createTextActivity(this, null, activitySet,
3934                                desc, attachment, oldUrl, null);
3935        return activitySet;
3936    }
3937
3938    /**
3939     * Remove the attachment.
3940     * On return the MutableBoolean physicallyDeleted is set to true,
3941     * if the attachment file also was removed by this operation.
3942     * If the attached File still exists for any reason, physicallyDeleted
3943     * will be set to false.
3944     * Note: You can enable/disable physical deletion by setting the
3945     * environment property scarab.attachment.remove.permanent
3946     * to true/false (false is the default setting).
3947     */

3948    public ActivitySet doRemoveAttachment(ActivitySet activitySet,
3949                                          MutableBoolean physicallyDeleted,
3950                                          Attachment attachment,
3951                                          ScarabUser user)
3952        throws Exception JavaDoc
3953    {
3954        boolean attachmentPhysicallyDeleted = false;
3955        boolean physicalDeletionAllowed = Turbine.getConfiguration()
3956        .getBoolean("scarab.attachment.remove.permanent",false);
3957
3958        if(physicalDeletionAllowed)
3959        {
3960            attachmentPhysicallyDeleted = attachment.deletePhysicalAttachment();
3961            physicallyDeleted.set(attachmentPhysicallyDeleted);
3962        }
3963
3964        attachment.setDeleted(true);
3965        attachment.save();
3966
3967        // Generate description of modification
3968
String JavaDoc name = new String JavaDoc();
3969        Localizable l10nMessage;
3970        if (!physicalDeletionAllowed)
3971        {
3972            name = attachment.getFileName();
3973            l10nMessage = new L10NMessage(L10NKeySet.AttachmentDeletedDesc, name);
3974        }
3975        else if (attachmentPhysicallyDeleted)
3976        {
3977            name = attachment.getFileName();
3978            l10nMessage = new L10NMessage(L10NKeySet.FileDeletedDesc, name );
3979        }
3980        else
3981        {
3982            name = attachment.getFullPath();
3983            l10nMessage = new L10NMessage(L10NKeySet.FileNotDeletedDesc, name);
3984        }
3985
3986        if (activitySet == null)
3987        {
3988             // Save activitySet record
3989
activitySet = getActivitySet(user,
3990                              ActivitySetTypePeer.EDIT_ISSUE__PK);
3991            activitySet.save();
3992        }
3993
3994        // Save activity record
3995
// [TODO] here we currently write resolved language Strings
3996
// into the database. I think, this should eventually
3997
// be changed to a language independent solution. HD
3998
String JavaDoc desc = l10nMessage.getMessage();
3999        ActivityManager
4000            .createTextActivity(this, null, activitySet,
4001                                desc, attachment, name, null);
4002
4003        return activitySet;
4004    }
4005
4006    
4007    /**
4008     * Returns users assigned to all user attributes.
4009     */

4010    public HashSet JavaDoc getAssociatedUsers() throws Exception JavaDoc
4011    {
4012        HashSet JavaDoc users = null;
4013        Object JavaDoc obj = ScarabCache.get(this, GET_ASSOCIATED_USERS);
4014        if (obj == null)
4015        {
4016            List JavaDoc attributeList = getModule()
4017                .getUserAttributes(getIssueType(), true);
4018            List JavaDoc attributeIdList = new ArrayList JavaDoc();
4019            
4020            for (int i=0; i<attributeList.size(); i++)
4021            {
4022                Attribute att = (Attribute) attributeList.get(i);
4023                RModuleAttribute modAttr = getModule().
4024                    getRModuleAttribute(att, getIssueType());
4025                if (modAttr.getActive())
4026                {
4027                    attributeIdList.add(att.getAttributeId());
4028                }
4029            }
4030            
4031            if (!attributeIdList.isEmpty())
4032            {
4033                users = new HashSet JavaDoc();
4034                Criteria crit = new Criteria()
4035                    .addIn(AttributeValuePeer.ATTRIBUTE_ID, attributeIdList)
4036                    .add(AttributeValuePeer.DELETED, false);
4037                crit.setDistinct();
4038                
4039                List JavaDoc attValues = getAttributeValues(crit);
4040                for (int i=0; i<attValues.size(); i++)
4041                {
4042                    List JavaDoc item = new ArrayList JavaDoc(2);
4043                    AttributeValue attVal = (AttributeValue) attValues.get(i);
4044                    ScarabUser su = ScarabUserManager.getInstance(attVal.getUserId());
4045                    Attribute attr = AttributeManager.getInstance(attVal.getAttributeId());
4046                    item.add(attr);
4047                    item.add(su);
4048                    users.add(item);
4049                }
4050            }
4051            ScarabCache.put(users, this, GET_ASSOCIATED_USERS);
4052        }
4053        else
4054        {
4055            users = (HashSet JavaDoc)obj;
4056        }
4057        return users;
4058    }
4059
4060
4061    public String JavaDoc toString()
4062    {
4063        String JavaDoc id = null;
4064        try
4065        {
4066            id = isNew() ? "New issue" : getUniqueId();
4067        }
4068        catch (Exception JavaDoc e)
4069        {
4070            id = "Error in getting unique id";
4071            Log.get().warn(id, e);
4072        }
4073        
4074        return super.toString() + '{' + id + '}';
4075    }
4076    
4077    /**
4078     * Returns if the issue's BlockingCondition is fulfilled.
4079     *
4080     * @return
4081     */

4082    public boolean isBlockingConditionTrue() throws Exception JavaDoc
4083    {
4084       boolean isBlockingConditionTrue = false;
4085       List JavaDoc blockingConditions = this.getRModuleIssueType().getConditions();
4086       for (Iterator JavaDoc it = blockingConditions.iterator(); !isBlockingConditionTrue && it.hasNext(); )
4087       {
4088           Condition cond = (Condition)it.next();
4089           Integer JavaDoc conditionOptionId = cond.getOptionId();
4090           Attribute attr = cond.getAttributeOption().getAttribute();
4091           AttributeValue attrVal = this.getAttributeValue(attr);
4092           if (attrVal != null)
4093           {
4094               Integer JavaDoc issueOptionId = attrVal.getOptionId();
4095               if (issueOptionId != null && issueOptionId.equals(conditionOptionId))
4096               {
4097                   isBlockingConditionTrue = true;
4098               }
4099           }
4100       }
4101       return isBlockingConditionTrue;
4102    }
4103    
4104    /**
4105     * Returns if this issue is currently blocking any other.
4106     * @return
4107     * @throws Exception
4108     */

4109    public boolean isBlockingAnyIssue() throws Exception JavaDoc
4110    {
4111       return this.getBlockedIssues().size() > 0;
4112    }
4113    
4114    /**
4115     * An issue is blocked when it depends, via a is_blocked_by dependency,
4116     * of an issue that is currently "blocking". Whenever an issue is blocked, some transitions
4117     * might not be availaible.
4118     * @return
4119     */

4120    public boolean isBlocked() throws Exception JavaDoc
4121    {
4122        return (getBlockingIssues().size()>0);
4123    }
4124    
4125    /**
4126     * Returns a list of issues that actually "block" this issue, i.e., that
4127     * are related via a "is blocked by" dependency, and are "blocking".
4128     * @return
4129     */

4130    public List JavaDoc getBlockingIssues() throws Exception JavaDoc
4131    {
4132        List JavaDoc blockingIssues = new ArrayList JavaDoc();
4133        List JavaDoc blockingDependantIssues = this.getBlockingDependantIssues();
4134        for (Iterator JavaDoc it = blockingDependantIssues.iterator(); it.hasNext(); )
4135        {
4136            Issue is = (Issue)it.next();
4137            if (is.isBlockingConditionTrue())
4138                blockingIssues.add(is);
4139        }
4140        return blockingIssues;
4141    }
4142    
4143    /**
4144     * Returns a list of issues that are blockable by this issue, via a "is_blocked_by"
4145     * relationship.
4146     * @return
4147     * @throws Exception
4148     */

4149    public List JavaDoc getBlockingDependantIssues() throws Exception JavaDoc
4150    {
4151        List JavaDoc blockingIssues = new ArrayList JavaDoc();
4152        List JavaDoc parentIssues = this.getParents();
4153        for (Iterator JavaDoc it = parentIssues.iterator(); it.hasNext(); )
4154        {
4155            Depend depend = (Depend)it.next();
4156            if (depend.getDependType().getDependTypeId().equals(DependTypePeer.BLOCKING__PK))
4157            {
4158                blockingIssues.add(IssuePeer.retrieveByPK(depend.getObservedId()));
4159            }
4160        }
4161        return blockingIssues;
4162    }
4163    
4164    /**
4165     * Returns a list of issues currently BLOCKED by this issue
4166     *
4167     * @return
4168     * @throws Exception
4169     */

4170    public List JavaDoc getBlockedIssues() throws Exception JavaDoc
4171    {
4172        if (this.isBlockingConditionTrue())
4173            return this.getBlockedDependantIssues();
4174        else
4175            return new ArrayList JavaDoc();
4176    }
4177    
4178    /**
4179     * Returns a list of issues that might be blocked by this issue because if its
4180     * "is_blocked_by" dependency.
4181     *
4182     * @return
4183     * @throws Exception
4184     */

4185    public List JavaDoc getBlockedDependantIssues() throws Exception JavaDoc
4186    {
4187        List JavaDoc blockableIssues = new ArrayList JavaDoc();
4188        List JavaDoc childIssues = this.getChildren();
4189        for (Iterator JavaDoc it = childIssues.iterator(); it.hasNext(); )
4190        {
4191            Depend depend = (Depend)it.next();
4192            if (depend.getDependType().getDependTypeId().equals(DependTypePeer.BLOCKING__PK))
4193            {
4194                blockableIssues.add(IssuePeer.retrieveByPK(depend.getObserverId()));
4195            }
4196        }
4197        return blockableIssues;
4198    }
4199}
4200
Popular Tags