Source Code Cross Referenced for Issue.java in  » Issue-Tracking » scarab-0.21 » org » tigris » scarab » om » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Issue Tracking » scarab 0.21 » org.tigris.scarab.om 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package org.tigris.scarab.om;
0002:
0003:        /* ================================================================
0004:         * Copyright (c) 2000-2005 CollabNet.  All rights reserved.
0005:         * 
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions are
0008:         * met:
0009:         * 
0010:         * 1. Redistributions of source code must retain the above copyright
0011:         * notice, this list of conditions and the following disclaimer.
0012:         * 
0013:         * 2. Redistributions in binary form must reproduce the above copyright
0014:         * notice, this list of conditions and the following disclaimer in the
0015:         * documentation and/or other materials provided with the distribution.
0016:         * 
0017:         * 3. The end-user documentation included with the redistribution, if
0018:         * any, must include the following acknowlegement: "This product includes
0019:         * software developed by Collab.Net <http://www.Collab.Net/>."
0020:         * Alternately, this acknowlegement may appear in the software itself, if
0021:         * and wherever such third-party acknowlegements normally appear.
0022:         * 
0023:         * 4. The hosted project names must not be used to endorse or promote
0024:         * products derived from this software without prior written
0025:         * permission. For written permission, please contact info@collab.net.
0026:         * 
0027:         * 5. Products derived from this software may not use the "Tigris" or 
0028:         * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without 
0029:         * prior written permission of Collab.Net.
0030:         * 
0031:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0032:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0033:         * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0034:         * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
0035:         * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0036:         * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
0037:         * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0038:         * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
0039:         * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
0040:         * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
0041:         * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0042:         *
0043:         * ====================================================================
0044:         * 
0045:         * This software consists of voluntary contributions made by many
0046:         * individuals on behalf of Collab.Net.
0047:         */
0048:
0049:        // JDK classes
0050:        import com.workingdogs.village.DataSetException;
0051:        import java.io.FileNotFoundException;
0052:        import java.io.IOException;
0053:        import java.io.Serializable;
0054:        import java.sql.Connection;
0055:        import java.util.ArrayList;
0056:        import java.util.Arrays;
0057:        import java.util.Date;
0058:        import java.util.HashMap;
0059:        import java.util.HashSet;
0060:        import java.util.Iterator;
0061:        import java.util.List;
0062:        import java.util.Locale;
0063:        import java.util.Map;
0064:        import java.util.Set;
0065:
0066:        import org.apache.commons.collections.MapIterator;
0067:        import org.apache.commons.collections.map.LinkedMap;
0068:        import org.apache.commons.lang.ObjectUtils;
0069:        import org.apache.commons.lang.StringUtils;
0070:        import org.apache.fulcrum.localization.Localization;
0071:        import org.apache.torque.TorqueException;
0072:        import org.apache.torque.manager.MethodResultCache;
0073:        import org.apache.torque.map.DatabaseMap;
0074:        import org.apache.torque.oid.IDBroker;
0075:        import org.apache.torque.om.Persistent;
0076:        import org.apache.torque.util.BasePeer;
0077:        import org.apache.torque.util.Criteria;
0078:        import org.apache.turbine.Turbine;
0079:        import org.tigris.scarab.attribute.OptionAttribute;
0080:        import org.tigris.scarab.attribute.TotalVotesAttribute;
0081:        import org.tigris.scarab.attribute.UserAttribute;
0082:        import org.tigris.scarab.notification.ActivityType;
0083:        import org.tigris.scarab.notification.NotificationManagerFactory;
0084:        import org.tigris.scarab.services.cache.ScarabCache;
0085:        import org.tigris.scarab.services.security.ScarabSecurity;
0086:        import org.tigris.scarab.tools.ScarabGlobalTool;
0087:        import org.tigris.scarab.tools.localization.L10NKeySet;
0088:        import org.tigris.scarab.tools.localization.L10NMessage;
0089:        import org.tigris.scarab.util.Log;
0090:        import org.tigris.scarab.util.MutableBoolean;
0091:        import org.tigris.scarab.util.ScarabConstants;
0092:        import org.tigris.scarab.util.ScarabException;
0093:        import org.tigris.scarab.util.ScarabRuntimeException;
0094:        import org.tigris.scarab.workflow.WorkflowFactory;
0095:
0096:        import com.workingdogs.village.Record;
0097:
0098:        /** 
0099:         * This class represents an Issue.
0100:         *
0101:         * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
0102:         * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
0103:         * @author <a href="mailto:elicia@collab.net">Elicia David</a>
0104:         * @version $Id: Issue.java 10331 2006-11-07 10:17:38Z ronvoe122 $
0105:         */
0106:        public class Issue extends BaseIssue implements  Persistent {
0107:            // the following Strings are method names that are used in caching results
0108:            protected static final String GET_ATTRIBUTE_VALUES_MAP = "getAttributeValuesMap";
0109:            protected static final String GET_ASSOCIATED_USERS = "getAssociatedUsers";
0110:            protected static final String GET_MODULE_ATTRVALUES_MAP = "getModuleAttributeValuesMap";
0111:            protected static final String GET_ATTRVALUE = "getAttributeValue";
0112:            protected static final String GET_ATTRVALUES = "getAttributeValues";
0113:            protected static final String GET_ALL_USERS_TO_EMAIL = "getAllUsersToEmail";
0114:            protected static final String GET_USER_ATTRIBUTEVALUE = "getUserAttributeValue";
0115:            protected static final String GET_USER_ATTRIBUTEVALUES = "getUserAttributeValues";
0116:            protected static final String GET_CREATED_DATE = "getCreatedDate";
0117:            protected static final String GET_CREATED_BY = "getCreatedBy";
0118:            protected static final String GET_LAST_TRANSACTION = "getLastActivitySet";
0119:            protected static final String GET_MODIFIED_BY = "getModifiedBy";
0120:            protected static final String GET_MODIFIED_DATE = "getModifiedDate";
0121:            protected static final String GET_COMMENTS = "getComments";
0122:            protected static final String GET_URLS = "getUrls";
0123:            protected static final String GET_EXISTING_ATTACHMENTS = "getExistingAttachments";
0124:            protected static final String GET_ACTIVITY = "getActivity";
0125:            protected static final String GET_TRANSACTIONS = "getActivitySets";
0126:            protected static final String GET_CHILDREN = "getChildren";
0127:            protected static final String GET_PARENTS = "getParents";
0128:            protected static final String GET_ALL_DEPENDENCY_TYPES = "getAllDependencyTypes";
0129:            protected static final String GET_DEPENDENCY = "getDependency";
0130:            protected static final String GET_TEMPLATE_TYPES = "getTemplateTypes";
0131:            protected static final String GET_TEMPLATEINFO = "getTemplateInfo";
0132:            protected static final String GET_CLOSED_DATE = "getClosedDate";
0133:            protected static final String GET_ORPHAN_ATTRIBUTEVALUES_LIST = "getNonMatchingAttributeValuesList";
0134:            protected static final String GET_DEFAULT_TEXT_ATTRIBUTEVALUE = "getDefaultTextAttributeValue";
0135:            protected static final String GET_DEFAULT_TEXT = "getDefaultText";
0136:            protected static final String GET_NULL_END_DATE = "getActivitiesWithNullEndDate";
0137:            protected static final String GET_INITIAL_ACTIVITYSET = "getInitialActivitySet";
0138:            protected static final String GET_HISTORY_LIMIT = "getHistoryLimit";
0139:
0140:            private static final Integer NUMBERKEY_0 = new Integer(0);
0141:            private static final Integer COPIED = new Integer(1);
0142:            private static final Integer MOVED = new Integer(2);
0143:
0144:            /** storage for any attachments which have not been saved yet */
0145:            private List unSavedAttachments = null;
0146:
0147:            /**
0148:             * new issues are created only when the issuetype and module are known
0149:             * Or by the Peer when retrieving from db
0150:             */
0151:            protected Issue() {
0152:            }
0153:
0154:            protected Issue(Module module, IssueType issueType)
0155:                    throws TorqueException {
0156:                this ();
0157:                setModule(module);
0158:                setIssueType(issueType);
0159:            }
0160:
0161:            /**
0162:             * Gets an issue associated to a Module
0163:             */
0164:            public static Issue getNewInstance(Module module,
0165:                    IssueType issueType) throws TorqueException {
0166:                Issue issue = new Issue(module, issueType);
0167:                return issue;
0168:            }
0169:
0170:            /**
0171:             * @deprecated use IssueManager.getIssueById
0172:             */
0173:            public static Issue getIssueById(String id) {
0174:                return IssueManager.getIssueById(id);
0175:            }
0176:
0177:            /**
0178:             * @deprecated use IssueManager.getIssueById
0179:             */
0180:            public static Issue getIssueById(Issue.FederatedId fid) {
0181:                return IssueManager.getIssueByIdImpl(fid);
0182:            }
0183:
0184:            /**
0185:             * Gets the UniqueId for this Issue.
0186:             */
0187:            public String getUniqueId() throws TorqueException {
0188:                if (getIdPrefix() == null) {
0189:                    setIdPrefix(getModule().getCode());
0190:                }
0191:                return getIdPrefix() + getIdCount();
0192:            }
0193:
0194:            /**
0195:             * NoOp for intake's benefit
0196:             */
0197:            public void setUniqueId(String id) {
0198:            }
0199:
0200:            public String getFederatedId() throws TorqueException {
0201:                if (getIdDomain() != null) {
0202:                    return getIdDomain() + '-' + getUniqueId();
0203:                }
0204:                return getUniqueId();
0205:            }
0206:
0207:            public void setFederatedId(String id) {
0208:                FederatedId fid = new FederatedId(id);
0209:                setIdDomain(fid.getDomain());
0210:                setIdPrefix(fid.getPrefix());
0211:                setIdCount(fid.getCount());
0212:            }
0213:
0214:            /**
0215:             * A FederatedId has this format: {Domain}-{Code}{Id}
0216:             * For example: collab.net-PACS1
0217:             * The domain can also be null.
0218:             */
0219:            public static class FederatedId implements  Serializable {
0220:                private String domainId;
0221:                private String prefix;
0222:                private int count;
0223:
0224:                public FederatedId(String id) {
0225:                    int dash = id.indexOf('-');
0226:                    if (dash > 0) {
0227:                        domainId = id.substring(0, dash);
0228:                        setUniqueId(id.substring(dash + 1));
0229:                    } else {
0230:                        setUniqueId(id);
0231:                    }
0232:                }
0233:
0234:                public FederatedId(String domain, String prefix, int count) {
0235:                    this .domainId = domain;
0236:                    setPrefix(prefix);
0237:                    this .count = count;
0238:                }
0239:
0240:                /**
0241:                 * @param id The unique identifier for this issue, generally a
0242:                 * combination of code and sequence number (e.g. SCB37).
0243:                 */
0244:                public void setUniqueId(String id) {
0245:                    int codeLength = ScarabGlobalTool.getModuleCodeLength();
0246:                    // we could start at 1 here, if the spec says one char is 
0247:                    // required, will keep it safe for now.
0248:                    StringBuffer code = new StringBuffer(codeLength);
0249:                    int max = id.length() < codeLength ? id.length()
0250:                            : codeLength;
0251:                    for (int i = 0; i < max; i++) {
0252:                        char c = id.charAt(i);
0253:                        if (c < '0' || c > '9') {
0254:                            code.append(c);
0255:                        }
0256:                    }
0257:                    if (code.length() != 0) {
0258:                        setPrefix(code.toString());
0259:                    }
0260:                    count = Integer.parseInt(id.substring(code.length()));
0261:                }
0262:
0263:                /**
0264:                 * @return The domain.
0265:                 */
0266:                public String getDomain() {
0267:                    return domainId;
0268:                }
0269:
0270:                /**
0271:                 * @return The prefix (upper-cased).
0272:                 */
0273:                public String getPrefix() {
0274:                    return prefix;
0275:                }
0276:
0277:                /**
0278:                 * @return The sequence of this issue within its code.
0279:                 */
0280:                public int getCount() {
0281:                    return count;
0282:                }
0283:
0284:                /**
0285:                 * Set the domainId
0286:                 */
0287:                public void setDomain(String domainId) {
0288:                    this .domainId = domainId;
0289:                }
0290:
0291:                /**
0292:                 * @param prefix The module code.
0293:                 */
0294:                public void setPrefix(String prefix) {
0295:                    if (prefix != null) {
0296:                        this .prefix = prefix.toUpperCase();
0297:                    }
0298:                }
0299:
0300:                /**
0301:                 * @param count The sequence of this issue within its code.
0302:                 */
0303:                public void setCount(int count) {
0304:                    this .count = count;
0305:                }
0306:
0307:                public boolean equals(Object obj) {
0308:                    boolean b = false;
0309:                    if (obj instanceof  FederatedId) {
0310:                        FederatedId fid = (FederatedId) obj;
0311:                        b = fid.count == this .count;
0312:                        b &= ObjectUtils.equals(fid.domainId, domainId);
0313:                        b &= ObjectUtils.equals(fid.prefix, prefix);
0314:                    }
0315:                    return b;
0316:                }
0317:
0318:                public int hashCode() {
0319:                    int hc = count;
0320:                    if (domainId != null) {
0321:                        hc += domainId.hashCode();
0322:                    }
0323:                    if (prefix != null) {
0324:                        hc += prefix.hashCode();
0325:                    }
0326:                    return hc;
0327:                }
0328:            }
0329:
0330:            /**
0331:             * @param module The current module.
0332:             * @param theList A textual representation of the list of issues
0333:             * to parse.
0334:             * @param The parsed list of issue identifiers.
0335:             */
0336:            public static List parseIssueList(final Module module,
0337:                    final String theList) throws TorqueException,
0338:                    DataSetException {
0339:                final String[] issues = StringUtils.split(theList, ",");
0340:                final List results = new ArrayList();
0341:                for (int i = 0; i < issues.length; i++) {
0342:                    if (issues[i].indexOf('*') != -1) {
0343:                        // Probably better to use more Torque here, but this
0344:                        // is definitely going to be faster and more
0345:                        // efficient.
0346:                        final String sql = "SELECT CONCAT("
0347:                                + IssuePeer.ID_PREFIX + ','
0348:                                + IssuePeer.ID_COUNT + ") FROM "
0349:                                + IssuePeer.TABLE_NAME + " WHERE "
0350:                                + IssuePeer.ID_PREFIX + " = '"
0351:                                + module.getCode() + '\'';
0352:                        final List records = BasePeer.executeQuery(sql);
0353:                        for (Iterator j = records.iterator(); j.hasNext();) {
0354:                            final Record rec = (Record) j.next();
0355:                            results.add(rec.getValue(1).asString());
0356:                        }
0357:                    }
0358:                    // check for a -
0359:                    else if (issues[i].indexOf('-') == -1) {
0360:                        // Make sure user is not trying to access issues from another
0361:                        // module.
0362:                        final FederatedId fid = createFederatedId(module,
0363:                                issues[i]);
0364:                        if (!fid.getPrefix().equalsIgnoreCase(module.getCode())) {
0365:                            final String[] args = { fid.getPrefix(),
0366:                                    module.getCode() };
0367:                            throw new TorqueException(Localization.format(
0368:                                    ScarabConstants.DEFAULT_BUNDLE_NAME, module
0369:                                            .getLocale(),
0370:                                    "IssueIDPrefixNotForModule", args)); //EXCEPTION
0371:                        }
0372:                        results.add(issues[i]);
0373:                    } else {
0374:                        final String[] issue = StringUtils
0375:                                .split(issues[i], "-");
0376:                        if (issue.length != 2) {
0377:                            throw new TorqueException(Localization.format(
0378:                                    ScarabConstants.DEFAULT_BUNDLE_NAME, module
0379:                                            .getLocale(),
0380:                                    "IssueIDRangeNotValid", issues[i])); //EXCEPTION
0381:                        }
0382:                        FederatedId fidStart = createFederatedId(module,
0383:                                issue[0]);
0384:                        FederatedId fidStop = createFederatedId(module,
0385:                                issue[1]);
0386:                        if (!fidStart.getPrefix().equalsIgnoreCase(
0387:                                module.getCode())
0388:                                || !fidStop.getPrefix().equalsIgnoreCase(
0389:                                        module.getCode())) {
0390:                            throw new TorqueException(Localization.format(
0391:                                    ScarabConstants.DEFAULT_BUNDLE_NAME, module
0392:                                            .getLocale(),
0393:                                    "IssueIDPrefixesNotForModule", module
0394:                                            .getCode())); //EXCEPTION
0395:                        } else if (!fidStart.getPrefix().equalsIgnoreCase(
0396:                                fidStop.getPrefix())) {
0397:                            final String[] args = { fidStart.getPrefix(),
0398:                                    fidStop.getPrefix() };
0399:                            throw new TorqueException(Localization.format(
0400:                                    ScarabConstants.DEFAULT_BUNDLE_NAME, module
0401:                                            .getLocale(),
0402:                                    "IssueIDPrefixesDoNotMatch", args)); //EXCEPTION
0403:                        } else if (fidStart.getCount() > fidStop.getCount()) {
0404:                            FederatedId swap = fidStart;
0405:                            fidStart = fidStop;
0406:                            fidStop = swap;
0407:                        }
0408:
0409:                        for (int j = fidStart.getCount(); j <= fidStop
0410:                                .getCount(); j++) {
0411:                            results.add(fidStart.getPrefix() + j);
0412:                        }
0413:                    }
0414:                }
0415:                return results;
0416:            }
0417:
0418:            /**
0419:             * Catches and rethrows parsing errors when creating the federated id.
0420:             */
0421:            private static FederatedId createFederatedId(Module module,
0422:                    String id) throws TorqueException {
0423:                FederatedId fid = null;
0424:                try {
0425:                    fid = new FederatedId(id.trim());
0426:                    if (fid.getPrefix() == null
0427:                            || fid.getPrefix().length() == 0) {
0428:                        fid.setPrefix(module.getCode());
0429:                    }
0430:                } catch (Exception e) {
0431:                    throw new TorqueException("Invalid federated id: " + id); //EXCEPTION
0432:                }
0433:                return fid;
0434:            }
0435:
0436:            /**
0437:             * Whether this issue is an enter issue template.
0438:             */
0439:            public boolean isTemplate() {
0440:                boolean isTemplate = false;
0441:                try {
0442:                    isTemplate = !getIssueType().getParentId().equals(
0443:                            NUMBERKEY_0);
0444:                } catch (Exception e) {
0445:                    getLog().error(
0446:                            "Problem determining whether issue is template");
0447:                }
0448:                return isTemplate;
0449:            }
0450:
0451:            /**
0452:             * Adds a url to an issue and passes null as the activity set
0453:             * to create a new one.
0454:             */
0455:            public ActivitySet addUrl(final Attachment attachment,
0456:                    final ScarabUser user) throws TorqueException,
0457:                    ScarabException {
0458:                return addUrl(null, attachment, user);
0459:            }
0460:
0461:            /**
0462:             * Adds a url to an issue.
0463:             */
0464:            public ActivitySet addUrl(ActivitySet activitySet,
0465:                    final Attachment attachment, final ScarabUser user)
0466:                    throws TorqueException, ScarabException {
0467:                attachment.setTextFields(user, this , Attachment.URL__PK);
0468:                attachment.save();
0469:
0470:                activitySet = attachActivitySet(activitySet, user);
0471:
0472:                // Save activity record
0473:                ActivityManager.createTextActivity(this , activitySet,
0474:                        ActivityType.URL_ADDED, attachment);
0475:
0476:                return activitySet;
0477:            }
0478:
0479:            private Locale getLocale() throws TorqueException {
0480:                return getModule().getLocale();
0481:            }
0482:
0483:            /**
0484:             * Adds a comment to an issue and passes null as the activity set
0485:             * to create a new one.
0486:             */
0487:            public ActivitySet addComment(final Attachment attachment,
0488:                    final ScarabUser user) throws TorqueException,
0489:                    ScarabException {
0490:                return addComment(null, attachment, user);
0491:            }
0492:
0493:            /**
0494:             * Adds a comment to an issue.
0495:             */
0496:            public ActivitySet addComment(ActivitySet activitySet,
0497:                    Attachment attachment, ScarabUser user)
0498:                    throws TorqueException, ScarabException {
0499:                String comment = attachment.getData();
0500:                if (comment == null || comment.length() == 0) {
0501:                    throw new ScarabException(L10NKeySet.NoDataInComment);
0502:                }
0503:
0504:                activitySet = attachActivitySet(activitySet, user);
0505:
0506:                // populates the attachment with data to be a comment
0507:                attachment = AttachmentManager.getComment(attachment, this ,
0508:                        user);
0509:
0510:                ActivityManager.createTextActivity(this , activitySet,
0511:                        ActivityType.COMMENT_ADDED, attachment);
0512:
0513:                NotificationManagerFactory.getInstance()
0514:                        .addActivityNotification(ActivityType.COMMENT_ADDED,
0515:                                activitySet, this , user);
0516:
0517:                return activitySet;
0518:            }
0519:
0520:            /**
0521:             * Adds an attachment file to this issue. Does not perform
0522:             * a save because the issue may not have been created yet.
0523:             * use the doSaveFileAttachment() to save the attachment
0524:             * after the issue has been created.
0525:             */
0526:            public synchronized void addFile(Attachment attachment,
0527:                    ScarabUser user) throws TorqueException {
0528:                attachment.setTypeId(Attachment.FILE__PK);
0529:                attachment.setCreatedBy(user.getUserId());
0530:                if (unSavedAttachments == null) {
0531:                    unSavedAttachments = new ArrayList();
0532:                }
0533:                unSavedAttachments.add(attachment);
0534:            }
0535:
0536:            /**
0537:             * Overrides the super method in order to allow
0538:             * us to return the unSavedAttachments if they exist.
0539:             */
0540:            public synchronized List getAttachments() throws TorqueException {
0541:                if (unSavedAttachments != null && unSavedAttachments.size() > 0) {
0542:                    return unSavedAttachments;
0543:                } else {
0544:                    return super .getAttachments();
0545:                }
0546:            }
0547:
0548:            /**
0549:             * Adds an attachment file to this issue. Does not perform
0550:             * a save because the issue may not have been created yet.
0551:             * use the doSaveFileAttachment() to save the attachment
0552:             * after the issue has been created.
0553:             */
0554:            public synchronized ActivitySet doSaveFileAttachments(
0555:                    final ScarabUser user) throws TorqueException,
0556:                    ScarabException {
0557:                return doSaveFileAttachments(null, user);
0558:            }
0559:
0560:            /**
0561:             * Adds an attachment file to this issue. Does not perform
0562:             * a save because the issue may not have been created yet.
0563:             * use the doSaveFileAttachment() to save the attachment
0564:             * after the issue has been created.
0565:             */
0566:            public synchronized ActivitySet doSaveFileAttachments(
0567:                    ActivitySet activitySet, final ScarabUser user)
0568:                    throws TorqueException, ScarabException {
0569:                if (unSavedAttachments == null) {
0570:                    return activitySet;
0571:                }
0572:                activitySet = attachActivitySet(activitySet, user);
0573:
0574:                final Iterator itr = unSavedAttachments.iterator();
0575:                while (itr.hasNext()) {
0576:                    final Attachment attachment = (Attachment) itr.next();
0577:                    // make sure we set the issue to the newly created issue
0578:                    attachment.setIssue(this );
0579:                    attachment.save();
0580:
0581:                    // Save activity record
0582:                    ActivityManager.createTextActivity(this , activitySet,
0583:                            ActivityType.ATTACHMENT_CREATED, attachment);
0584:
0585:                }
0586:                // reset the super method so that the query has to hit the database again
0587:                // so that all of the information is cleaned up and reset.
0588:                super .collAttachments = null;
0589:                // we don't need this one anymore either.
0590:                this .unSavedAttachments = null;
0591:                return activitySet;
0592:            }
0593:
0594:            /** 
0595:             * Remove an attachment file
0596:             * @param index starts with 1 because velocityCount start from 1
0597:             * but ArrayList starts from 0
0598:             */
0599:            public void removeFile(String index) throws TorqueException {
0600:                int indexInt = Integer.parseInt(index) - 1;
0601:                if (indexInt >= 0) {
0602:                    if (unSavedAttachments != null
0603:                            && unSavedAttachments.size() > 0) {
0604:                        unSavedAttachments.remove(indexInt);
0605:                    } else {
0606:                        List attachList = getAttachments();
0607:                        if (attachList != null && attachList.size() > 0) {
0608:                            attachList.remove(indexInt);
0609:                        }
0610:                    }
0611:                }
0612:            }
0613:
0614:            /**
0615:             * Throws UnsupportedOperationException.  Use
0616:             * <code>getModule()</code> instead.
0617:             *
0618:             * @return a <code>ScarabModule</code> value
0619:             */
0620:            public ScarabModule getScarabModule() {
0621:                throw new UnsupportedOperationException("Should use getModule"); //EXCEPTION
0622:            }
0623:
0624:            /**
0625:             * Throws UnsupportedOperationException.  Use
0626:             * <code>setModule(Module)</code> instead.
0627:             *
0628:             */
0629:            public void setScarabModule(ScarabModule module) {
0630:                throw new UnsupportedOperationException(
0631:                        "Should use setModule(Module). Note module cannot be new."); //EXCEPTION
0632:            }
0633:
0634:            /**
0635:             * Use this instead of setScarabModule.  Note: module cannot be new.
0636:             */
0637:            public void setModule(Module me) throws TorqueException {
0638:                Integer id = me.getModuleId();
0639:                if (id == null) {
0640:                    throw new TorqueException("Modules must be saved prior to "
0641:                            + "being associated with other objects."); //EXCEPTION
0642:                }
0643:                setModuleId(id);
0644:            }
0645:
0646:            /**
0647:             * Module getter.  Use this method instead of getScarabModule().
0648:             *
0649:             * @return a <code>Module</code> value
0650:             */
0651:            public Module getModule() throws TorqueException {
0652:                Module module = null;
0653:                Integer id = getModuleId();
0654:                if (id != null) {
0655:                    module = ModuleManager.getInstance(id);
0656:                }
0657:
0658:                return module;
0659:            }
0660:
0661:            /**
0662:             * The RModuleIssueType related to this issue's module and issue type.
0663:             *
0664:             * @return a <code>RModuleIssueType</code> if this issue's module and
0665:             * issue type are not null, otherwise return null.
0666:             */
0667:            public RModuleIssueType getRModuleIssueType()
0668:                    throws TorqueException {
0669:                RModuleIssueType rmit = null;
0670:                Module module = getModule();
0671:                IssueType issueType = getIssueType();
0672:                if (module != null && issueType != null) {
0673:                    rmit = module.getRModuleIssueType(issueType);
0674:                }
0675:                return rmit;
0676:            }
0677:
0678:            /**
0679:             * Calls the overloaded version by passing 'true' so that only active
0680:             * attributes will be considered.
0681:             * @see #getModuleAttributeValuesMap(boolean)
0682:             */
0683:            public LinkedMap getModuleAttributeValuesMap()
0684:                    throws TorqueException {
0685:                return getModuleAttributeValuesMap(true);
0686:            }
0687:
0688:            /**
0689:             * AttributeValues that are relevant to the issue's current module.
0690:             * Empty AttributeValues that are relevant for the module, but have
0691:             * not been set for the issue are included.  The values are ordered
0692:             * according to the module's preference
0693:             *
0694:             * @param isActive TRUE if only active attributes need to be considered
0695:             * and FALSE if both active and inactive attributes need to be considered
0696:             */
0697:            public LinkedMap getModuleAttributeValuesMap(final boolean isActive)
0698:                    throws TorqueException {
0699:                LinkedMap result = null;
0700:                Object obj = getCachedObject(GET_MODULE_ATTRVALUES_MAP,
0701:                        isActive ? Boolean.TRUE : Boolean.FALSE);
0702:                if (obj == null) {
0703:                    List attributes = null;
0704:                    Module module = getModule();
0705:                    IssueType issueType = getIssueType();
0706:                    if (isActive) {
0707:                        attributes = issueType.getActiveAttributes(module);
0708:                    } else {
0709:                        attributes = module.getAttributes(issueType);
0710:                    }
0711:                    Map siaValuesMap = getAttributeValuesMap();
0712:                    result = new LinkedMap((int) (1.25 * attributes.size() + 1));
0713:                    for (int i = 0; i < attributes.size(); i++) {
0714:                        String key = ((Attribute) attributes.get(i)).getName()
0715:                                .toUpperCase();
0716:                        if (siaValuesMap.containsKey(key)) {
0717:                            result.put(key, siaValuesMap.get(key));
0718:                        } else {
0719:                            Attribute attr = (Attribute) attributes.get(i);
0720:                            AttributeValue aval = AttributeValue
0721:                                    .getNewInstance(attr, this );
0722:                            addAttributeValue(aval);
0723:                            String avalKey = aval.getAttribute().getName()
0724:                                    .toUpperCase();
0725:                            siaValuesMap.put(avalKey, aval);
0726:                            result.put(key, aval);
0727:                        }
0728:                    }
0729:                    putCachedObject(result, GET_MODULE_ATTRVALUES_MAP,
0730:                            isActive ? Boolean.TRUE : Boolean.FALSE);
0731:                } else {
0732:                    result = (LinkedMap) obj;
0733:                }
0734:                return result;
0735:            }
0736:
0737:            public void addAttributeValue(AttributeValue aval)
0738:                    throws TorqueException {
0739:                List avals = getAttributeValues();
0740:                if (!avals.contains(aval)) {
0741:                    super .addAttributeValue(aval);
0742:                }
0743:            }
0744:
0745:            public AttributeValue getAttributeValue(String attributeName)
0746:                    throws TorqueException {
0747:                Attribute attribute = Attribute.getInstance(attributeName);
0748:                AttributeValue result;
0749:                if (attribute == null) {
0750:                    result = null;
0751:                } else {
0752:                    result = getAttributeValue(attribute);
0753:                }
0754:                return result;
0755:            }
0756:
0757:            public AttributeValue getAttributeValue(int id)
0758:                    throws TorqueException {
0759:                Attribute attribute = Attribute.getInstance(id);
0760:                return getAttributeValue(attribute);
0761:            }
0762:
0763:            public AttributeValue getAttributeValue(Attribute attribute)
0764:                    throws TorqueException {
0765:                AttributeValue result = null;
0766:                Object obj = ScarabCache.get(this , GET_ATTRVALUE, attribute);
0767:                if (obj == null) {
0768:                    if (isNew()) {
0769:                        List avals = getAttributeValues();
0770:                        if (avals != null) {
0771:                            Iterator i = avals.iterator();
0772:                            while (i.hasNext()) {
0773:                                AttributeValue tempAval = (AttributeValue) i
0774:                                        .next();
0775:                                if (tempAval.getAttribute().equals(attribute)) {
0776:                                    result = tempAval;
0777:                                    break;
0778:                                }
0779:                            }
0780:                        }
0781:                    } else {
0782:                        Criteria crit = new Criteria(2).add(
0783:                                AttributeValuePeer.ISSUE_ID, getIssueId()).add(
0784:                                AttributeValuePeer.DELETED, false).add(
0785:                                AttributeValuePeer.ATTRIBUTE_ID,
0786:                                attribute.getAttributeId());
0787:
0788:                        List avals = getAttributeValues(crit);
0789:                        if (avals.size() > 0) {
0790:                            result = (AttributeValue) avals.get(0);
0791:                        }
0792:                        if (avals.size() > 1) {
0793:                            getLog()
0794:                                    .error(
0795:                                            "getAttributeValue(): Error when retrieving attribute values of attribute. Expected 1 and found "
0796:                                                    + avals.size()
0797:                                                    + ". List follows: "
0798:                                                    + avals);
0799:                        }
0800:                    }
0801:                    ScarabCache.put(result, this , GET_ATTRVALUE, attribute);
0802:                } else {
0803:                    result = (AttributeValue) obj;
0804:                }
0805:                return result;
0806:            }
0807:
0808:            /**
0809:             * Returns the (undeleted) AttributeValues for the Attribute.
0810:             */
0811:            public List getAttributeValues(final Attribute attribute)
0812:                    throws TorqueException {
0813:                List result = null;
0814:                Object obj = ScarabCache.get(this , GET_ATTRVALUES, attribute);
0815:                if (obj == null) {
0816:                    if (isNew()) {
0817:                        final List avals = getAttributeValues();
0818:                        result = new ArrayList();
0819:                        if (avals != null) {
0820:                            final Iterator i = avals.iterator();
0821:                            while (i.hasNext()) {
0822:                                final AttributeValue tempAval = (AttributeValue) i
0823:                                        .next();
0824:                                if (tempAval.getAttribute().equals(attribute)) {
0825:                                    result.add(tempAval);
0826:                                }
0827:                            }
0828:                        }
0829:                    } else {
0830:                        final Criteria crit = new Criteria(2).add(
0831:                                AttributeValuePeer.DELETED, false).add(
0832:                                AttributeValuePeer.ATTRIBUTE_ID,
0833:                                attribute.getAttributeId());
0834:
0835:                        result = getAttributeValues(crit);
0836:                        ScarabCache
0837:                                .put(result, this , GET_ATTRVALUES, attribute);
0838:                    }
0839:                } else {
0840:                    result = (List) obj;
0841:                }
0842:                return result;
0843:            }
0844:
0845:            public boolean isAttributeValue(AttributeValue attVal)
0846:                    throws TorqueException {
0847:                boolean isValue = false;
0848:                List attValues = getAttributeValues(attVal.getAttribute());
0849:                if (attValues.contains(attVal)) {
0850:                    isValue = true;
0851:                }
0852:                return isValue;
0853:            }
0854:
0855:            /**
0856:             * Returns the attributevalue of the attribute with the value passed
0857:             * (as String or Number). This is needed, because some attributes might
0858:             * have MULTIPLE VALUES for the same issue (user attributes at least)
0859:             * @param att Attribute for with to get the attributevalue
0860:             * @param strVal String value to test
0861:             * @param numVal Integer value to test
0862:             * @return the attributevalue or null if not found
0863:             * @throws TorqueException
0864:             */
0865:            private AttributeValue getAttributeValueWithValue(Attribute att,
0866:                    String strVal, Integer numVal) throws TorqueException {
0867:                AttributeValue val = null;
0868:                boolean bFound = false;
0869:                List attValues = getAttributeValues(att);
0870:                for (Iterator it = attValues.iterator(); !bFound
0871:                        && it.hasNext();) {
0872:                    val = (AttributeValue) it.next();
0873:                    if (strVal != null)
0874:                        bFound = val.getValue().equals(strVal);
0875:                    else if (!bFound && numVal != null)
0876:                        bFound = val.getNumericValue().equals(numVal);
0877:                }
0878:                return val;
0879:            }
0880:
0881:            /**
0882:             * AttributeValues that are set for this Issue
0883:             */
0884:            public Map getAttributeValuesMap() throws TorqueException {
0885:                Map result = null;
0886:                Object obj = ScarabCache.get(this , GET_ATTRIBUTE_VALUES_MAP);
0887:                if (obj == null) {
0888:                    final Criteria crit = new Criteria(2).add(
0889:                            AttributeValuePeer.DELETED, false);
0890:                    final List siaValues = getAttributeValues(crit);
0891:                    result = new HashMap((int) (1.25 * siaValues.size() + 1));
0892:                    for (Iterator i = siaValues.iterator(); i.hasNext();) {
0893:                        final AttributeValue att = (AttributeValue) i.next();
0894:                        result.put(att.getAttribute().getName().toUpperCase(),
0895:                                att);
0896:                    }
0897:
0898:                    ScarabCache.put(result, this , GET_ATTRIBUTE_VALUES_MAP);
0899:                } else {
0900:                    result = (Map) obj;
0901:                }
0902:                return result;
0903:            }
0904:
0905:            /**
0906:             * AttributeValues that are set for this issue and
0907:             * Empty AttributeValues that are relevant for the module, but have 
0908:             * not been set for the issue are included.
0909:             */
0910:            public Map getAllAttributeValuesMap() throws TorqueException {
0911:                Map moduleAtts = getModuleAttributeValuesMap();
0912:                Map issueAtts = getAttributeValuesMap();
0913:                Map allValuesMap = new HashMap(
0914:                        (int) (1.25 * (moduleAtts.size() + issueAtts.size()) + 1));
0915:
0916:                allValuesMap.putAll(moduleAtts);
0917:                allValuesMap.putAll(issueAtts);
0918:                return allValuesMap;
0919:            }
0920:
0921:            /**
0922:             * Describe <code>containsMinimumAttributeValues</code> method here.
0923:             *
0924:             * @return a <code>boolean</code> value
0925:             * @exception Exception if an error occurs
0926:             */
0927:            public boolean containsMinimumAttributeValues()
0928:                    throws TorqueException {
0929:                List attributes = getIssueType().getRequiredAttributes(
0930:                        getModule());
0931:
0932:                boolean result = true;
0933:                LinkedMap avMap = getModuleAttributeValuesMap();
0934:                MapIterator i = avMap.mapIterator();
0935:                while (i.hasNext()) {
0936:                    AttributeValue aval = (AttributeValue) avMap.get(i.next());
0937:
0938:                    if (aval.getOptionId() == null && aval.getValue() == null) {
0939:                        for (int j = attributes.size() - 1; j >= 0; j--) {
0940:                            if (aval.getAttribute().getPrimaryKey().equals(
0941:                                    ((Attribute) attributes.get(j))
0942:                                            .getPrimaryKey())) {
0943:                                result = false;
0944:                                break;
0945:                            }
0946:                        }
0947:                        if (!result) {
0948:                            break;
0949:                        }
0950:                    }
0951:                }
0952:                return result;
0953:            }
0954:
0955:            /**
0956:             * Users who are valid values to the attribute this issue.  
0957:             * if a user has already
0958:             * been assigned to this issue, they will not show up in this list.
0959:             * use module.getEligibleUsers(Attribute) to get a complete list.
0960:             *
0961:             * @return a <code>List</code> value
0962:             */
0963:            public List getEligibleUsers(Attribute attribute)
0964:                    throws TorqueException, ScarabException {
0965:                ScarabUser[] users = getModule().getEligibleUsers(attribute);
0966:                // remove those already assigned
0967:                List assigneeAVs = getAttributeValues(attribute);
0968:                if (users != null && assigneeAVs != null) {
0969:                    for (int i = users.length - 1; i >= 0; i--) {
0970:                        for (int j = assigneeAVs.size() - 1; j >= 0; j--) {
0971:                            AttributeValue av = (AttributeValue) assigneeAVs
0972:                                    .get(j);
0973:                            Integer avUserId = av.getUserId();
0974:                            Integer userUserId = users[i].getUserId();
0975:                            if (av != null && avUserId != null
0976:                                    && userUserId != null
0977:                                    && avUserId.equals(userUserId)) {
0978:                                users[i] = null;
0979:                                break;
0980:                            }
0981:                        }
0982:                    }
0983:                }
0984:
0985:                List eligibleUsers = new ArrayList(users.length);
0986:                for (int i = 0; i < users.length; i++) {
0987:                    if (users[i] != null) {
0988:                        eligibleUsers.add(users[i]);
0989:                    }
0990:                }
0991:
0992:                return eligibleUsers;
0993:            }
0994:
0995:            /**
0996:             * Returns the users which should be notified when this issue is
0997:             * modified.  The set contains those users associated with user
0998:             * attributes for this issue, plus the creator of the issue.
0999:             *
1000:             * @param action
1001:             * @param issue Usually a reference to this or a dependent issue.
1002:             * @param users The list of users to append to, or
1003:             * <code>null</code> to create a new list.
1004:             */
1005:            protected Set getUsersToEmail(String action, Issue issue, Set users)
1006:                    throws TorqueException {
1007:                if (users == null) {
1008:                    users = new HashSet(1);
1009:                }
1010:
1011:                Module module = getModule();
1012:
1013:                ScarabUser createdBy = issue.getCreatedBy();
1014:                if (createdBy != null
1015:                        && !users.contains(createdBy)
1016:                        && AttributePeer.EMAIL_TO.equals(action)
1017:                        && createdBy.hasPermission(ScarabSecurity.ISSUE__ENTER,
1018:                                module)) {
1019:                    users.add(createdBy);
1020:                }
1021:
1022:                Criteria crit = new Criteria().add(AttributeValuePeer.ISSUE_ID,
1023:                        issue.getIssueId()).addJoin(
1024:                        AttributeValuePeer.ATTRIBUTE_ID,
1025:                        AttributePeer.ATTRIBUTE_ID).add(AttributePeer.ACTION,
1026:                        action).add(RModuleAttributePeer.MODULE_ID,
1027:                        getModuleId()).add(RModuleAttributePeer.ISSUE_TYPE_ID,
1028:                        getTypeId()).add(AttributeValuePeer.DELETED, 0).add(
1029:                        RModuleAttributePeer.ACTIVE, true).addJoin(
1030:                        RModuleAttributePeer.ATTRIBUTE_ID,
1031:                        AttributeValuePeer.ATTRIBUTE_ID);
1032:                List userAttVals = AttributeValuePeer.doSelect(crit);
1033:                for (Iterator i = userAttVals.iterator(); i.hasNext();) {
1034:                    AttributeValue attVal = (AttributeValue) i.next();
1035:                    try {
1036:                        ScarabUser su = ScarabUserManager.getInstance(attVal
1037:                                .getUserId());
1038:                        if (!users.contains(su)
1039:                                && su.hasPermission(attVal.getAttribute()
1040:                                        .getPermission(), module)) {
1041:                            users.add(su);
1042:                        }
1043:                    } catch (Exception e) {
1044:                        throw new TorqueException(
1045:                                "Error retrieving users to email"); //EXCEPTION
1046:                    }
1047:                }
1048:                return users;
1049:            }
1050:
1051:            /**
1052:             * Returns users assigned to user attributes that get emailed 
1053:             * When issue is modified. Plus creating user.
1054:             * Adds users to email for dependant issues as well.
1055:             *
1056:             * @see #getUsersToEmail
1057:             */
1058:            public Set getAllUsersToEmail(String action) throws TorqueException {
1059:                Set result = null;
1060:                Object obj = ScarabCache.get(this , GET_ALL_USERS_TO_EMAIL,
1061:                        action);
1062:                if (obj == null) {
1063:                    Set users = new HashSet();
1064:                    try {
1065:                        users = getUsersToEmail(action, this , users);
1066:                        List children = getChildren();
1067:                        for (int i = 0; i < children.size(); i++) {
1068:                            Issue depIssue = IssueManager
1069:                                    .getInstance(((Depend) children.get(i))
1070:                                            .getObserverId());
1071:                            users = getUsersToEmail(action, depIssue, users);
1072:                        }
1073:                        result = users;
1074:                    } catch (Exception e) {
1075:                        getLog().error("Issue.getUsersToEmail(): ", e);
1076:                        throw new TorqueException("Error in retrieving users."); //EXCEPTION
1077:                    }
1078:                    ScarabCache.put(result, this , GET_ALL_USERS_TO_EMAIL,
1079:                            action);
1080:                } else {
1081:                    result = (Set) obj;
1082:                }
1083:                return result;
1084:            }
1085:
1086:            /**
1087:             * Returns the specific user's attribute value.
1088:             */
1089:            public AttributeValue getUserAttributeValue(final ScarabUser user,
1090:                    final Attribute attribute) throws TorqueException {
1091:                AttributeValue result = null;
1092:                Object obj = getCachedObject(GET_USER_ATTRIBUTEVALUE, attribute
1093:                        .getAttributeId(), user.getUserId());
1094:                if (obj == null) {
1095:                    final Criteria crit = new Criteria().add(
1096:                            AttributeValuePeer.ATTRIBUTE_ID,
1097:                            attribute.getAttributeId()).add(
1098:                            AttributeValuePeer.ISSUE_ID, getIssueId()).add(
1099:                            AttributeValuePeer.USER_ID, user.getUserId()).add(
1100:                            AttributeValuePeer.DELETED, 0);
1101:                    final List resultList = AttributeValuePeer.doSelect(crit);
1102:                    if (resultList != null && resultList.size() == 1) {
1103:                        result = (AttributeValue) resultList.get(0);
1104:                    }
1105:                    putCachedObject(result, GET_USER_ATTRIBUTEVALUE, attribute
1106:                            .getAttributeId(), user.getUserId());
1107:                } else {
1108:                    result = (AttributeValue) obj;
1109:                }
1110:                return result;
1111:            }
1112:
1113:            /**
1114:             * Returns attribute values for user attributes.
1115:             */
1116:            public List getUserAttributeValues() throws TorqueException {
1117:                List result = null;
1118:                Object obj = getCachedObject(GET_USER_ATTRIBUTEVALUES);
1119:                if (obj == null) {
1120:                    List attributeList = getModule().getUserAttributes(
1121:                            getIssueType(), true);
1122:                    List attributeIdList = new ArrayList();
1123:
1124:                    for (int i = 0; i < attributeList.size(); i++) {
1125:                        Attribute att = (Attribute) attributeList.get(i);
1126:                        attributeIdList.add(att.getAttributeId());
1127:                    }
1128:
1129:                    if (!attributeIdList.isEmpty()) {
1130:                        Criteria crit = new Criteria().addIn(
1131:                                AttributeValuePeer.ATTRIBUTE_ID,
1132:                                attributeIdList).add(
1133:                                AttributeValuePeer.ISSUE_ID, getIssueId()).add(
1134:                                AttributeValuePeer.DELETED, 0);
1135:                        result = AttributeValuePeer.doSelect(crit);
1136:                    } else {
1137:                        result = new ArrayList(0);
1138:                    }
1139:                    putCachedObject(result, GET_USER_ATTRIBUTEVALUES);
1140:                } else {
1141:                    result = (List) obj;
1142:                }
1143:                return result;
1144:            }
1145:
1146:            /**
1147:             * The initial activity set from issue creation.
1148:             *
1149:             * @return a <code>ActivitySet</code> value
1150:             * @exception Exception if an error occurs
1151:             */
1152:            public ActivitySet getInitialActivitySet() throws TorqueException {
1153:                ActivitySet activitySet = getActivitySetRelatedByCreatedTransId();
1154:                if (activitySet == null) {
1155:                    Log.get().warn("Creation ActivitySet is null for " + this );
1156:                }
1157:
1158:                return activitySet;
1159:            }
1160:
1161:            /**
1162:             * The date the issue was created.
1163:             *
1164:             * @return a <code>Date</code> value
1165:             * @exception TorqueException if an error occurs
1166:             */
1167:            public Date getCreatedDate() throws TorqueException {
1168:                ActivitySet creationSet = getActivitySetRelatedByCreatedTransId();
1169:                Date result = null;
1170:                if (creationSet == null) {
1171:                    getLog().warn(
1172:                            "Issue " + getUniqueId() + " (pk=" + getIssueId()
1173:                                    + ") does not have a creation ActivitySet");
1174:                } else {
1175:                    result = creationSet.getCreatedDate();
1176:                }
1177:                return result;
1178:            }
1179:
1180:            /**
1181:             * The user that created the issue.
1182:             * @return a <code>ScarabUser</code> value
1183:             */
1184:            public ScarabUser getCreatedBy() throws TorqueException {
1185:                ActivitySet creationSet = getActivitySetRelatedByCreatedTransId();
1186:                ScarabUser result = null;
1187:                if (creationSet == null) {
1188:                    getLog().warn(
1189:                            "Issue " + getUniqueId() + " (pk=" + getIssueId()
1190:                                    + ") does not have a creation ActivitySet");
1191:                } else {
1192:                    result = creationSet.getScarabUser();
1193:                }
1194:                return result;
1195:            }
1196:
1197:            public boolean isCreatingUser(ScarabUser user)
1198:                    throws TorqueException {
1199:                ActivitySet creationSet = getActivitySetRelatedByCreatedTransId();
1200:                boolean result = false;
1201:                if (creationSet == null) {
1202:                    getLog().warn(
1203:                            "Issue " + getUniqueId() + " (pk=" + getIssueId()
1204:                                    + ") does not have a creation ActivitySet");
1205:                } else {
1206:                    result = creationSet.getCreatedBy()
1207:                            .equals(user.getUserId());
1208:                }
1209:                return result;
1210:            }
1211:
1212:            /**
1213:             * The last modification made to the issue.
1214:             *
1215:             * @return a <code>ScarabUser</code> value
1216:             */
1217:            public ActivitySet getLastActivitySet() throws TorqueException {
1218:                ActivitySet t = null;
1219:                if (!isNew()) {
1220:                    Object obj = ScarabCache.get(this , GET_LAST_TRANSACTION);
1221:                    if (obj == null) {
1222:                        Criteria crit = new Criteria();
1223:                        crit.addJoin(ActivitySetPeer.TRANSACTION_ID,
1224:                                ActivityPeer.TRANSACTION_ID);
1225:                        crit.add(ActivityPeer.ISSUE_ID, getIssueId());
1226:                        Integer[] typeIds = {
1227:                                ActivitySetTypePeer.EDIT_ISSUE__PK,
1228:                                ActivitySetTypePeer.MOVE_ISSUE__PK };
1229:                        crit.addIn(ActivitySetPeer.TYPE_ID, typeIds);
1230:                        // there could be multiple attributes modified during the 
1231:                        // creation which will lead to duplicates
1232:                        crit.setDistinct();
1233:                        crit
1234:                                .addDescendingOrderByColumn(ActivitySetPeer.CREATED_DATE);
1235:                        List activitySets = ActivitySetPeer.doSelect(crit);
1236:                        if (activitySets.size() > 0) {
1237:                            t = (ActivitySet) activitySets.get(0);
1238:                        }
1239:                        ScarabCache.put(t, this , GET_LAST_TRANSACTION);
1240:                    } else {
1241:                        t = (ActivitySet) obj;
1242:                    }
1243:                }
1244:                return t;
1245:            }
1246:
1247:            /**
1248:             * The date issue was last modified.
1249:             *
1250:             * @return a <code>ScarabUser</code> value
1251:             */
1252:            public Date getModifiedDate() throws TorqueException {
1253:                Date result = null;
1254:                if (!isNew()) {
1255:                    ActivitySet t = getLastActivitySet();
1256:                    if (t == null) {
1257:                        result = getCreatedDate();
1258:                    } else {
1259:                        result = t.getCreatedDate();
1260:                    }
1261:                }
1262:                return result;
1263:            }
1264:
1265:            /**
1266:             * The last user to modify the issue.
1267:             *
1268:             * @return a <code>ScarabUser</code> value
1269:             */
1270:            public ScarabUser getModifiedBy() throws TorqueException {
1271:                ScarabUser result = null;
1272:                if (!isNew()) {
1273:                    ActivitySet t = getLastActivitySet();
1274:                    if (t == null) {
1275:                        result = getCreatedBy();
1276:                    } else {
1277:                        result = ScarabUserManager
1278:                                .getInstance(t.getCreatedBy());
1279:                    }
1280:                }
1281:                return result;
1282:            }
1283:
1284:            /**
1285:             * Returns the total number of comments.
1286:             */
1287:            public int getCommentsCount() throws TorqueException {
1288:                return getComments(true).size();
1289:            }
1290:
1291:            /**
1292:             * Determines whether the comments list is longer than
1293:             * The default limit.
1294:             */
1295:            public boolean isCommentsLong() throws TorqueException {
1296:                return (getCommentsCount() > getCommentsLimit());
1297:            }
1298:
1299:            /**
1300:             * Gets default comments limit for this module-issue type.
1301:             */
1302:            public int getCommentsLimit() throws TorqueException {
1303:                int limit = 0;
1304:                try {
1305:                    limit = getModule().getRModuleIssueType(getIssueType())
1306:                            .getComments();
1307:                } catch (Exception e) {
1308:                    // ignored (return 0 by default)
1309:                }
1310:                return limit;
1311:            }
1312:
1313:            /**
1314:             * Returns a list of Attachment objects with type "Comment"
1315:             * That are associated with this issue.
1316:             */
1317:            public List getComments(boolean full) throws TorqueException {
1318:                List result = null;
1319:                Boolean fullBool = (full ? Boolean.TRUE : Boolean.FALSE);
1320:                Object obj = getCachedObject(GET_COMMENTS, fullBool);
1321:                if (obj == null) {
1322:                    Criteria crit = new Criteria().add(AttachmentPeer.ISSUE_ID,
1323:                            getIssueId()).addJoin(
1324:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1325:                            AttachmentPeer.ATTACHMENT_TYPE_ID).add(
1326:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1327:                            Attachment.COMMENT__PK).addDescendingOrderByColumn(
1328:                            AttachmentPeer.CREATED_DATE);
1329:                    if (!full) {
1330:                        crit.setLimit(getCommentsLimit());
1331:                    }
1332:                    result = AttachmentPeer.doSelect(crit);
1333:                    putCachedObject(result, GET_COMMENTS, fullBool);
1334:                } else {
1335:                    result = (List) obj;
1336:                }
1337:                return result;
1338:            }
1339:
1340:            /**
1341:             * Returns a list of Attachment objects with type "URL"
1342:             * That are associated with this issue.
1343:             */
1344:            public List getUrls() throws TorqueException {
1345:                List result = null;
1346:                Object obj = getCachedObject(GET_URLS);
1347:                if (obj == null) {
1348:                    Criteria crit = new Criteria().add(AttachmentPeer.ISSUE_ID,
1349:                            getIssueId()).addJoin(
1350:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1351:                            AttachmentPeer.ATTACHMENT_TYPE_ID).add(
1352:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1353:                            Attachment.URL__PK).add(AttachmentPeer.DELETED, 0);
1354:                    result = AttachmentPeer.doSelect(crit);
1355:                    putCachedObject(result, GET_URLS);
1356:                } else {
1357:                    result = (List) obj;
1358:                }
1359:                return result;
1360:            }
1361:
1362:            /**
1363:             * Get attachments that are not deleted
1364:             */
1365:            public List getExistingAttachments() throws TorqueException {
1366:                List result = null;
1367:                Object obj = getCachedObject(GET_EXISTING_ATTACHMENTS);
1368:                if (obj == null) {
1369:                    Criteria crit = new Criteria().add(AttachmentPeer.ISSUE_ID,
1370:                            getIssueId()).addJoin(
1371:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1372:                            AttachmentPeer.ATTACHMENT_TYPE_ID).add(
1373:                            AttachmentTypePeer.ATTACHMENT_TYPE_ID,
1374:                            Attachment.FILE__PK).add(AttachmentPeer.DELETED, 0);
1375:                    result = AttachmentPeer.doSelect(crit);
1376:                    putCachedObject(result, GET_EXISTING_ATTACHMENTS);
1377:                } else {
1378:                    result = (List) obj;
1379:                }
1380:                return result;
1381:            }
1382:
1383:            public List getActivitiesWithNullEndDate(Attribute attribute)
1384:                    throws TorqueException {
1385:                List result = null;
1386:                Object obj = ScarabCache
1387:                        .get(this , GET_NULL_END_DATE, attribute);
1388:                if (obj == null) {
1389:                    Criteria crit = new Criteria();
1390:                    crit.add(ActivityPeer.ISSUE_ID, this .getIssueId());
1391:                    crit.add(ActivityPeer.ATTRIBUTE_ID, attribute
1392:                            .getAttributeId());
1393:                    crit.add(ActivityPeer.END_DATE, null);
1394:                    result = ActivityPeer.doSelect(crit);
1395:                    ScarabCache.put(result, this , GET_NULL_END_DATE, attribute);
1396:                } else {
1397:                    result = (List) obj;
1398:                }
1399:                return result;
1400:            }
1401:
1402:            /**
1403:             * Gets default history limit for this module-issue type.
1404:             * The default is 5.
1405:             */
1406:            public int getHistoryLimit() throws TorqueException {
1407:                RModuleIssueType rmit = getModule().getRModuleIssueType(
1408:                        getIssueType());
1409:                if (rmit != null) {
1410:                    return rmit.getHistory();
1411:                } else {
1412:                    return 5;
1413:                }
1414:            }
1415:
1416:            /**
1417:             * Determines whether the history list is longer than
1418:             * The default limit.
1419:             */
1420:            public boolean isHistoryLong() throws TorqueException {
1421:                return isHistoryLong(getHistoryLimit());
1422:            }
1423:
1424:            /**
1425:             * Determines whether the history list is longer than
1426:             * The limit.
1427:             */
1428:            public boolean isHistoryLong(int limit) throws TorqueException {
1429:                return (getActivity(true).size() > limit);
1430:            }
1431:
1432:            /**
1433:             * Returns list of Activity objects associated with this Issue.
1434:             */
1435:            public List getActivity() throws TorqueException {
1436:                return getActivity(false, getHistoryLimit());
1437:            }
1438:
1439:            /**
1440:             * Returns limited list of Activity objects associated with this Issue.
1441:             */
1442:            public List getActivity(int limit) throws TorqueException {
1443:                return getActivity(false, limit);
1444:            }
1445:
1446:            /**
1447:             * Returns limited list of Activity objects associated with this Issue.
1448:             * If fullHistory is false, it limits it,
1449:             * (this is the default)
1450:             */
1451:            public List getActivity(boolean fullHistory) throws TorqueException {
1452:                return getActivity(fullHistory, getHistoryLimit());
1453:            }
1454:
1455:            /**
1456:             * Returns full list of Activity objects associated with this Issue.
1457:             */
1458:            private List getActivity(boolean fullHistory, int limit)
1459:                    throws TorqueException {
1460:                List result = null;
1461:                Boolean fullHistoryObj = fullHistory ? Boolean.TRUE
1462:                        : Boolean.FALSE;
1463:                Object obj = getCachedObject(GET_ACTIVITY, fullHistoryObj,
1464:                        new Integer(limit));
1465:                if (obj == null) {
1466:                    Criteria crit = new Criteria().add(ActivityPeer.ISSUE_ID,
1467:                            getIssueId()).addAscendingOrderByColumn(
1468:                            ActivityPeer.TRANSACTION_ID);
1469:                    if (!fullHistory) {
1470:                        crit.setLimit(limit);
1471:                    }
1472:                    result = ActivityPeer.doSelect(crit);
1473:                    putCachedObject(result, GET_ACTIVITY, fullHistoryObj,
1474:                            new Integer(limit));
1475:                } else {
1476:                    result = (List) obj;
1477:                }
1478:                return result;
1479:            }
1480:
1481:            /**
1482:             * Returns limited list of Activity objects associated with this Issue.
1483:             */
1484:            public void addActivity(Activity activity) throws TorqueException {
1485:                List activityList = null;
1486:                try {
1487:                    activityList = getActivity(true);
1488:                } catch (Exception e) {
1489:                    throw new TorqueException(e); //EXCEPTION
1490:                }
1491:                super .addActivity(activity);
1492:                if (!activityList.contains(activity)) {
1493:                    activityList.add(activity);
1494:                }
1495:            }
1496:
1497:            /**
1498:             * Returns a list of ActivitySet objects associated to this issue.
1499:             */
1500:            public List getActivitySets() throws TorqueException {
1501:                List result = null;
1502:                Object obj = ScarabCache.get(this , GET_TRANSACTIONS);
1503:                if (obj == null) {
1504:                    Criteria crit = new Criteria();
1505:                    crit.add(ActivityPeer.ISSUE_ID, getIssueId());
1506:                    crit.addJoin(ActivitySetPeer.TRANSACTION_ID,
1507:                            ActivityPeer.TRANSACTION_ID);
1508:                    crit.setDistinct();
1509:                    result = ActivitySetPeer.doSelect(crit);
1510:                    ScarabCache.put(result, this , GET_TRANSACTIONS);
1511:                } else {
1512:                    result = (List) obj;
1513:                }
1514:                return result;
1515:            }
1516:
1517:            /**
1518:             * Creates a new ActivitySet object for the issue.
1519:             */
1520:            public ActivitySet getActivitySet(final ScarabUser user,
1521:                    final Attachment attachment, final Integer type)
1522:                    throws TorqueException, ScarabException {
1523:                ActivitySet activitySet = null;
1524:                if (attachment == null) {
1525:                    activitySet = ActivitySetManager.getInstance(type, user);
1526:                } else {
1527:                    activitySet = ActivitySetManager.getInstance(type, user,
1528:                            attachment);
1529:                }
1530:                return activitySet;
1531:            }
1532:
1533:            /**
1534:             * Creates a new ActivitySet object for the issue.
1535:             */
1536:            public ActivitySet getActivitySet(ScarabUser user, Integer type)
1537:                    throws TorqueException, ScarabException {
1538:                return getActivitySet(user, null, type);
1539:            }
1540:
1541:            /**
1542:             * Returns the combined output from getChildren() and getParents()
1543:             */
1544:            public List getAllDependencies() throws TorqueException {
1545:                List dependencies = new ArrayList();
1546:                dependencies.addAll(getChildren());
1547:                dependencies.addAll(getParents());
1548:                return dependencies;
1549:            }
1550:
1551:            /**
1552:             * Returns list of child dependencies
1553:             * i.e., related to this issue through the DEPEND table.
1554:             */
1555:            public List getChildren() throws TorqueException {
1556:                return getChildren(true);
1557:            }
1558:
1559:            /**
1560:             * Returns list of child dependencies
1561:             * i.e., related to this issue through the DEPEND table.
1562:             */
1563:            public List getChildren(boolean hideDeleted) throws TorqueException {
1564:                List result = null;
1565:                Boolean hide = hideDeleted ? Boolean.TRUE : Boolean.FALSE;
1566:                Object obj = getCachedObject(GET_CHILDREN, hide);
1567:                if (obj == null) {
1568:                    Criteria crit = new Criteria().add(DependPeer.OBSERVED_ID,
1569:                            getIssueId());
1570:                    if (hideDeleted) {
1571:                        crit.add(DependPeer.DELETED, false);
1572:                    }
1573:                    result = DependPeer.doSelect(crit);
1574:                    putCachedObject(result, GET_CHILDREN, hide);
1575:                } else {
1576:                    result = (List) obj;
1577:                }
1578:                return result;
1579:            }
1580:
1581:            /**
1582:             * Returns list of parent dependencies
1583:             * i.e., related to this issue through the DEPEND table.
1584:             */
1585:            public List getParents() throws TorqueException {
1586:                return getParents(true);
1587:            }
1588:
1589:            /**
1590:             * Returns list of parent dependencies
1591:             * i.e., related to this issue through the DEPEND table.
1592:             */
1593:            public List getParents(boolean hideDeleted) throws TorqueException {
1594:                List result = null;
1595:                Boolean hide = hideDeleted ? Boolean.TRUE : Boolean.FALSE;
1596:                Object obj = getCachedObject(GET_PARENTS, hide);
1597:                if (obj == null) {
1598:                    Criteria crit = new Criteria().add(DependPeer.OBSERVER_ID,
1599:                            getIssueId());
1600:                    if (hideDeleted) {
1601:                        crit.add(DependPeer.DELETED, false);
1602:                    }
1603:                    result = DependPeer.doSelect(crit);
1604:                    putCachedObject(result, GET_PARENTS, hide);
1605:                } else {
1606:                    result = (List) obj;
1607:                }
1608:                return result;
1609:            }
1610:
1611:            /**
1612:             * Returns list of all types of dependencies an issue can have
1613:             * On another issue.
1614:             * @deprecated use DependencyTypeManager.getAll();
1615:             */
1616:            public List getAllDependencyTypes() throws TorqueException {
1617:                return DependTypeManager.getAll();
1618:            }
1619:
1620:            public ActivitySet doAddDependency(ActivitySet activitySet,
1621:                    Depend depend, Issue childIssue, ScarabUser user)
1622:                    throws TorqueException, ScarabException {
1623:                // Check whether the entered issue is already dependent on this
1624:                // Issue. If so, then throw an exception because we don't want
1625:                // to add it again.
1626:                Depend prevDepend = this .getDependency(childIssue, true);
1627:                if (prevDepend != null) {
1628:                    throw new ScarabException(L10NKeySet.DependencyExists);
1629:                }
1630:
1631:                // we definitely want to do an insert here so force it.
1632:                depend.setNew(true);
1633:                depend.setDeleted(false);
1634:                depend.save();
1635:
1636:                Attachment comment = depend.getDescriptionAsAttachment(user,
1637:                        this );
1638:                activitySet = attachActivitySet(activitySet, user, comment);
1639:
1640:                // Save activity record for the parent issue
1641:                ActivityManager.createAddDependencyActivity(this , activitySet,
1642:                        depend);
1643:
1644:                // Save activity record for the child issue
1645:                ActivityManager.createAddDependencyActivity(childIssue,
1646:                        activitySet, depend);
1647:
1648:                return activitySet;
1649:            }
1650:
1651:            /**
1652:             * Checks to see if this issue has a dependency on the passed in issue.
1653:             * or if the passed in issue has a dependency on this issue.
1654:             */
1655:            public Depend getDependency(Issue potentialDependency)
1656:                    throws TorqueException {
1657:                return getDependency(potentialDependency, true);
1658:            }
1659:
1660:            /**
1661:             * Checks to see if this issue has a dependency on the passed in issue.
1662:             * or if the passed in issue has a dependency on this issue.
1663:             *
1664:             * @param potentialDependency the issue for which we are determining if there is a
1665:             * parent or child dependency to this issue
1666:             * @param hideDeleted true if deleted issues are omitted from the search
1667:             * @returns the dependency object or null
1668:             */
1669:            public Depend getDependency(Issue potentialDependency,
1670:                    boolean hideDeleted) throws TorqueException {
1671:                Depend result = null;
1672:                Object obj = ScarabCache.get(this , GET_DEPENDENCY,
1673:                        potentialDependency);
1674:                if (obj == null) {
1675:
1676:                    // Determine if this issue is a parent to the potentialDependency
1677:                    Criteria crit = new Criteria(2).add(DependPeer.OBSERVED_ID,
1678:                            getIssueId()).add(DependPeer.OBSERVER_ID,
1679:                            potentialDependency.getIssueId());
1680:                    if (hideDeleted) {
1681:                        crit.add(DependPeer.DELETED, false);
1682:                    }
1683:
1684:                    List childIssues = DependPeer.doSelect(crit);
1685:                    // A system invariant is that we will get one and only one
1686:                    // record back.
1687:                    if (!childIssues.isEmpty()) {
1688:                        result = (Depend) childIssues.get(0);
1689:                    } else {
1690:                        // Determine if this issue is a child to the potentialDependency
1691:                        Criteria crit2 = new Criteria(2).add(
1692:                                DependPeer.OBSERVER_ID, getIssueId()).add(
1693:                                DependPeer.OBSERVED_ID,
1694:                                potentialDependency.getIssueId());
1695:                        if (hideDeleted) {
1696:                            crit2.add(DependPeer.DELETED, false);
1697:                        }
1698:                        List parentIssues = DependPeer.doSelect(crit2);
1699:                        if (!parentIssues.isEmpty()) {
1700:                            result = (Depend) parentIssues.get(0);
1701:                        }
1702:                    }
1703:
1704:                    if (result != null) {
1705:                        ScarabCache.put(result, this , GET_DEPENDENCY,
1706:                                potentialDependency);
1707:                    }
1708:                } else {
1709:                    result = (Depend) obj;
1710:                }
1711:                return result;
1712:            }
1713:
1714:            /**
1715:             * Removes any unset attributes and sets the issue # prior to saving
1716:             * for the first time.  Calls super.save()
1717:             *
1718:             * If the issue does not have an <i>idCount</i> then the next
1719:             * available ID is allocated.
1720:             *
1721:             * If the issue has a non-zero <i>idCount</i>, this value is honoured.
1722:             * WARNING: do not set the idCount to an existing ID!
1723:             * The nominated value is ignored if it is not at least as high as the 
1724:             * next available ID.
1725:             *
1726:             *
1727:             * @param dbCon a <code>DBConnection</code> value
1728:             * @exception TorqueException if an error occurs
1729:             */
1730:            public void save(Connection dbCon) throws TorqueException {
1731:                Module module = getModule();
1732:                if (!module.allowsIssues()
1733:                        || (isNew() && !module.allowsNewIssues())) {
1734:                    throw new UnsupportedOperationException(module.getName()
1735:                            + " does not allow issues."); //EXCEPTION
1736:                }
1737:
1738:                // remove unset AttributeValues before saving
1739:                List attValues = getAttributeValues();
1740:                // reverse order since removing from list
1741:                for (int i = attValues.size() - 1; i >= 0; i--) {
1742:                    AttributeValue attVal = (AttributeValue) attValues.get(i);
1743:                    if (!attVal.isSet()) {
1744:                        attValues.remove(i);
1745:                    }
1746:                }
1747:
1748:                if (isNew()) {
1749:                    // set the issue id
1750:                    setIdDomain(module.getScarabInstanceId());
1751:                    setIdPrefix(module.getCode());
1752:
1753:                    // for an enter issue template, do not give issue id
1754:                    // set id count to -1 so does not show up as an issue
1755:                    if (isTemplate()) {
1756:                        setIdCount(-1);
1757:                    } else {
1758:                        try {
1759:                            final int suggestedID = getIdCount();
1760:                            if (suggestedID != 0) {
1761:                                // Force the next available issue ID to be the
1762:                                // nominated value, if not out of sequence.
1763:                                // TODO: assert that this issue doesn't already exist
1764:                                // In this case, just skip the next action.
1765:                                setNextIssueId(dbCon, suggestedID);
1766:                            }
1767:
1768:                            // Set the ID to the next available value.
1769:                            setIdCount(getNextIssueId(dbCon));
1770:                        } catch (Exception e) {
1771:                            throw new TorqueException(e); //EXCEPTION
1772:                        }
1773:                    }
1774:                }
1775:                super .save(dbCon);
1776:            }
1777:
1778:            /* Gets the next available issue ID.
1779:             * If the ID table doesn't yet exist, it is created.
1780:             */
1781:            private int getNextIssueId(Connection con) throws TorqueException,
1782:                    ScarabException {
1783:                int id = -1;
1784:                String key = getIdTableKey();
1785:                DatabaseMap dbMap = IssuePeer.getTableMap().getDatabaseMap();
1786:                IDBroker idbroker = dbMap.getIDBroker();
1787:                try {
1788:                    id = idbroker.getIdAsInt(con, key);
1789:                } catch (Exception e) {
1790:                    synchronized (idbroker) {
1791:                        try {
1792:                            id = idbroker.getIdAsInt(con, key);
1793:                        } catch (Exception idRetrievalErr) {
1794:                            // a module code entry in the id_table was likely not 
1795:                            // entered, insert a row into the id_table and try again.
1796:                            try {
1797:                                saveIdTableKey(con, 2);
1798:                                id = 1;
1799:                            } catch (Exception badException) {
1800:                                getLog()
1801:                                        .error(
1802:                                                "Could not get an id, even after "
1803:                                                        + "trying to add a module entry into the ID_TABLE",
1804:                                                e);
1805:                                getLog()
1806:                                        .error(
1807:                                                "Error trying to create ID_TABLE entry for "
1808:                                                        + getIdTableKey(),
1809:                                                badException);
1810:                                // throw the original
1811:                                throw new ScarabException(
1812:                                        L10NKeySet.ExceptionRetrievingIssueId,
1813:                                        badException);
1814:                            }
1815:                        }
1816:                    }
1817:                }
1818:                return id;
1819:            }
1820:
1821:            /*
1822:             * Sets the next available issue ID to the given value
1823:             * If the ID table doesn't yet exist, it is created.
1824:             */
1825:            private void setNextIssueId(Connection con, int newID)
1826:                    throws TorqueException, ScarabException {
1827:                String key = getIdTableKey();
1828:                DatabaseMap dbMap = IssuePeer.getTableMap().getDatabaseMap();
1829:                IDBroker idbroker = dbMap.getIDBroker();
1830:                int nextID = 1;
1831:
1832:                synchronized (idbroker) {
1833:                    try {
1834:                        // Check if the ID table is available and get the next ID
1835:                        nextID = idbroker.getIdAsInt(con, key);
1836:                    } catch (Exception idRetrievalErr) {
1837:                        // No, create the ID table now
1838:                        saveIdTableKey(con, nextID);
1839:                    }
1840:
1841:                    if (nextID > newID) {
1842:                        getLog()
1843:                                .error(
1844:                                        "New issue ID "
1845:                                                + newID
1846:                                                + "is out of sequence. Must be at least "
1847:                                                + nextID);
1848:                    } else {
1849:                        try {
1850:                            // Now set the next available ID in the table
1851:                            setIdTableKey(con, newID);
1852:                        } catch (Exception badException) {
1853:                            getLog().error(
1854:                                    "Error creating ID_TABLE entry for "
1855:                                            + getIdTableKey(), badException);
1856:                            // throw the original
1857:                            throw new ScarabException(
1858:                                    L10NKeySet.ExceptionRetrievingIssueId,
1859:                                    badException);
1860:                        }
1861:                    }
1862:                }
1863:            }
1864:
1865:            private String getIdTableKey() throws TorqueException {
1866:                Module module = getModule();
1867:                String prefix = module.getCode();
1868:
1869:                String domain = module.getScarabInstanceId();
1870:                if (domain != null && domain.length() > 0) {
1871:                    prefix = domain + "-" + prefix;
1872:                }
1873:                return prefix;
1874:            }
1875:
1876:            private void saveIdTableKey(final Connection dbCon, final int nextID)
1877:                    throws TorqueException {
1878:                int id = 0;
1879:                final DatabaseMap dbMap = IssuePeer.getTableMap()
1880:                        .getDatabaseMap();
1881:                final IDBroker idbroker = dbMap.getIDBroker();
1882:                final String idTable = IDBroker.TABLE_NAME.substring(0,
1883:                        IDBroker.TABLE_NAME.indexOf('.'));
1884:                try {
1885:                    id = idbroker.getIdAsInt(dbCon, idTable);
1886:                } catch (Exception e) {
1887:                    Log.get(getClass().getName()).error(e);
1888:                    throw new TorqueException(e);
1889:                }
1890:
1891:                final String key = getIdTableKey();
1892:
1893:                // FIXME: UGLY! IDBroker doesn't have a Peer yet.
1894:                final String sql = "insert into " + idTable
1895:                        + " (ID_TABLE_ID,TABLE_NAME,NEXT_ID,QUANTITY) "
1896:                        + " VALUES (" + id + ",'" + key + "'," + nextID + ",1)";
1897:                BasePeer.executeStatement(sql, dbCon);
1898:            }
1899:
1900:            /*
1901:             * Sets the next available ID to the given ID
1902:             */
1903:            private void setIdTableKey(final Connection dbCon, int id)
1904:                    throws TorqueException {
1905:                final String key = getIdTableKey();
1906:
1907:                // FIXME: UGLY! IDBroker doesn't have a Peer yet.
1908:                final String sql = "update ID_TABLE set NEXT_ID=" + id
1909:                        + " where TABLE_NAME='" + key + "'";
1910:                BasePeer.executeStatement(sql, dbCon);
1911:            }
1912:
1913:            /**
1914:             * Returns list of issue template types.
1915:            public List getTemplateTypes() throws TorqueException
1916:            {
1917:                List result = null;
1918:                Object obj = ScarabCache.get(this, GET_TEMPLATE_TYPES); 
1919:                if (obj == null) 
1920:                {        
1921:                    Criteria crit = new Criteria()
1922:                        .add(IssueTypePeer.ISSUE_TYPE_ID, 
1923:                             IssueType.ISSUE__PK, Criteria.NOT_EQUAL);
1924:                    result = IssueTypePeer.doSelect(crit);
1925:                    ScarabCache.put(result, this, GET_TEMPLATE_TYPES);
1926:                }
1927:                else 
1928:                {
1929:                    result = (List)obj;
1930:                }
1931:                return result;
1932:            }
1933:             */
1934:
1935:            /**
1936:             * Get IssueTemplateInfo by Issue Id.
1937:             */
1938:            public IssueTemplateInfo getTemplateInfo() throws TorqueException {
1939:                IssueTemplateInfo result = null;
1940:                Object obj = ScarabCache.get(this , GET_TEMPLATEINFO);
1941:                if (obj == null) {
1942:                    Criteria crit = new Criteria(1);
1943:                    crit.add(IssueTemplateInfoPeer.ISSUE_ID, getIssueId());
1944:                    result = (IssueTemplateInfo) IssueTemplateInfoPeer
1945:                            .doSelect(crit).get(0);
1946:                    ScarabCache.put(result, this , GET_TEMPLATEINFO);
1947:                } else {
1948:                    result = (IssueTemplateInfo) obj;
1949:                }
1950:                return result;
1951:            }
1952:
1953:            /**
1954:             *  Get Unset required attributes in destination module / issue type.
1955:             */
1956:            public List getUnsetRequiredAttrs(Module newModule,
1957:                    IssueType newIssueType) throws TorqueException {
1958:                List attrs = new ArrayList();
1959:                if (!getIssueType().getIssueTypeId().equals(
1960:                        newIssueType.getIssueTypeId())
1961:                        || !getModule().getModuleId().equals(
1962:                                newModule.getModuleId())) {
1963:                    List requiredAttributes = newIssueType
1964:                            .getRequiredAttributes(newModule);
1965:                    Map attrValues = getAttributeValuesMap();
1966:
1967:                    for (Iterator i = requiredAttributes.iterator(); i
1968:                            .hasNext();) {
1969:                        Attribute attr = (Attribute) i.next();
1970:                        if (!attrValues.containsKey(attr.getName()
1971:                                .toUpperCase())) {
1972:                            attrs.add(attr);
1973:                        }
1974:                    }
1975:                }
1976:                return attrs;
1977:            }
1978:
1979:            /**
1980:             * Checks if the 'nonmatching' list contains the given 'value', treating the
1981:             * UserAttributes as an special case, in which the UserName is used to make
1982:             * the comparison.
1983:             * @param nonmatching
1984:             * @param value
1985:             * @return
1986:             */
1987:            private boolean isNonMatchingAttribute(List nonmatching,
1988:                    AttributeValue value) {
1989:                boolean bRdo = false;
1990:                if (value instanceof  UserAttribute) {
1991:                    for (Iterator it = nonmatching.iterator(); !bRdo
1992:                            && it.hasNext();) {
1993:                        Object attr = it.next();
1994:                        if (attr instanceof  UserAttribute) {
1995:                            UserAttribute userAttr = (UserAttribute) attr;
1996:                            bRdo = userAttr.getUserName().equals(
1997:                                    ((UserAttribute) value).getUserName());
1998:                        } else {
1999:                            Log
2000:                                    .get()
2001:                                    .warn(
2002:                                            "in Issue.isNonMatchingAttribute: encountered an attribute of type ["
2003:                                                    + attr.getClass().getName()
2004:                                                    + "] in the list of nonmatching objects");
2005:                            Log.get().warn(
2006:                                    "This is not a UserAttribute.(ignored)");
2007:                        }
2008:                    }
2009:                } else {
2010:                    bRdo = nonmatching.contains(value);
2011:                }
2012:                return bRdo;
2013:            }
2014:
2015:            /**
2016:             *  Move or copy issue to destination module.
2017:             */
2018:            public Issue move(final Module newModule,
2019:                    final IssueType newIssueType, final String action,
2020:                    final ScarabUser user, final String reason,
2021:                    final List commentAttrs, final List commentUserValues)
2022:                    throws TorqueException, ScarabException {
2023:                Issue newIssue;
2024:
2025:                final Attachment attachment = new Attachment();
2026:
2027:                // If moving to a new issue type, just change the issue type id
2028:                // otherwise, create fresh issue
2029:                if (getModule().getModuleId().equals(newModule.getModuleId())
2030:                        && !getIssueType().getIssueTypeId().equals(
2031:                                newIssueType.getIssueTypeId())
2032:                        && action.equals("move")) {
2033:                    newIssue = this ;
2034:                    newIssue.setIssueType(newIssueType);
2035:                } else {
2036:                    newIssue = newModule.getNewIssue(newIssueType);
2037:                }
2038:                newIssue.save();
2039:
2040:                if (newIssue != this ) {
2041:                    // If moving issue to new module, delete original
2042:                    if (action.equals("move")) {
2043:                        setMoved(true);
2044:                        save();
2045:                    }
2046:
2047:                    ActivitySet createActivitySet = ActivitySetManager
2048:                            .getInstance(ActivitySetTypePeer.CREATE_ISSUE__PK,
2049:                                    getCreatedBy());
2050:                    createActivitySet.setCreatedDate(getCreatedDate());
2051:                    createActivitySet.save();
2052:                    newIssue.setCreatedTransId(createActivitySet
2053:                            .getActivitySetId());
2054:                    newIssue.save();
2055:
2056:                    // Adjust dependencies if its a new issue id
2057:                    // (i.e.. moved to new module)
2058:                    final List children = getChildren();
2059:                    for (Iterator i = children.iterator(); i.hasNext();) {
2060:                        Depend depend = (Depend) i.next();
2061:                        if (action.equals("move")) {
2062:                            doDeleteDependency(null, depend, user);
2063:                        }
2064:                        final Issue child = IssueManager.getInstance(depend
2065:                                .getObserverId());
2066:                        final Depend newDepend = new Depend();
2067:                        newDepend.setObserverId(child.getIssueId());
2068:                        newDepend.setObservedId(newIssue.getIssueId());
2069:                        newDepend.setTypeId(depend.getTypeId());
2070:                        newIssue.doAddDependency(null, newDepend, child, user);
2071:                    }
2072:                    final List parents = getParents();
2073:                    for (Iterator j = parents.iterator(); j.hasNext();) {
2074:                        final Depend depend = (Depend) j.next();
2075:                        if (action.equals("move")) {
2076:                            doDeleteDependency(null, depend, user);
2077:                        }
2078:                        final Issue parent = IssueManager.getInstance(depend
2079:                                .getObservedId());
2080:                        final Depend newDepend = new Depend();
2081:                        newDepend.setObserverId(newIssue.getIssueId());
2082:                        newDepend.setObservedId(parent.getIssueId());
2083:                        newDepend.setTypeId(depend.getTypeId());
2084:                        parent.doAddDependency(null, newDepend, newIssue, user);
2085:                    }
2086:
2087:                    // copy attachments: comments/files etc.
2088:                    final Iterator attachments = getAttachments().iterator();
2089:                    while (attachments.hasNext()) {
2090:                        final Attachment oldA = (Attachment) attachments.next();
2091:                        final Attachment newA = oldA.copy();
2092:                        newA.setIssueId(newIssue.getIssueId());
2093:                        newA.save();
2094:                        final Activity oldAct = oldA.getActivity();
2095:                        if (oldAct != null) {
2096:                            final ActivitySet activitySet = newIssue
2097:                                    .attachActivitySet(null, user);
2098:                            ActivityManager.createTextActivity(newIssue,
2099:                                    activitySet, ActivityType
2100:                                            .getActivityType(oldA.getActivity()
2101:                                                    .getActivityType()), newA);
2102:                        }
2103:                        if (Attachment.FILE__PK.equals(newA.getTypeId())) {
2104:                            try {
2105:                                oldA.copyFileTo(newA.getFullPath());
2106:                            } catch (Exception ex) {
2107:                                throw new ScarabException(
2108:                                        L10NKeySet.ExceptionGeneral, ex);
2109:                            }
2110:                        }
2111:                    }
2112:
2113:                    // Copy over activity sets for the source issue's previous
2114:                    // Transactions
2115:                    final List activitySets = getActivitySets();
2116:                    final List nonMatchingAttributes = getNonMatchingAttributeValuesList(
2117:                            newModule, newIssueType);
2118:                    final List alreadyAssociatedUsers = new ArrayList();
2119:                    for (Iterator i = activitySets.iterator(); i.hasNext();) {
2120:                        final ActivitySet as = (ActivitySet) i.next();
2121:                        ActivitySet newAS = null;
2122:                        Attachment newAtt = null;
2123:                        // If activity set has an attachment, make a copy for new issue
2124:                        if (as.getAttachmentId() != null) {
2125:                            newAtt = as.getAttachment().copy();
2126:                            newAtt.save();
2127:                        }
2128:                        // Copy over activities with sets
2129:                        final List activities = as
2130:                                .getActivityListForIssue(this );
2131:                        for (Iterator j = activities.iterator(); j.hasNext();) {
2132:                            final Activity a = (Activity) j.next();
2133:                            // Only copy transactions that are records of previous move/copies
2134:                            // Or transactions relating to attributes.
2135:                            // Other transactions (attachments, dependencies)
2136:                            // Will be saved when attachments and dependencies are copied
2137:                            if (as.getTypeId().equals(
2138:                                    (ActivitySetTypePeer.MOVE_ISSUE__PK))
2139:                                    || !a.getAttributeId().equals(
2140:                                            new Integer("0"))) {
2141:                                newAS = new ActivitySet();
2142:                                newAS.setTypeId(as.getTypeId());
2143:                                if (newAtt != null) {
2144:                                    newAS.setAttachmentId(newAtt
2145:                                            .getAttachmentId());
2146:                                }
2147:                                newAS.setCreatedBy(as.getCreatedBy());
2148:                                newAS.setCreatedDate(as.getCreatedDate());
2149:                                newAS.save();
2150:
2151:                                // iterate over and copy transaction's activities
2152:                                final Activity newA = a.copy(newIssue, newAS);
2153:                                newIssue.getActivity(true).add(newA);
2154:
2155:                                // If this is an activity relating to setting an attribute value
2156:                                // And the final value is in the issue right now, we'll copy
2157:                                // over the attribute value
2158:                                final AttributeValue attVal = getAttributeValueWithValue(
2159:                                        a.getAttribute(), a.getNewValue(), a
2160:                                                .getNewNumericValue());
2161:                                if (a.getEndDate() == null && attVal != null) {
2162:                                    final List values = getAttributeValues(a
2163:                                            .getAttribute());
2164:                                    for (Iterator it = values.iterator(); it
2165:                                            .hasNext();) {
2166:                                        final AttributeValue att = (AttributeValue) it
2167:                                                .next();
2168:                                        // Only copy if the target artifact type contains this
2169:                                        // Attribute
2170:                                        if (attVal != null
2171:                                                && !isNonMatchingAttribute(
2172:                                                        nonMatchingAttributes,
2173:                                                        att)) {
2174:                                            final boolean isUser = (att instanceof  UserAttribute);
2175:                                            if (!isUser
2176:                                                    || !alreadyAssociatedUsers
2177:                                                            .contains(((UserAttribute) att)
2178:                                                                    .getUserName()
2179:                                                                    + att
2180:                                                                            .getAttribute()
2181:                                                                            .getName())) {
2182:                                                final AttributeValue newAttVal = att
2183:                                                        .copy();
2184:                                                newAttVal.setIssueId(newIssue
2185:                                                        .getIssueId());
2186:                                                newAttVal.setActivity(newA);
2187:                                                newAttVal
2188:                                                        .startActivitySet(newAS);
2189:                                                newAttVal.save();
2190:                                                if (isUser) {
2191:                                                    alreadyAssociatedUsers
2192:                                                            .add(((UserAttribute) att)
2193:                                                                    .getUserName()
2194:                                                                    + att
2195:                                                                            .getAttribute()
2196:                                                                            .getName());
2197:                                                }
2198:                                            }
2199:                                        }
2200:                                    }
2201:                                }
2202:                            }
2203:                        }
2204:                    }
2205:                }
2206:
2207:                // Generate comment to deal with attributes that do not
2208:                // Exist in destination module, as well as the user attributes.
2209:                final StringBuffer attachmentBuf = new StringBuffer();
2210:                final StringBuffer delAttrsBuf = new StringBuffer();
2211:                if (reason != null && reason.length() > 0) {
2212:                    attachmentBuf.append(reason).append(". ");
2213:                }
2214:                if (commentAttrs.size() > 0 || commentUserValues.size() > 0) {
2215:                    attachmentBuf.append(Localization.format(
2216:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2217:                            "DidNotCopyAttributes", newIssueType.getName()
2218:                                    + "/" + newModule.getName()));
2219:                    attachmentBuf.append("\n");
2220:                    for (int i = 0; i < commentAttrs.size(); i++) {
2221:                        final List attVals = getAttributeValues((Attribute) commentAttrs
2222:                                .get(i));
2223:                        for (int j = 0; j < attVals.size(); j++) {
2224:                            final AttributeValue attVal = (AttributeValue) attVals
2225:                                    .get(j);
2226:                            String field = null;
2227:                            delAttrsBuf.append(attVal.getAttribute().getName());
2228:                            field = attVal.getValue();
2229:                            delAttrsBuf.append("=").append(field).append(". ")
2230:                                    .append("\n");
2231:                        }
2232:                    }
2233:                    for (int i = 0; i < commentUserValues.size(); i++) {
2234:                        final UserAttribute useratt = (UserAttribute) commentUserValues
2235:                                .get(i);
2236:                        delAttrsBuf.append(useratt.getAttribute().getName()
2237:                                + ": " + useratt.getUserName() + "\n");
2238:                    }
2239:                    final String delAttrs = delAttrsBuf.toString();
2240:                    attachmentBuf.append(delAttrs);
2241:
2242:                    // Also create a regular comment with non-matching attribute info
2243:                    final Attachment comment = new Attachment();
2244:                    comment.setTextFields(user, newIssue,
2245:                            Attachment.COMMENT__PK);
2246:
2247:                    final Object[] args = {
2248:                            this .getUniqueId(),
2249:                            newIssueType.getName() + " / "
2250:                                    + newModule.getName() };
2251:                    final StringBuffer commentBuf = new StringBuffer(
2252:                            Localization.format(
2253:                                    ScarabConstants.DEFAULT_BUNDLE_NAME,
2254:                                    getLocale(),
2255:                                    "DidNotCopyAttributesFromArtifact", args));
2256:                    commentBuf.append("\n").append(delAttrs);
2257:                    comment.setData(commentBuf.toString());
2258:                    comment.setName(Localization.getString(
2259:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2260:                            "Comment"));
2261:                    comment.save();
2262:                } else {
2263:                    attachmentBuf.append(Localization.getString(
2264:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2265:                            "AllCopied"));
2266:                }
2267:                attachment.setData(attachmentBuf.toString());
2268:
2269:                if (action.equals("move")) {
2270:                    attachment.setName(Localization.getString(
2271:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2272:                            "MovedIssueNote"));
2273:                } else {
2274:                    attachment.setName(Localization.getString(
2275:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2276:                            "CopiedIssueNote"));
2277:                }
2278:                attachment.setTextFields(user, newIssue,
2279:                        Attachment.MODIFICATION__PK);
2280:                attachment.save();
2281:
2282:                // Create activitySet for the MoveIssue activity
2283:                final ActivitySet activitySet2 = newIssue.attachActivitySet(
2284:                        null, user, attachment,
2285:                        ActivitySetTypePeer.MOVE_ISSUE__PK);
2286:
2287:                // Save activity record
2288:                final Attribute zeroAttribute = AttributeManager
2289:                        .getInstance(NUMBERKEY_0);
2290:                ActivityManager.createTextActivity(newIssue, zeroAttribute,
2291:                        activitySet2, ActivityType.ISSUE_MOVED, getUniqueId(),
2292:                        newIssue.getUniqueId());
2293:
2294:                return newIssue;
2295:            }
2296:
2297:            public void addVote(ScarabUser user) throws ScarabException,
2298:                    Exception {
2299:                // check to see if the user has voted for this issue
2300:                int previousVotes = 0;
2301:                IssueVote issueVote = null;
2302:                Criteria crit = new Criteria().add(IssueVotePeer.ISSUE_ID,
2303:                        getIssueId()).add(IssueVotePeer.USER_ID,
2304:                        user.getUserId());
2305:                List votes = IssueVotePeer.doSelect(crit);
2306:                if (votes != null && votes.size() != 0) {
2307:                    issueVote = (IssueVote) votes.get(0);
2308:                    previousVotes = issueVote.getVotes();
2309:                } else {
2310:                    issueVote = new IssueVote();
2311:                    issueVote.setIssueId(getIssueId());
2312:                    issueVote.setUserId(user.getUserId());
2313:                }
2314:
2315:                // check if the module accepts multiple votes
2316:                if (!getModule().allowsMultipleVoting() && previousVotes > 0) {
2317:                    throw new ScarabException(
2318:                            L10NKeySet.ExceptionMultipleVoteForUnallowed, user
2319:                                    .getUserName(), getUniqueId());
2320:                }
2321:
2322:                // save the user's vote
2323:                issueVote.setVotes(previousVotes + 1);
2324:                issueVote.save();
2325:
2326:                // update the total votes for the issue
2327:                crit = new Criteria().add(AttributeValuePeer.ATTRIBUTE_ID,
2328:                        AttributePeer.TOTAL_VOTES__PK);
2329:                List voteValues = getAttributeValues(crit);
2330:                TotalVotesAttribute voteValue = null;
2331:                if (voteValues.size() == 0) {
2332:                    voteValue = new TotalVotesAttribute();
2333:                    voteValue.setIssue(this );
2334:                    voteValue.setAttributeId(AttributePeer.TOTAL_VOTES__PK);
2335:                } else {
2336:                    voteValue = (TotalVotesAttribute) voteValues.get(0);
2337:                }
2338:                // Updating attribute values requires a activitySet
2339:                ActivitySet activitySet = attachActivitySet(null, user, null,
2340:                        ActivitySetTypePeer.RETOTAL_ISSUE_VOTE__PK);
2341:                voteValue.startActivitySet(activitySet);
2342:                voteValue.addVote();
2343:                voteValue.save();
2344:            }
2345:
2346:            /**
2347:             * Gets a list of non-user AttributeValues which match a given Module.
2348:             * It is used in the MoveIssue2.vm template
2349:             */
2350:            public List getMatchingAttributeValuesList(Module newModule,
2351:                    IssueType newIssueType) throws TorqueException {
2352:                List matchingAttributes = new ArrayList();
2353:                Map setMap = this .getAttributeValuesMap();
2354:                for (Iterator iter = setMap.keySet().iterator(); iter.hasNext();) {
2355:                    AttributeValue aval = (AttributeValue) setMap.get(iter
2356:                            .next());
2357:                    List values = getAttributeValues(aval.getAttribute());
2358:                    // loop thru the values for this attribute
2359:                    for (int i = 0; i < values.size(); i++) {
2360:                        AttributeValue attVal = (AttributeValue) values.get(i);
2361:                        RModuleAttribute modAttr = newModule
2362:                                .getRModuleAttribute(aval.getAttribute(),
2363:                                        newIssueType);
2364:
2365:                        // If this attribute is active for the destination module,
2366:                        // Add to matching attributes list
2367:                        if (modAttr != null && modAttr.getActive()) {
2368:                            // If attribute is an option attribute,
2369:                            // Check if attribute option is active for destination module.
2370:                            if (aval instanceof  OptionAttribute) {
2371:                                // FIXME: Use select count
2372:                                Criteria crit2 = new Criteria(4).add(
2373:                                        RModuleOptionPeer.ACTIVE, true).add(
2374:                                        RModuleOptionPeer.OPTION_ID,
2375:                                        attVal.getOptionId()).add(
2376:                                        RModuleOptionPeer.MODULE_ID,
2377:                                        newModule.getModuleId()).add(
2378:                                        RModuleOptionPeer.ISSUE_TYPE_ID,
2379:                                        newIssueType.getIssueTypeId());
2380:                                List modOpt = RModuleOptionPeer.doSelect(crit2);
2381:
2382:                                if (!modOpt.isEmpty()) {
2383:                                    matchingAttributes.add(attVal);
2384:                                }
2385:                            } else if (attVal instanceof  UserAttribute) {
2386:                                ScarabUser user = null;
2387:                                try {
2388:                                    user = ScarabUserManager.getInstance(attVal
2389:                                            .getUserId());
2390:                                } catch (Exception e) {
2391:                                    getLog().error(e);
2392:                                    e.printStackTrace();
2393:                                }
2394:                                Attribute attr = attVal.getAttribute();
2395:                                ScarabUser[] userArray = newModule
2396:                                        .getUsers(attr.getPermission());
2397:                                // If user exists in destination module with this permission,
2398:                                // Add as matching value
2399:                                if (Arrays.asList(userArray).contains(user)) {
2400:                                    matchingAttributes.add(attVal);
2401:                                }
2402:                            } else {
2403:                                matchingAttributes.add(attVal);
2404:                            }
2405:                        }
2406:                    }
2407:                }
2408:                return matchingAttributes;
2409:            }
2410:
2411:            public List getMatchingAttributeValuesList(String moduleId,
2412:                    String issueTypeId) throws TorqueException {
2413:                Module module = ModuleManager
2414:                        .getInstance(new Integer(moduleId));
2415:                IssueType issueType = IssueTypeManager.getInstance(new Integer(
2416:                        issueTypeId));
2417:                return getMatchingAttributeValuesList(module, issueType);
2418:            }
2419:
2420:            /**
2421:             * Gets a list AttributeValues which the source module has,
2422:             * But the destination module does not have, when doing a copy.
2423:             * It is used in the MoveIssue2.vm template
2424:             */
2425:            public List getNonMatchingAttributeValuesList(Module newModule,
2426:                    IssueType newIssueType) throws TorqueException {
2427:                List nonMatchingAttributes = new ArrayList();
2428:                AttributeValue aval = null;
2429:
2430:                Map setMap = this .getAttributeValuesMap();
2431:                for (Iterator iter = setMap.values().iterator(); iter.hasNext();) {
2432:                    aval = (AttributeValue) iter.next();
2433:                    List values = getAttributeValues(aval.getAttribute());
2434:                    // loop thru the values for this attribute
2435:                    for (Iterator i = values.iterator(); i.hasNext();) {
2436:                        AttributeValue attVal = (AttributeValue) i.next();
2437:                        RModuleAttribute modAttr = newModule
2438:                                .getRModuleAttribute(aval.getAttribute(),
2439:                                        newIssueType);
2440:
2441:                        // If this attribute is not active for the destination module,
2442:                        // Add to nonMatchingAttributes list
2443:                        if (modAttr == null || !modAttr.getActive()) {
2444:                            nonMatchingAttributes.add(attVal);
2445:                        } else {
2446:                            // If attribute is an option attribute, Check if
2447:                            // attribute option is active for destination module.
2448:                            if (attVal instanceof  OptionAttribute) {
2449:                                Criteria crit2 = new Criteria(1).add(
2450:                                        RModuleOptionPeer.ACTIVE, true).add(
2451:                                        RModuleOptionPeer.OPTION_ID,
2452:                                        attVal.getOptionId()).add(
2453:                                        RModuleOptionPeer.MODULE_ID,
2454:                                        newModule.getModuleId()).add(
2455:                                        RModuleOptionPeer.ISSUE_TYPE_ID,
2456:                                        newIssueType.getIssueTypeId());
2457:                                List modOpt = RModuleOptionPeer.doSelect(crit2);
2458:
2459:                                if (modOpt.isEmpty()) {
2460:                                    nonMatchingAttributes.add(attVal);
2461:                                }
2462:                            } else if (attVal instanceof  UserAttribute) {
2463:                                ScarabUser user = null;
2464:                                try {
2465:                                    user = ScarabUserManager.getInstance(attVal
2466:                                            .getUserId());
2467:                                } catch (Exception e) {
2468:                                    Log.get().error(
2469:                                            "Unable to retrieve user for "
2470:                                                    + "attribute", e);
2471:                                }
2472:                                Attribute attr = attVal.getAttribute();
2473:                                ScarabUser[] userArray = newModule
2474:                                        .getUsers(attr.getPermission());
2475:                                // If user exists in destination module with
2476:                                // this permission, add as matching value.
2477:                                if (!Arrays.asList(userArray).contains(user)) {
2478:                                    nonMatchingAttributes.add(attVal);
2479:                                }
2480:                            }
2481:                        }
2482:                    }
2483:                }
2484:                return nonMatchingAttributes;
2485:            }
2486:
2487:            public List getNonMatchingAttributeValuesList(String moduleId,
2488:                    String issueTypeId) throws TorqueException {
2489:                Module module = ModuleManager
2490:                        .getInstance(new Integer(moduleId));
2491:                IssueType issueType = IssueTypeManager.getInstance(new Integer(
2492:                        issueTypeId));
2493:                return getNonMatchingAttributeValuesList(module, issueType);
2494:            }
2495:
2496:            /**
2497:             * Checks if user has permission to delete issue template.
2498:             * Only the creating user can delete a personal template.
2499:             * Only project owner or admin can delete a project-wide template.
2500:             */
2501:            public void deleteItem(ScarabUser user) throws TorqueException,
2502:                    ScarabException {
2503:                Module module = getModule();
2504:                if (user.hasPermission(ScarabSecurity.ITEM__DELETE, module)
2505:                        || (user.getUserId().equals(getCreatedBy().getUserId()) && isTemplate())) {
2506:                    setDeleted(true);
2507:                    save();
2508:                } else {
2509:                    throw new ScarabException(
2510:                            L10NKeySet.YouDoNotHavePermissionToAction);
2511:                }
2512:            }
2513:
2514:            /**
2515:             * If the user has permission to delete ISSUES in this module,
2516:             * it wil mark this issue as DELETED.
2517:             * @param user
2518:             * @throws Exception
2519:             * @throws ScarabException
2520:             */
2521:            public void deleteIssue(ScarabUser user) throws Exception,
2522:                    ScarabException {
2523:                if (user.hasPermission(ScarabSecurity.ISSUE__DELETE, this 
2524:                        .getModule())) {
2525:                    ActivitySet activitySet = attachActivitySet(null, user);
2526:                    ActivityManager
2527:                            .createDeleteIssueActivity(this , activitySet);
2528:                    this .setDeleted(true);
2529:                    List dependencies = this .getDependsRelatedByObservedId();
2530:                    dependencies.addAll(this .getDependsRelatedByObserverId());
2531:                    for (Iterator it = dependencies.iterator(); it.hasNext();) {
2532:                        Depend depend = (Depend) it.next();
2533:                        ActivitySet deleteSet = this .doDeleteDependency(
2534:                                activitySet, depend, user);
2535:                        for (Iterator act = deleteSet.getActivityList()
2536:                                .iterator(); act.hasNext();) {
2537:                            activitySet.addActivity((Activity) act.next());
2538:                        }
2539:                        NotificationManagerFactory.getInstance()
2540:                                .addActivityNotification(
2541:                                        ActivityType.ISSUE_DELETED,
2542:                                        activitySet, this , user);
2543:                    }
2544:                    save();
2545:                }
2546:            }
2547:
2548:            /**
2549:             * This method will return the AttributeValue which represents the default
2550:             * text attribute.
2551:             * 
2552:             * @return the AttributeValue to use as the email subject, or null
2553:             *         if no suitable AttributeValue could be found.
2554:             */
2555:            public AttributeValue getDefaultTextAttributeValue()
2556:                    throws TorqueException {
2557:                AttributeValue result = null;
2558:                Object obj = ScarabCache.get(this ,
2559:                        GET_DEFAULT_TEXT_ATTRIBUTEVALUE);
2560:                if (obj == null) {
2561:                    Attribute defaultTextAttribute = getIssueType()
2562:                            .getDefaultTextAttribute(getModule());
2563:                    if (defaultTextAttribute != null) {
2564:                        result = getAttributeValue(defaultTextAttribute);
2565:                    }
2566:                    ScarabCache.put(result, this ,
2567:                            GET_DEFAULT_TEXT_ATTRIBUTEVALUE);
2568:                } else {
2569:                    result = (AttributeValue) obj;
2570:                }
2571:                return result;
2572:            }
2573:
2574:            /**
2575:             * This calls getDefaultTextAttributeValue() and then returns the
2576:             * String value of the Attribute. This method is used to get the
2577:             * subject of an email. if no text attribute value is found it
2578:             * will use the first ActivitySet comment.
2579:             */
2580:            public String getDefaultText() throws TorqueException {
2581:                String result = null;
2582:                Object obj = ScarabCache.get(this , GET_DEFAULT_TEXT);
2583:                if (obj == null) {
2584:                    AttributeValue emailAV = getDefaultTextAttributeValue();
2585:                    if (emailAV != null) {
2586:                        result = emailAV.getValue();
2587:                    }
2588:                    if (result == null) {
2589:                        ActivitySet activitySet = getInitialActivitySet();
2590:                        if (activitySet != null) {
2591:                            Attachment reason = activitySet.getAttachment();
2592:                            if (reason != null && reason.getData() != null
2593:                                    && reason.getData().trim().length() > 0) {
2594:                                result = reason.getData();
2595:                            }
2596:                        }
2597:                    }
2598:                    result = (result == null) ? Localization.getString(
2599:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2600:                            "NoIssueSummaryAvailable") : result;
2601:                    ScarabCache.put(result, this , GET_DEFAULT_TEXT);
2602:                } else {
2603:                    result = (String) obj;
2604:                }
2605:                return result;
2606:            }
2607:
2608:            private MethodResultCache getMethodResult() {
2609:                return IssueManager.getMethodResult();
2610:            }
2611:
2612:            /**
2613:             * gets an object from the appropriate cache, based on whether this is
2614:             * a saved issue.  if you know the object should only be in ScarabCache
2615:             * do not use this method.
2616:             */
2617:            private Object getCachedObject(String methodName) {
2618:                Object obj = null;
2619:                // Cache Note:
2620:                // we check for issue id, so that we only (JCS) cache for saved issues
2621:                // if we decide to cache results for new issues we should replace
2622:                // this conditional with (this instanceof IssueSearch) because
2623:                // we definitely do not want to cache those.
2624:                if (getIssueId() == null) {
2625:                    obj = ScarabCache.get(this , methodName);
2626:                } else {
2627:                    obj = getMethodResult().get(this , methodName);
2628:                }
2629:                return obj;
2630:            }
2631:
2632:            /**
2633:             * puts an object into the appropriate cache, based on whether this is
2634:             * a saved issue.  if you know the object should only be in ScarabCache
2635:             * do not use this method.
2636:             */
2637:            private void putCachedObject(Object obj, String methodName) {
2638:                // see Cache Note above
2639:                if (getIssueId() == null) {
2640:                    ScarabCache.put(obj, this , methodName);
2641:                } else {
2642:                    getMethodResult().put(obj, this , methodName);
2643:                }
2644:            }
2645:
2646:            /**
2647:             * gets an object from the appropriate cache, based on whether this is
2648:             * a saved issue.  if you know the object should only be in ScarabCache
2649:             * do not use this method.
2650:             */
2651:            private Object getCachedObject(String methodName, Serializable arg1) {
2652:                Object obj = null;
2653:                // Cache Note:
2654:                // we check for issue id, so that we only (JCS) cache for saved issues
2655:                // if we decide to cache results for new issues we should replace
2656:                // this conditional with (this instanceof IssueSearch) because
2657:                // we definitely do not want to cache those.
2658:                if (getIssueId() == null) {
2659:                    obj = ScarabCache.get(this , methodName, arg1);
2660:                } else {
2661:                    obj = getMethodResult().get(this , methodName, arg1);
2662:                }
2663:                return obj;
2664:            }
2665:
2666:            /**
2667:             * puts an object into the appropriate cache, based on whether this is
2668:             * a saved issue.  if you know the object should only be in ScarabCache
2669:             * do not use this method.
2670:             */
2671:            private void putCachedObject(Object obj, String methodName,
2672:                    Serializable arg1) {
2673:                // see Cache Note above
2674:                if (getIssueId() == null) {
2675:                    ScarabCache.put(obj, this , methodName, arg1);
2676:                } else {
2677:                    getMethodResult().put(obj, this , methodName, arg1);
2678:                }
2679:            }
2680:
2681:            /**
2682:             * gets an object from the appropriate cache, based on whether this is
2683:             * a saved issue.  if you know the object should only be in ScarabCache
2684:             * do not use this method.
2685:             */
2686:            private Object getCachedObject(String methodName,
2687:                    Serializable arg1, Serializable arg2) {
2688:                Object obj = null;
2689:                // Cache Note:
2690:                // we check for issue id, so that we only (JCS) cache for saved issues
2691:                // if we decide to cache results for new issues we should replace
2692:                // this conditional with (this instanceof IssueSearch) because
2693:                // we definitely do not want to cache those.
2694:                if (getIssueId() == null) {
2695:                    obj = ScarabCache.get(this , methodName, arg1, arg2);
2696:                } else {
2697:                    obj = getMethodResult().get(this , methodName, arg1, arg2);
2698:                }
2699:                return obj;
2700:            }
2701:
2702:            /**
2703:             * puts an object into the appropriate cache, based on whether this is
2704:             * a saved issue.  if you know the object should only be in ScarabCache
2705:             * do not use this method.
2706:             */
2707:            private void putCachedObject(Object obj, String methodName,
2708:                    Serializable arg1, Serializable arg2) {
2709:                // see Cache Note above
2710:                if (getIssueId() == null) {
2711:                    ScarabCache.put(obj, this , methodName, arg1, arg2);
2712:                } else {
2713:                    getMethodResult().put(obj, this , methodName, arg1, arg2);
2714:                }
2715:            }
2716:
2717:            // *******************************************************************
2718:            // Permissions methods - these are deprecated
2719:            // *******************************************************************
2720:
2721:            /**
2722:             * Checks if user has permission to enter issue.
2723:             * @deprecated user.hasPermission(ScarabSecurity.ISSUE__ENTER, module)
2724:             */
2725:            public boolean hasEnterPermission(ScarabUser user, Module module)
2726:                    throws TorqueException {
2727:                boolean hasPerm = false;
2728:
2729:                if (user.hasPermission(ScarabSecurity.ISSUE__ENTER, module)) {
2730:                    hasPerm = true;
2731:                }
2732:                return hasPerm;
2733:            }
2734:
2735:            /**
2736:             * Checks if user has permission to edit issue.
2737:             * @deprecated user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
2738:             */
2739:            public boolean hasEditPermission(ScarabUser user, Module module)
2740:                    throws TorqueException {
2741:                boolean hasPerm = false;
2742:
2743:                if (user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
2744:                        || user.equals(getCreatedBy())) {
2745:                    hasPerm = true;
2746:                }
2747:                return hasPerm;
2748:            }
2749:
2750:            /**
2751:             * Checks if user has permission to move issue to destination module.
2752:             * @deprecated user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
2753:             */
2754:            public boolean hasMovePermission(ScarabUser user, Module module)
2755:                    throws TorqueException {
2756:                boolean hasPerm = false;
2757:
2758:                if (user.hasPermission(ScarabSecurity.ISSUE__EDIT, module)
2759:                        || user.equals(getCreatedBy())) {
2760:                    hasPerm = true;
2761:                }
2762:                return hasPerm;
2763:            }
2764:
2765:            /**
2766:             * Assigns user to issue.
2767:             */
2768:            public ActivitySet assignUser(ActivitySet activitySet,
2769:                    final ScarabUser assignee, final ScarabUser assigner,
2770:                    final Attribute attribute, final Attachment attachment)
2771:                    throws TorqueException, ScarabException {
2772:                final UserAttribute attVal = new UserAttribute();
2773:
2774:                activitySet = attachActivitySet(activitySet, assigner,
2775:                        attachment);
2776:                attVal.startActivitySet(activitySet);
2777:
2778:                ActivityManager.createUserActivity(this , attribute,
2779:                        activitySet, null, null, assignee.getUserId());
2780:
2781:                // Save user attribute values
2782:                attVal.setIssue(this );
2783:                attVal.setAttributeId(attribute.getAttributeId());
2784:                attVal.setUserId(assignee.getUserId());
2785:                attVal.setValue(assignee.getUserName());
2786:                attVal.save();
2787:
2788:                return activitySet;
2789:            }
2790:
2791:            /**
2792:             * Used to change a user attribute value from one user attribute
2793:             * to a new one. 
2794:             */
2795:            public ActivitySet changeUserAttributeValue(
2796:                    ActivitySet activitySet, final ScarabUser assignee,
2797:                    final ScarabUser assigner, final AttributeValue oldAttVal,
2798:                    final Attribute newAttr, final Attachment attachment)
2799:                    throws TorqueException, ScarabException {
2800:                activitySet = attachActivitySet(activitySet, assigner,
2801:                        attachment);
2802:                oldAttVal.startActivitySet(activitySet);
2803:
2804:                // Save activity record for deletion of old assignment
2805:                ActivityManager.createUserActivity(this , oldAttVal
2806:                        .getAttribute(), activitySet, null, assignee
2807:                        .getUserId(), null);
2808:
2809:                // Save activity record for new assignment
2810:                ActivityManager.createUserActivity(this , newAttr, activitySet,
2811:                        null, null, assignee.getUserId());
2812:
2813:                // Save assignee value
2814:                oldAttVal.setAttributeId(newAttr.getAttributeId());
2815:                oldAttVal.save();
2816:
2817:                return activitySet;
2818:            }
2819:
2820:            /**
2821:             * Used to delete a user attribute value.
2822:             */
2823:            public ActivitySet deleteUser(ActivitySet activitySet,
2824:                    final ScarabUser assignee, final ScarabUser assigner,
2825:                    final AttributeValue attVal, final Attachment attachment)
2826:                    throws TorqueException, ScarabException {
2827:                activitySet = attachActivitySet(activitySet, assigner,
2828:                        attachment);
2829:                attVal.startActivitySet(activitySet);
2830:
2831:                // Save activity record
2832:                ActivityManager.createUserActivity(this , attVal.getAttribute(),
2833:                        activitySet, null, assignee.getUserId(), null);
2834:
2835:                // Save assignee value
2836:                attVal.setDeleted(true);
2837:                attVal.save();
2838:
2839:                return activitySet;
2840:            }
2841:
2842:            /**
2843:             * Deletes a specific dependency on this issue.
2844:             */
2845:            public ActivitySet doDeleteDependency(ActivitySet activitySet,
2846:                    Depend oldDepend, final ScarabUser user)
2847:                    throws TorqueException, ScarabException {
2848:                final Issue otherIssue = IssueManager.getInstance(oldDepend
2849:                        .getObserverId(), false);
2850:                /* XXX Why can a child not delete a dependency??
2851:                 if (otherIssue.equals(this))
2852:                 {
2853:                 throw new ScarabException("CannotDeleteDependency");
2854:                 }
2855:                 */
2856:                final Issue this Issue = IssueManager.getInstance(oldDepend
2857:                        .getObservedId(), false);
2858:
2859:                // get the original object so that we do an update
2860:                oldDepend = this Issue.getDependency(otherIssue);
2861:                oldDepend.setNew(false);
2862:                oldDepend.setDeleted(true);
2863:                oldDepend.save();
2864:
2865:                // need to null out the cache entry so that Issue.getDependency()
2866:                // does not try to return the item from the cache
2867:                ScarabCache.put(null, this Issue, GET_DEPENDENCY, otherIssue);
2868:
2869:                Attachment comment = oldDepend.getDescriptionAsAttachment(user,
2870:                        this Issue);
2871:
2872:                activitySet = this Issue.attachActivitySet(activitySet, user,
2873:                        comment);
2874:                activitySet = otherIssue.attachActivitySet(activitySet, user,
2875:                        comment);
2876:
2877:                ActivityManager.createDeleteDependencyActivity(this Issue,
2878:                        activitySet, oldDepend);
2879:                ActivityManager.createDeleteDependencyActivity(otherIssue,
2880:                        activitySet, oldDepend);
2881:
2882:                return activitySet;
2883:            }
2884:
2885:            /**
2886:             * Given a specific attachment object allow us to update
2887:             * the information in it. If the old matches the new, then
2888:             * nothing is modified.
2889:             */
2890:            public ActivitySet doChangeUrlDescription(ActivitySet activitySet,
2891:                    final ScarabUser user, final Attachment attachment,
2892:                    final String oldDescription) throws TorqueException,
2893:                    ScarabException {
2894:                final String newDescription = attachment.getName();
2895:                if (!oldDescription.equals(newDescription)) {
2896:                    final Object[] args = { oldDescription, newDescription, };
2897:                    String desc = Localization.format(
2898:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2899:                            "UrlDescChangedDesc", args);
2900:
2901:                    if (desc.length() > 248) {
2902:                        desc = desc.substring(0, 248) + "...";
2903:                    }
2904:                    activitySet = attachActivitySet(activitySet, user);
2905:                    ActivityManager.createTextActivity(this , activitySet,
2906:                            ActivityType.URL_DESC_CHANGED, attachment,
2907:                            oldDescription, newDescription);
2908:                    NotificationManagerFactory.getInstance()
2909:                            .addActivityNotification(
2910:                                    ActivityType.URL_DESC_CHANGED, activitySet,
2911:                                    this , user);
2912:                }
2913:                return activitySet;
2914:            }
2915:
2916:            /**
2917:             * Given a specific attachment object allow us to update
2918:             * the information in it. If the old matches the new, then
2919:             * nothing is modified.
2920:             */
2921:            public ActivitySet doChangeUrlUrl(ActivitySet activitySet,
2922:                    final ScarabUser user, final Attachment attachment,
2923:                    final String oldUrl) throws TorqueException,
2924:                    ScarabException {
2925:                final String newUrl = attachment.getData();
2926:                if (!oldUrl.equals(newUrl)) {
2927:                    final Object[] args = { oldUrl, newUrl };
2928:                    String desc = Localization.format(
2929:                            ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
2930:                            "UrlChangedDesc", args);
2931:
2932:                    if (desc.length() > 248) {
2933:                        desc = desc.substring(0, 248) + "...";
2934:                    }
2935:                    activitySet = attachActivitySet(activitySet, user);
2936:
2937:                    // Save activity record
2938:                    ActivityManager.createTextActivity(this , activitySet,
2939:                            ActivityType.URL_CHANGED, attachment, oldUrl,
2940:                            newUrl);
2941:
2942:                    NotificationManagerFactory.getInstance()
2943:                            .addActivityNotification(ActivityType.URL_CHANGED,
2944:                                    activitySet, this , user);
2945:                }
2946:                return activitySet;
2947:            }
2948:
2949:            /**
2950:             * changes the dependency type as well as. will not change deptype
2951:             * for deleted deps
2952:             */
2953:            public ActivitySet doChangeDependencyType(ActivitySet activitySet,
2954:                    final Depend oldDepend, final Depend newDepend,
2955:                    final ScarabUser user) throws TorqueException,
2956:                    ScarabException {
2957:                final String oldName = oldDepend.getDependType().getName();
2958:                final String newName = newDepend.getDependType().getName();
2959:
2960:                final boolean rolesHaveSwitched = (oldDepend.getObserverId()
2961:                        .equals(newDepend.getObservedId()) && oldDepend
2962:                        .getObservedId().equals(newDepend.getObserverId()));
2963:                final boolean typeHasChanged = (!newName.equals(oldName));
2964:
2965:                final boolean isActive = !newDepend.getDeleted();
2966:
2967:                // check to see if something changed
2968:                // only change dependency type for non-deleted deps
2969:                if (isActive && (rolesHaveSwitched || typeHasChanged)) {
2970:                    final Issue otherIssue = IssueManager.getInstance(newDepend
2971:                            .getObservedId(), false);
2972:
2973:                    // always delete an old dependency
2974:                    oldDepend.setDeleted(true);
2975:                    oldDepend.save();
2976:                    // always create a new dependency
2977:                    newDepend.setNew(true);
2978:                    newDepend.save();
2979:
2980:                    // need to null out the cache entry so that Issue.getDependency()
2981:                    // does not try to return the item from the cache
2982:                    ScarabCache.put(null, this , GET_DEPENDENCY, otherIssue);
2983:
2984:                    final Attachment comment = newDepend
2985:                            .getDescriptionAsAttachment(user, this );
2986:
2987:                    activitySet = attachActivitySet(activitySet, user, comment);
2988:                    activitySet = otherIssue.attachActivitySet(activitySet,
2989:                            user, comment);
2990:
2991:                    ActivityManager.createChangeDependencyActivity(this ,
2992:                            activitySet, newDepend, oldName, newName);
2993:                    ActivityManager.createChangeDependencyActivity(otherIssue,
2994:                            activitySet, newDepend, oldName, newName);
2995:                }
2996:                return activitySet;
2997:            }
2998:
2999:            /**
3000:             * Sets original AttributeValues for an new issue based on a hashmap of values
3001:             * This is data is saved to the database and the proper ActivitySet is 
3002:             * also recorded.
3003:             *
3004:             * @throws TorqueException when the workflow has an error to report
3005:             */
3006:            public ActivitySet setInitialAttributeValues(
3007:                    ActivitySet activitySet, Attachment attachment,
3008:                    final HashMap newValues, final ScarabUser user)
3009:                    throws TorqueException, ScarabException {
3010:                // Check new values for workflow
3011:                final String msg = doCheckInitialAttributeValueWorkflow(
3012:                        newValues, user);
3013:                if (msg != null) {
3014:                    throw new TorqueException(msg); //EXCEPTION
3015:                }
3016:
3017:                if (activitySet == null) {
3018:                    // Save activitySet record
3019:                    activitySet = ActivitySetManager.getInstance(
3020:                            ActivitySetTypePeer.CREATE_ISSUE__PK, user);
3021:                    activitySet.save();
3022:                }
3023:                setActivitySetRelatedByCreatedTransId(activitySet);
3024:
3025:                // enter the values into the activitySet
3026:                final LinkedMap avMap = getModuleAttributeValuesMap();
3027:                final MapIterator iter = avMap.mapIterator();
3028:                while (iter.hasNext()) {
3029:                    final AttributeValue aval = (AttributeValue) avMap.get(iter
3030:                            .next());
3031:                    try {
3032:                        aval.startActivitySet(activitySet);
3033:                    } catch (ScarabException se) {
3034:                        L10NMessage l10nmsg = new L10NMessage(
3035:                                L10NKeySet.ExceptionTorqueGeneric, se);
3036:                        throw new ScarabException(l10nmsg);
3037:                    }
3038:                }
3039:                this .save();
3040:
3041:                // create initial issue creation activity
3042:                ActivityManager.createReportIssueActivity(this , activitySet,
3043:                        Localization.getString(
3044:                                ScarabConstants.DEFAULT_BUNDLE_NAME,
3045:                                getLocale(), "IssueCreated"));
3046:
3047:                // this needs to be done after the issue is created.
3048:                // check to make sure the attachment has data before submitting it.
3049:                final String attachmentData = attachment.getData();
3050:                if (attachmentData != null && attachmentData.length() > 0) {
3051:                    attachment = AttachmentManager.getReason(attachment, this ,
3052:                            user);
3053:                    activitySet.setAttachment(attachment);
3054:                }
3055:                activitySet.save();
3056:
3057:                // need to clear the cache since this is after the 
3058:                // issue is saved. for some reason, things don't
3059:                // show up properly right away.
3060:                ScarabCache.clear();
3061:                return activitySet;
3062:            }
3063:
3064:            /**
3065:             * Sets AttributeValues for an issue based on a hashmap of attribute values
3066:             * This is data is saved to the database and the proper ActivitySet is
3067:             * also recorded.
3068:             * @param activitySet ActivitySet instance
3069:             * @param newAttVals A map of attribute Id's vs new AttributeValues
3070:             * @param attachment Attachment to the issue
3071:             * @param user User responsible for this activity
3072:             * @return ActivitySet object containing the changes made to the issue
3073:             * @throws TorqueException when the workflow has an error to report
3074:             */
3075:            public ActivitySet setAttributeValues(ActivitySet activitySet,
3076:                    final HashMap newAttVals, final Attachment attachment,
3077:                    final ScarabUser user) throws TorqueException,
3078:                    ScarabException {
3079:                if (!isTemplate()) {
3080:                    final String msg = doCheckAttributeValueWorkflow(
3081:                            newAttVals, user);
3082:                    if (msg != null) {
3083:                        throw new ScarabException(
3084:                                L10NKeySet.ErrorExceptionMessage, msg); //EXCEPTION 
3085:                    }
3086:                }
3087:                // save the attachment if it exists.
3088:                if (attachment != null) {
3089:                    attachment.setTextFields(user, this ,
3090:                            Attachment.MODIFICATION__PK);
3091:                    attachment.save();
3092:                }
3093:                activitySet = attachActivitySet(activitySet, user, attachment);
3094:
3095:                final LinkedMap avMap = getModuleAttributeValuesMap();
3096:                AttributeValue oldAttVal = null;
3097:                AttributeValue newAttVal = null;
3098:                final Iterator iter = newAttVals.keySet().iterator();
3099:                boolean attValDeleted = false;
3100:                while (iter.hasNext()) {
3101:                    final Integer attrId = (Integer) iter.next();
3102:                    final Attribute attr = AttributeManager.getInstance(attrId);
3103:                    oldAttVal = (AttributeValue) avMap.get(attr.getName()
3104:                            .toUpperCase());
3105:                    newAttVal = (AttributeValue) newAttVals.get(attrId);
3106:                    final String newAttValValue = newAttVal.getValue();
3107:
3108:                    if (oldAttVal != null
3109:                            && (newAttValValue != null
3110:                                    && !newAttValValue.equals(oldAttVal
3111:                                            .getValue()) || newAttValValue == null)) {
3112:                        if (Log.get().isDebugEnabled()) {
3113:                            Log.get().debug(
3114:                                    "Attribute: " + attr.getName()
3115:                                            + " has newAttValValue = "
3116:                                            + newAttValValue);
3117:                        }
3118:                        if (newAttValValue != null
3119:                                && newAttValValue.length() > 0) {
3120:                            oldAttVal.setProperties(newAttVal);
3121:                        } else {
3122:                            oldAttVal.setDeleted(true);
3123:                            Log.get().debug("setDeleted(true)");
3124:                            attValDeleted = true;
3125:                        }
3126:                        oldAttVal.startActivitySet(activitySet);
3127:                        oldAttVal.save();
3128:                    }
3129:
3130:                }
3131:                if (attValDeleted) {
3132:                    //Remove attribute value map from cache
3133:                    getMethodResult().remove(this , GET_MODULE_ATTRVALUES_MAP,
3134:                            Boolean.TRUE);
3135:                }
3136:                return activitySet;
3137:            }
3138:
3139:            /**
3140:             * Sets an ActivitySet as the lastActivitySet of an Issue.
3141:             * Crates and saves a new ActivitySet, if required.
3142:             * Saves the Issue.
3143:             * @return ActivitySet
3144:             * @throws TorqueException
3145:             */
3146:            protected ActivitySet attachActivitySet(ActivitySet activitySet,
3147:                    final ScarabUser user, final Attachment attachment,
3148:                    final Integer activitySetType) throws TorqueException,
3149:                    ScarabException {
3150:                if (activitySet == null) {
3151:                    activitySet = getActivitySet(user, attachment,
3152:                            activitySetType);
3153:                    activitySet.save();
3154:                    ScarabCache.clear();
3155:                }
3156:                setLastTransId(activitySet.getActivitySetId());
3157:                save();
3158:                return activitySet;
3159:            }
3160:
3161:            protected ActivitySet attachActivitySet(ActivitySet activitySet,
3162:                    final ScarabUser user, final Attachment attachment)
3163:                    throws TorqueException, ScarabException {
3164:                return attachActivitySet(activitySet, user, attachment,
3165:                        ActivitySetTypePeer.EDIT_ISSUE__PK);
3166:            }
3167:
3168:            protected ActivitySet attachActivitySet(ActivitySet activitySet,
3169:                    final ScarabUser user) throws TorqueException,
3170:                    ScarabException {
3171:                return attachActivitySet(activitySet, user, null,
3172:                        ActivitySetTypePeer.EDIT_ISSUE__PK);
3173:            }
3174:
3175:            /**
3176:             * This method is used with the setInitialAttributeValues() method to 
3177:             * Make sure that workflow is valid for the initial values of a new issue. 
3178:             * It will return a non-null String
3179:             * which is the workflow error message otherwise it will return null.
3180:             */
3181:            public String doCheckInitialAttributeValueWorkflow(
3182:                    final HashMap newValues, final ScarabUser user)
3183:                    throws TorqueException, ScarabException {
3184:                String msg = null;
3185:                final Iterator iter = newValues.keySet().iterator();
3186:                while (iter.hasNext()) {
3187:                    final Integer attrId = (Integer) iter.next();
3188:                    final Attribute attr = AttributeManager.getInstance(attrId);
3189:                    if (attr.isOptionAttribute()) {
3190:                        final AttributeOption toOption = AttributeOptionManager
3191:                                .getInstance(new Integer((String) newValues
3192:                                        .get(attrId)));
3193:                        msg = WorkflowFactory.getInstance()
3194:                                .checkInitialTransition(toOption, this ,
3195:                                        newValues, user);
3196:                    }
3197:                    if (msg != null) {
3198:                        break;
3199:                    }
3200:                }
3201:                return msg;
3202:            }
3203:
3204:            /**
3205:             * This method is used with the setAttributeValues() method to 
3206:             * Make sure that workflow is valid. It will return a non-null String
3207:             * which is the workflow error message otherwise it will return null.
3208:             */
3209:            public String doCheckAttributeValueWorkflow(
3210:                    final HashMap newAttVals, final ScarabUser user)
3211:                    throws TorqueException, ScarabException {
3212:                final LinkedMap avMap = getModuleAttributeValuesMap();
3213:                AttributeValue oldAttVal = null;
3214:                AttributeValue newAttVal = null;
3215:                String msg = null;
3216:                final Iterator iter = newAttVals.keySet().iterator();
3217:                while (iter.hasNext()) {
3218:                    final Integer attrId = (Integer) iter.next();
3219:                    final Attribute attr = AttributeManager.getInstance(attrId);
3220:                    oldAttVal = (AttributeValue) avMap.get(attr.getName()
3221:                            .toUpperCase());
3222:                    newAttVal = (AttributeValue) newAttVals.get(attrId);
3223:                    AttributeOption fromOption = null;
3224:                    AttributeOption toOption = null;
3225:
3226:                    if (newAttVal.getValue() != null) {
3227:                        if (newAttVal.getAttribute().isOptionAttribute()) {
3228:                            if (oldAttVal.getOptionId() == null) {
3229:                                fromOption = AttributeOptionManager
3230:                                        .getInstance(ScarabConstants.INTEGER_0);
3231:                            } else {
3232:                                fromOption = oldAttVal.getAttributeOption();
3233:                            }
3234:                            toOption = newAttVal.getAttributeOption();
3235:                            msg = WorkflowFactory.getInstance()
3236:                                    .checkTransition(fromOption, toOption,
3237:                                            this , newAttVals, user);
3238:                        }
3239:                        if (msg != null) {
3240:                            break;
3241:                        }
3242:                    }
3243:                }
3244:                return msg;
3245:            }
3246:
3247:            /**
3248:             * This method is used with the setAttributeValues() method to 
3249:             * Make sure that workflow is valid. It will return a non-null String
3250:             * which is the workflow error message otherwise it will return null.
3251:             *
3252:             * @deprecated The attachment doesn't need to be passed into this method.
3253:             */
3254:            public String doCheckAttributeValueWorkflow(
3255:                    final HashMap newAttVals, final Attachment attachment,
3256:                    final ScarabUser user) throws TorqueException,
3257:                    ScarabException {
3258:                return doCheckAttributeValueWorkflow(newAttVals, user);
3259:            }
3260:
3261:            /**
3262:             * If the comment hasn't changed, it will return a valid ActivitySet
3263:             * otherwise it returns null.
3264:             */
3265:            public ActivitySet doEditComment(ActivitySet activitySet,
3266:                    final String newComment, final Attachment attachment,
3267:                    final ScarabUser user) throws TorqueException,
3268:                    ScarabException {
3269:                final String oldComment = attachment.getData();
3270:                if (!newComment.equals(oldComment)) {
3271:                    attachment.setData(newComment);
3272:                    attachment.save();
3273:
3274:                    activitySet = attachActivitySet(activitySet, user);
3275:                    // Save activity record
3276:                    ActivityManager.createTextActivity(this , null, activitySet,
3277:                            ActivityType.COMMENT_CHANGED, null, attachment,
3278:                            oldComment, newComment);
3279:
3280:                    NotificationManagerFactory.getInstance()
3281:                            .addActivityNotification(
3282:                                    ActivityType.COMMENT_CHANGED, activitySet,
3283:                                    this , user);
3284:                }
3285:                return activitySet;
3286:            }
3287:
3288:            /**
3289:             * If the URL hasn't changed, it will return a valid ActivitySet
3290:             * otherwise it returns null.
3291:             */
3292:            public ActivitySet doDeleteUrl(ActivitySet activitySet,
3293:                    final Attachment attachment, final ScarabUser user)
3294:                    throws TorqueException, ScarabException {
3295:                final String oldUrl = attachment.getData();
3296:                attachment.setDeleted(true);
3297:                attachment.save();
3298:
3299:                activitySet = attachActivitySet(activitySet, user);
3300:
3301:                // Save activity record
3302:                ActivityManager.createTextActivity(this , null, activitySet,
3303:                        ActivityType.URL_DELETED, null, attachment, oldUrl,
3304:                        null);
3305:                return activitySet;
3306:            }
3307:
3308:            /**
3309:             * Remove the attachment. 
3310:             * On return the MutableBoolean physicallyDeleted is set to true,
3311:             * if the attachment file also was removed by this operation.
3312:             * If the attached File still exists for any reason, physicallyDeleted
3313:             * will be set to false.
3314:             * Note: You can enable/disable physical deletion by setting the
3315:             *       environment property scarab.attachment.remove.permanent
3316:             *       to true/false (false is the default setting).
3317:             */
3318:            public ActivitySet doRemoveAttachment(ActivitySet activitySet,
3319:                    final MutableBoolean physicallyDeleted,
3320:                    final Attachment attachment, final ScarabUser user)
3321:                    throws TorqueException, ScarabException {
3322:                boolean attachmentPhysicallyDeleted = false;
3323:                final boolean physicalDeletionAllowed = Turbine
3324:                        .getConfiguration().getBoolean(
3325:                                "scarab.attachment.remove.permanent", false);
3326:
3327:                if (physicalDeletionAllowed) {
3328:                    attachmentPhysicallyDeleted = attachment
3329:                            .deletePhysicalAttachment();
3330:                    physicallyDeleted.set(attachmentPhysicallyDeleted);
3331:                }
3332:
3333:                attachment.setDeleted(true);
3334:                attachment.save();
3335:
3336:                activitySet = attachActivitySet(activitySet, user);
3337:
3338:                // Save activity record
3339:                ActivityManager.createTextActivity(this , null, activitySet,
3340:                        ActivityType.ATTACHMENT_REMOVED, null, attachment,
3341:                        attachment.getFileName(), null);
3342:
3343:                return activitySet;
3344:            }
3345:
3346:            /**
3347:             * Returns users assigned to all user attributes.
3348:             */
3349:            public HashSet getAssociatedUsers() throws TorqueException {
3350:                HashSet users = null;
3351:                final Object obj = ScarabCache.get(this , GET_ASSOCIATED_USERS);
3352:                if (obj == null) {
3353:                    final List attributeList = getModule().getUserAttributes(
3354:                            getIssueType(), true);
3355:                    final List attributeIdList = new ArrayList();
3356:
3357:                    for (int i = 0; i < attributeList.size(); i++) {
3358:                        final Attribute att = (Attribute) attributeList.get(i);
3359:                        final RModuleAttribute modAttr = getModule()
3360:                                .getRModuleAttribute(att, getIssueType());
3361:                        if (modAttr.getActive()) {
3362:                            attributeIdList.add(att.getAttributeId());
3363:                        }
3364:                    }
3365:
3366:                    if (!attributeIdList.isEmpty()) {
3367:                        users = new HashSet();
3368:                        final Criteria crit = new Criteria().addIn(
3369:                                AttributeValuePeer.ATTRIBUTE_ID,
3370:                                attributeIdList).add(
3371:                                AttributeValuePeer.DELETED, false);
3372:                        crit.setDistinct();
3373:
3374:                        final List attValues = getAttributeValues(crit);
3375:                        for (int i = 0; i < attValues.size(); i++) {
3376:                            final List item = new ArrayList(2);
3377:                            final AttributeValue attVal = (AttributeValue) attValues
3378:                                    .get(i);
3379:                            final ScarabUser su = ScarabUserManager
3380:                                    .getInstance(attVal.getUserId());
3381:                            final Attribute attr = AttributeManager
3382:                                    .getInstance(attVal.getAttributeId());
3383:                            item.add(attr);
3384:                            item.add(su);
3385:                            users.add(item);
3386:                        }
3387:                    }
3388:                    ScarabCache.put(users, this , GET_ASSOCIATED_USERS);
3389:                } else {
3390:                    users = (HashSet) obj;
3391:                }
3392:                return users;
3393:            }
3394:
3395:            public String toString() {
3396:                String id = null;
3397:                try {
3398:                    id = isNew() ? "New issue" : getUniqueId();
3399:                } catch (Exception e) {
3400:                    id = "Error in getting unique id";
3401:                    Log.get().warn(id, e);
3402:                }
3403:
3404:                return super .toString() + '{' + id + '}';
3405:            }
3406:
3407:            /**
3408:             * Returns if the issue's BlockingCondition is fulfilled.
3409:             * 
3410:             * @return
3411:             */
3412:            public boolean isBlockingConditionTrue() throws TorqueException {
3413:                boolean isBlockingConditionTrue = false;
3414:                final List blockingConditions = this .getRModuleIssueType()
3415:                        .getConditions();
3416:                for (Iterator it = blockingConditions.iterator(); !isBlockingConditionTrue
3417:                        && it.hasNext();) {
3418:                    final Condition cond = (Condition) it.next();
3419:                    final Integer conditionOptionId = cond.getOptionId();
3420:                    final Attribute attr = cond.getAttributeOption()
3421:                            .getAttribute();
3422:                    final AttributeValue attrVal = this .getAttributeValue(attr);
3423:                    if (attrVal != null) {
3424:                        final Integer issueOptionId = attrVal.getOptionId();
3425:                        if (issueOptionId != null
3426:                                && issueOptionId.equals(conditionOptionId)) {
3427:                            isBlockingConditionTrue = true;
3428:                        }
3429:                    }
3430:                }
3431:                return isBlockingConditionTrue;
3432:            }
3433:
3434:            /**
3435:             * Returns if this issue is currently blocking any other.
3436:             * @return
3437:             * @throws TorqueException
3438:             */
3439:            public boolean isBlockingAnyIssue() throws TorqueException {
3440:                return this .getBlockedIssues().size() > 0;
3441:            }
3442:
3443:            /**
3444:             * An issue is blocked when it depends, via a is_blocked_by dependency,
3445:             * of an issue that is currently "blocking". Whenever an issue is blocked, some transitions
3446:             * might not be availaible.
3447:             * @return
3448:             */
3449:            public boolean isBlocked() throws TorqueException {
3450:                return (getBlockingIssues().size() > 0);
3451:            }
3452:
3453:            /**
3454:             * An issue is blocked when it depends, via a is_blocked_by dependency,
3455:             * of an issue that is currently "blocking". Whenever an issue is blocked, some transitions
3456:             * might not be availaible.
3457:             * @return
3458:             */
3459:            public boolean isBlockedBy(final String blockingId)
3460:                    throws TorqueException {
3461:                final List blockingIssues = getBlockingIssues();
3462:                int issueCount = getBlockingIssues().size();
3463:                if (issueCount == 0) {
3464:                    return false;
3465:                }
3466:
3467:                for (int index = 0; index < issueCount; index++) {
3468:                    final Issue issue = (Issue) blockingIssues.get(index);
3469:                    final String id = issue.getUniqueId();
3470:                    if (id.equals(blockingId)) {
3471:                        return true;
3472:                    }
3473:                }
3474:                return false;
3475:            }
3476:
3477:            public boolean isBlocking(final String blockedId)
3478:                    throws TorqueException {
3479:                final List blockedIssues = getBlockedIssues();
3480:                final int issueCount = blockedIssues.size();
3481:                if (issueCount == 0) {
3482:                    return false;
3483:                }
3484:
3485:                for (int index = 0; index < issueCount; index++) {
3486:                    final Issue issue = (Issue) blockedIssues.get(index);
3487:                    final String id = issue.getUniqueId();
3488:                    if (id.equals(blockedId)) {
3489:                        return true;
3490:                    }
3491:                }
3492:                return false;
3493:            }
3494:
3495:            /**
3496:             * Returns a list of issues that actually "block" this issue, i.e., that
3497:             * are related via a "is blocked by" dependency, and are "blocking".
3498:             * @return
3499:             */
3500:            public List getBlockingIssues() throws TorqueException {
3501:                final List blockingIssues = new ArrayList();
3502:                final List prerequisiteIssues = this .getPrerequisiteIssues();
3503:                for (Iterator it = prerequisiteIssues.iterator(); it.hasNext();) {
3504:                    final Issue is = (Issue) it.next();
3505:                    if (is.isBlockingConditionTrue())
3506:                        blockingIssues.add(is);
3507:                }
3508:                return blockingIssues;
3509:            }
3510:
3511:            /**
3512:             * Returns a list of issues that are blockable by this issue, via a "is_blocked_by"
3513:             * relationship. 
3514:             * @return
3515:             * @throws TorqueException
3516:             */
3517:            public List getPrerequisiteIssues() throws TorqueException {
3518:                final List blockingIssues = new ArrayList();
3519:                final List parentIssues = this .getParents();
3520:                for (Iterator it = parentIssues.iterator(); it.hasNext();) {
3521:                    final Depend depend = (Depend) it.next();
3522:                    if (depend.getDependType().getDependTypeId().equals(
3523:                            DependTypePeer.BLOCKING__PK)) {
3524:                        blockingIssues.add(IssuePeer.retrieveByPK(depend
3525:                                .getObservedId()));
3526:                    }
3527:                }
3528:                return blockingIssues;
3529:            }
3530:
3531:            /**
3532:             * Returns a list of issues that are related to this issue, via a "is_related to"
3533:             * relationship. 
3534:             * @return
3535:             * @throws TorqueException
3536:             */
3537:            public List getRelatedIssues() throws TorqueException {
3538:                return getAssociatedIssues(DependTypePeer.NON_BLOCKING__PK);
3539:            }
3540:
3541:            /**
3542:             * Returns a list of issues that are related to this issue, via a "is_duplicate of"
3543:             * relationship. 
3544:             * @return
3545:             * @throws TorqueException
3546:             */
3547:            public List getDuplicateIssues() throws TorqueException {
3548:                return getAssociatedIssues(DependTypePeer.DUPLICATE__PK);
3549:            }
3550:
3551:            /**
3552:             * Returns a list of issues that are associated to this issue via 
3553:             * the dependandTypeId. 
3554:             * @param dependTypeId
3555:             * @return
3556:             * @throws TorqueException
3557:             */
3558:            private List getAssociatedIssues(final Integer dependTypeId)
3559:                    throws TorqueException {
3560:                final List relatedIssues = new ArrayList();
3561:                final List allIssues = this .getAllDependencies();
3562:                for (Iterator it = allIssues.iterator(); it.hasNext();) {
3563:                    final Depend depend = (Depend) it.next();
3564:                    final DependType type = depend.getDependType();
3565:                    final Integer typeId = type.getDependTypeId();
3566:                    if (typeId.equals(dependTypeId)) {
3567:                        //Assume, the dependant issue is the ObservedId in the Depend
3568:                        Issue relatedIssue = IssuePeer.retrieveByPK(depend
3569:                                .getObservedId());
3570:                        if (relatedIssue.getIssueId().equals(this .getIssueId())) {
3571:                            //No, the dependant issue is the ObserverId in the depend.
3572:                            relatedIssue = IssuePeer.retrieveByPK(depend
3573:                                    .getObserverId());
3574:                        }
3575:                        relatedIssues.add(relatedIssue);
3576:                    }
3577:                }
3578:                return relatedIssues;
3579:            }
3580:
3581:            /**
3582:             * Returns a list of issues currently BLOCKED by this issue
3583:             * 
3584:             * @return
3585:             * @throws TorqueException
3586:             */
3587:            public List getBlockedIssues() throws TorqueException {
3588:                if (this .isBlockingConditionTrue()) {
3589:                    return this .getDependantIssues();
3590:                } else {
3591:                    return new ArrayList();
3592:                }
3593:            }
3594:
3595:            /**
3596:             * Returns a list of issues that might be blocked by this issue because if its
3597:             * "is_blocked_by"   dependency.
3598:             * 
3599:             * @return
3600:             * @throws TorqueException
3601:             */
3602:            public List getDependantIssues() throws TorqueException {
3603:                final List dependantIssues = new ArrayList();
3604:                final List childIssues = this .getChildren();
3605:                for (Iterator it = childIssues.iterator(); it.hasNext();) {
3606:                    final Depend depend = (Depend) it.next();
3607:                    if (depend.getDependType().getDependTypeId().equals(
3608:                            DependTypePeer.BLOCKING__PK)) {
3609:                        dependantIssues.add(IssuePeer.retrieveByPK(depend
3610:                                .getObserverId()));
3611:                    }
3612:                }
3613:                return dependantIssues;
3614:            }
3615:
3616:            /**
3617:             * This method search for the new ID of a moved issue.
3618:             * @return
3619:             * @throws TorqueException
3620:             */
3621:            public String getIssueNewId() throws TorqueException {
3622:                return ActivityPeer.getNewIssueUniqueId(this);
3623:            }
3624:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.