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: import com.workingdogs.village.DataSetException;
0050: import java.util.List;
0051: import java.util.ArrayList;
0052: import java.util.Iterator;
0053: import java.util.Properties;
0054: import java.io.InputStream;
0055: import java.io.IOException;
0056: import org.apache.torque.TorqueException;
0057:
0058: import org.apache.torque.util.Criteria;
0059: import org.apache.torque.om.Persistent;
0060: import org.apache.torque.manager.MethodResultCache;
0061: import org.apache.fulcrum.localization.Localization;
0062:
0063: import org.tigris.scarab.services.cache.ScarabCache;
0064: import org.tigris.scarab.tools.localization.L10NKeySet;
0065: import org.tigris.scarab.om.Module;
0066: import org.tigris.scarab.om.IssuePeer;
0067: import org.tigris.scarab.util.ScarabException;
0068: import org.tigris.scarab.util.ScarabConstants;
0069: import org.tigris.scarab.util.Log;
0070: import org.tigris.scarab.workflow.Workflow;
0071: import org.tigris.scarab.workflow.WorkflowFactory;
0072:
0073: /**
0074: * This class represents an IssueType.
0075: *
0076: * @author <a href="mailto:elicia@collab.net">Elicia David</a>
0077: * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
0078: * @version $Id: IssueType.java 10123 2006-05-30 09:46:01Z dabbous $
0079: */
0080: public class IssueType extends BaseIssueType implements Persistent {
0081: private static final String ISSUE_TYPE = "IssueType";
0082: private static final String GET_TEMPLATE_ISSUE_TYPE = "getTemplateIssueType";
0083: private static final String GET_INSTANCE = "getInstance";
0084: protected static final String GET_ATTRIBUTE_GROUPS = "getAttributeGroups";
0085: protected static final String GET_ATTRIBUTE_GROUP = "getAttributeGroup";
0086: protected static final String GET_R_ISSUETYPE_ATTRIBUTES = "getRIssueTypeAttributes";
0087: protected static final String GET_R_ISSUETYPE_OPTIONS = "getRIssueTypeOptions";
0088: protected static final String GET_ALL_R_ISSUETYPE_OPTIONS = "getAllRIssueTypeOptions";
0089: protected static final String GET_DEFAULT_TEXT_ATTRIBUTE = "getDefaultTextAttribute";
0090: protected static final String GET_QUICK_SEARCH_ATTRIBUTES = "getQuickSearchAttributes";
0091: protected static final String GET_REQUIRED_ATTRIBUTES = "getRequiredAttributes";
0092: protected static final String GET_ACTIVE_ATTRIBUTES = "getActiveAttributes";
0093:
0094: static final String USER = "user";
0095: static final String NON_USER = "non-user";
0096:
0097: private static final Properties SYSTEM_CONFIG = new Properties();
0098:
0099: //loads the properties file that specifies system defined issue types
0100: static {
0101: InputStream in = IssueType.class
0102: .getResourceAsStream("IssueTypeConfig.properties");
0103: if (in != null) {
0104: try {
0105: SYSTEM_CONFIG.load(in);
0106: } catch (IOException ioe) {
0107: Log
0108: .get()
0109: .warn(
0110: "Exception while loading the file: IssueTypeConfig.properties",
0111: ioe);
0112: }
0113: }
0114: }
0115:
0116: // this will not change, so only look it up once.
0117: private IssueType templateIssueType;
0118:
0119: // this will not change, so only look it up once.
0120: private IssueType parentIssueType;
0121:
0122: /**
0123: * Gets the IssueType template for this IssueType. The template
0124: * is a special type of IssueType.
0125: */
0126: public IssueType getTemplateIssueType() throws TorqueException,
0127: ScarabException {
0128: if (templateIssueType == null) {
0129: final Criteria crit = new Criteria();
0130: crit.add(IssueTypePeer.PARENT_ID, getIssueTypeId());
0131: final List results = IssueTypePeer.doSelect(crit);
0132: if (results.isEmpty()) {
0133: throw new ScarabException(
0134: L10NKeySet.ExceptionTemplateTypeForIssueType);
0135: } else {
0136: templateIssueType = (IssueType) results.get(0);
0137: }
0138: }
0139: return templateIssueType;
0140: }
0141:
0142: /**
0143: * Gets the parent IssueType for this template IssueType. The template
0144: * is a special type of IssueType.
0145: */
0146: public IssueType getIssueTypeForTemplateType()
0147: throws TorqueException {
0148: if (parentIssueType == null) {
0149: parentIssueType = getIssueTypeRelatedByParentId();
0150: }
0151: return parentIssueType;
0152: }
0153:
0154: /**
0155: * Gets the id of the template that corresponds to the issue type.
0156: */
0157: public Integer getTemplateId() throws TorqueException,
0158: ScarabException {
0159: return getTemplateIssueType().getIssueTypeId();
0160: }
0161:
0162: /**
0163: * Returns true if the issue type has issues associated with it.
0164: */
0165: public boolean hasIssues() throws TorqueException, DataSetException {
0166: return hasIssues((Module) null);
0167: }
0168:
0169: /**
0170: * If module name is identical to global name, return the global
0171: * name. Otherwise return the module name followed by a space
0172: * and the global name in parentheses.
0173: * @return a <code>String</code> representation of the display name.
0174: */
0175: public String getDisplayName(Module module) throws TorqueException {
0176: String moduleName = module.getRModuleIssueType(this )
0177: .getDisplayName();
0178: String displayName = getName();
0179: if (!moduleName.equals(displayName)) {
0180: displayName = moduleName + " (" + displayName + ")";
0181: }
0182: return displayName;
0183: }
0184:
0185: /**
0186: * Returns true if the issue type/module has issues associated with it.
0187: */
0188: public boolean hasIssues(Module module) throws TorqueException,
0189: DataSetException {
0190: Criteria crit = new Criteria();
0191: crit.add(IssuePeer.TYPE_ID, getIssueTypeId());
0192: if (module != null) {
0193: crit.add(IssuePeer.MODULE_ID, module.getModuleId());
0194: }
0195: return (IssuePeer.count(crit) > 0);
0196: }
0197:
0198: /**
0199: * Get the IssueType using a issue type name
0200: */
0201: public static IssueType getInstance(final String issueTypeName)
0202: throws TorqueException, ScarabException {
0203: IssueType result = null;
0204: Object obj = ScarabCache.get(ISSUE_TYPE, GET_INSTANCE,
0205: issueTypeName);
0206: if (obj == null) {
0207: final Criteria crit = new Criteria();
0208: crit.add(IssueTypePeer.NAME, issueTypeName);
0209: final List issueTypes = IssueTypePeer.doSelect(crit);
0210: if (issueTypes == null || issueTypes.size() == 0) {
0211: throw new ScarabException(
0212: L10NKeySet.ExceptionInvalidIssueType,
0213: issueTypeName);
0214: }
0215: result = (IssueType) issueTypes.get(0);
0216: ScarabCache.put(result, ISSUE_TYPE, GET_INSTANCE,
0217: issueTypeName);
0218: } else {
0219: result = (IssueType) obj;
0220: }
0221: return result;
0222: }
0223:
0224: /**
0225: * Copy the IssueType and its corresponding template type
0226: */
0227: public IssueType copyIssueType() throws TorqueException,
0228: ScarabException {
0229: final IssueType newIssueType = new IssueType();
0230: newIssueType.setName(getName() + " (copy)");
0231: newIssueType.setDescription(getDescription());
0232: newIssueType.setParentId(ScarabConstants.INTEGER_0);
0233: newIssueType.save();
0234: final Integer newId = newIssueType.getIssueTypeId();
0235:
0236: // Copy template type
0237: final IssueType template = IssueTypePeer
0238: .retrieveByPK(getTemplateId());
0239: final IssueType newTemplate = new IssueType();
0240: newTemplate.setName(template.getName());
0241: newTemplate.setParentId(newId);
0242: newTemplate.save();
0243:
0244: // Copy user attributes
0245: final List userRIAs = getRIssueTypeAttributes(false, USER);
0246: for (int m = 0; m < userRIAs.size(); m++) {
0247: final RIssueTypeAttribute userRia = (RIssueTypeAttribute) userRIAs
0248: .get(m);
0249: final RIssueTypeAttribute newUserRia = userRia.copyRia();
0250: newUserRia.setIssueTypeId(newId);
0251: newUserRia.save();
0252: }
0253:
0254: // Copy attribute groups
0255: final List attrGroups = getAttributeGroups(false);
0256: for (int i = 0; i < attrGroups.size(); i++) {
0257: final AttributeGroup group = (AttributeGroup) attrGroups
0258: .get(i);
0259: final AttributeGroup newGroup = group.copyGroup();
0260: newGroup.setIssueTypeId(newId);
0261: newGroup.save();
0262:
0263: // add attributes
0264: final List attrs = group.getAttributes();
0265: if (attrs != null) {
0266: for (int j = 0; j < attrs.size(); j++) {
0267: // save attribute-attribute group maps
0268: final Attribute attr = (Attribute) attrs.get(j);
0269: final RAttributeAttributeGroup raag = group
0270: .getRAttributeAttributeGroup(attr);
0271: final RAttributeAttributeGroup newRaag = new RAttributeAttributeGroup();
0272: newRaag.setAttributeId(raag.getAttributeId());
0273: newRaag.setOrder(raag.getOrder());
0274: newRaag.setGroupId(newGroup.getAttributeGroupId());
0275: newRaag.save();
0276:
0277: // save attribute-issueType maps
0278: final RIssueTypeAttribute ria = getRIssueTypeAttribute(attr);
0279: final RIssueTypeAttribute newRia = ria.copyRia();
0280: newRia.setIssueTypeId(newId);
0281: newRia.save();
0282:
0283: // save options
0284: final List rios = getRIssueTypeOptions(attr, false);
0285: if (rios != null) {
0286: for (int k = 0; k < rios.size(); k++) {
0287: final RIssueTypeOption rio = (RIssueTypeOption) rios
0288: .get(k);
0289: final RIssueTypeOption newRio = rio
0290: .copyRio();
0291: newRio.setIssueTypeId(newId);
0292: newRio.save();
0293: }
0294: }
0295: }
0296: }
0297: }
0298:
0299: // add workflow
0300: WorkflowFactory.getInstance().copyIssueTypeWorkflows(this ,
0301: newIssueType);
0302:
0303: return newIssueType;
0304: }
0305:
0306: /**
0307: * Delete mappings with all modules
0308: */
0309: public void deleteModuleMappings(final ScarabUser user)
0310: throws TorqueException, ScarabException {
0311: final Criteria crit = new Criteria();
0312: crit.add(RModuleIssueTypePeer.ISSUE_TYPE_ID, getIssueTypeId());
0313: final List rmits = RModuleIssueTypePeer.doSelect(crit);
0314: for (int i = 0; i < rmits.size(); i++) {
0315: final RModuleIssueType rmit = (RModuleIssueType) rmits
0316: .get(i);
0317: rmit.delete(user);
0318: }
0319: ScarabCache.clear();
0320: }
0321:
0322: /**
0323: * Create default groups upon issue type creation.
0324: */
0325: public void createDefaultGroups() throws TorqueException {
0326: AttributeGroup ag = createNewGroup();
0327: ag.setOrder(1);
0328: ag.setDedupe(true);
0329: ag.setDescription(null);
0330: ag.save();
0331: AttributeGroup ag2 = createNewGroup();
0332: ag2.setOrder(3);
0333: ag2.setDedupe(false);
0334: ag2.setDescription(null);
0335: ag2.save();
0336: }
0337:
0338: public List getAttributeGroups(Module module)
0339: throws TorqueException {
0340: return getAttributeGroups(module, false);
0341: }
0342:
0343: public List getAttributeGroups(boolean activeOnly)
0344: throws TorqueException {
0345: return getAttributeGroups(null, activeOnly);
0346: }
0347:
0348: /**
0349: * List of attribute groups associated with this module).
0350: */
0351: public List getAttributeGroups(Module module, boolean activeOnly)
0352: throws TorqueException {
0353: List groups = null;
0354: Boolean activeBool = activeOnly ? Boolean.TRUE : Boolean.FALSE;
0355: Object obj = getMethodResult().get(this , GET_ATTRIBUTE_GROUPS,
0356: module, activeBool);
0357: if (obj == null) {
0358: Criteria crit = new Criteria().add(
0359: AttributeGroupPeer.ISSUE_TYPE_ID, getIssueTypeId())
0360: .addAscendingOrderByColumn(
0361: AttributeGroupPeer.PREFERRED_ORDER);
0362: if (activeOnly) {
0363: crit.add(AttributeGroupPeer.ACTIVE, true);
0364: }
0365: if (module != null) {
0366: crit.add(AttributeGroupPeer.MODULE_ID, module
0367: .getModuleId());
0368: } else {
0369: // TODO Change this to be crit.add(AttributeGroupPeer.MODULE_ID, Criteria.ISNULL) when torque is fixed
0370: crit
0371: .add(
0372: AttributeGroupPeer.MODULE_ID,
0373: (Object) (AttributeGroupPeer.MODULE_ID + " IS NULL"),
0374: Criteria.CUSTOM);
0375: }
0376: groups = AttributeGroupPeer.doSelect(crit);
0377: getMethodResult().put(groups, this , GET_ATTRIBUTE_GROUPS,
0378: module, activeBool);
0379: } else {
0380: groups = (List) obj;
0381: }
0382: return groups;
0383: }
0384:
0385: public AttributeGroup createNewGroup() throws TorqueException {
0386: return createNewGroup(null);
0387: }
0388:
0389: /**
0390: * Creates new attribute group.
0391: */
0392: public AttributeGroup createNewGroup(Module module)
0393: throws TorqueException {
0394: List groups = getAttributeGroups(module, false);
0395: AttributeGroup ag = new AttributeGroup();
0396:
0397: // Make default group name 'new attribute group'
0398: ag.setName(Localization.getString("ScarabBundle",
0399: ScarabConstants.DEFAULT_LOCALE, "NewAttributeGroup"));
0400: ag.setActive(true);
0401: ag.setIssueTypeId(getIssueTypeId());
0402: if (module != null) {
0403: ag.setModuleId(module.getModuleId());
0404: }
0405: if (groups.size() == 0) {
0406: ag.setDedupe(true);
0407: ag.setOrder(groups.size() + 1);
0408: } else {
0409: ag.setDedupe(false);
0410: ag.setOrder(groups.size() + 2);
0411: }
0412: ag.save();
0413: groups.add(ag);
0414: return ag;
0415: }
0416:
0417: /**
0418: * Gets the sequence where the dedupe screen fits between groups.
0419: *
0420: * @see #getDedupeSequence(Module)
0421: */
0422: public int getDedupeSequence() throws TorqueException {
0423: return getDedupeSequence(null);
0424: }
0425:
0426: /**
0427: * Gets the sequence where the dedupe screen fits between groups.
0428: *
0429: * @param module A specific Module to retrieve AttributeGroup
0430: * associations for, or <code>null</code> for groups associated
0431: * with the global issue type.
0432: */
0433: int getDedupeSequence(Module module) throws TorqueException {
0434: List groups = getAttributeGroups(module, false);
0435: int sequence = groups.size() + 1;
0436: for (int i = 1; i <= groups.size(); i++) {
0437: int order;
0438: int previousOrder;
0439: try {
0440: order = ((AttributeGroup) groups.get(i)).getOrder();
0441: previousOrder = ((AttributeGroup) groups.get(i - 1))
0442: .getOrder();
0443: } catch (Exception e) {
0444: Log.get().warn(
0445: "Error accessing dedupe sequence for issue "
0446: + "type '" + this + '\'', e);
0447: return sequence;
0448: }
0449:
0450: if (order != previousOrder + 1) {
0451: sequence = order - 1;
0452: break;
0453: }
0454: }
0455: return sequence;
0456: }
0457:
0458: /**
0459: * Gets associated attributes.
0460: */
0461: public List getRIssueTypeAttributes() {
0462: List rias = null;
0463: try {
0464: rias = getRIssueTypeAttributes(false);
0465: } catch (Exception e) {
0466: Log.get().warn(
0467: "Could not get RIA records for " + getName(), e);
0468: }
0469: return rias;
0470: }
0471:
0472: /**
0473: * Gets associated attributes.
0474: */
0475: public List getRIssueTypeAttributes(boolean activeOnly)
0476: throws TorqueException {
0477: return getRIssueTypeAttributes(activeOnly, "all");
0478: }
0479:
0480: /**
0481: * Gets associated attributes.
0482: */
0483: public List getRIssueTypeAttributes(boolean activeOnly,
0484: String attributeType) throws TorqueException {
0485: List rias = null;
0486: Boolean activeBool = (activeOnly ? Boolean.TRUE : Boolean.FALSE);
0487: Object obj = getMethodResult().get(this ,
0488: GET_R_ISSUETYPE_ATTRIBUTES, activeBool, attributeType);
0489: if (obj == null) {
0490: Criteria crit = new Criteria();
0491: crit.add(RIssueTypeAttributePeer.ISSUE_TYPE_ID,
0492: getIssueTypeId());
0493: crit
0494: .addAscendingOrderByColumn(RIssueTypeAttributePeer.PREFERRED_ORDER);
0495:
0496: if (activeOnly) {
0497: crit.add(RIssueTypeAttributePeer.ACTIVE, true);
0498: }
0499:
0500: if (USER.equals(attributeType)) {
0501: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID,
0502: AttributeTypePeer.USER_TYPE_KEY);
0503: crit.addJoin(AttributePeer.ATTRIBUTE_ID,
0504: RIssueTypeAttributePeer.ATTRIBUTE_ID);
0505: } else if (NON_USER.equals(attributeType)) {
0506: crit.addJoin(AttributePeer.ATTRIBUTE_ID,
0507: RIssueTypeAttributePeer.ATTRIBUTE_ID);
0508: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID,
0509: AttributeTypePeer.USER_TYPE_KEY,
0510: Criteria.NOT_EQUAL);
0511: }
0512:
0513: rias = RIssueTypeAttributePeer.doSelect(crit);
0514: getMethodResult().put(rias, this ,
0515: GET_R_ISSUETYPE_ATTRIBUTES, activeBool,
0516: attributeType);
0517: } else {
0518: rias = (List) obj;
0519: }
0520: return rias;
0521: }
0522:
0523: /**
0524: * Gets associated activeattributes.
0525: */
0526: public List getAttributes(String attributeType)
0527: throws TorqueException {
0528: ArrayList attrs = new ArrayList();
0529: List rias = getRIssueTypeAttributes(true, attributeType);
0530: for (int i = 0; i < rias.size(); i++) {
0531: attrs.add(((RIssueTypeAttribute) rias.get(i))
0532: .getAttribute());
0533: }
0534: return attrs;
0535: }
0536:
0537: /**
0538: * Adds issuetype-attribute mapping to issue type.
0539: */
0540: public RIssueTypeAttribute addRIssueTypeAttribute(
0541: Attribute attribute) throws TorqueException {
0542: String attributeType = null;
0543: attributeType = (attribute.isUserAttribute() ? USER : NON_USER);
0544:
0545: RIssueTypeAttribute ria = new RIssueTypeAttribute();
0546: ria.setIssueTypeId(getIssueTypeId());
0547: ria.setAttributeId(attribute.getAttributeId());
0548: ria.setOrder(getLastAttribute(attributeType) + 1);
0549: ria.save();
0550: getRIssueTypeAttributes(false, attributeType).add(ria);
0551: return ria;
0552: }
0553:
0554: public RIssueTypeAttribute getRIssueTypeAttribute(
0555: Attribute attribute) throws TorqueException {
0556: RIssueTypeAttribute ria = null;
0557: List rias = null;
0558: if (attribute.isUserAttribute()) {
0559: rias = getRIssueTypeAttributes(false, USER);
0560: } else {
0561: rias = getRIssueTypeAttributes(false, NON_USER);
0562: }
0563: Iterator i = rias.iterator();
0564: while (i.hasNext()) {
0565: RIssueTypeAttribute tempRia = (RIssueTypeAttribute) i
0566: .next();
0567: if (tempRia.getAttribute().equals(attribute)) {
0568: ria = tempRia;
0569: break;
0570: }
0571: }
0572: return ria;
0573: }
0574:
0575: /**
0576: * gets a list of all of the User Attributes in an issue type.
0577: */
0578: public List getUserAttributes() throws TorqueException {
0579: return getUserAttributes(true);
0580: }
0581:
0582: /**
0583: * gets a list of all of the User Attributes in an issue type.
0584: */
0585: public List getUserAttributes(boolean activeOnly)
0586: throws TorqueException {
0587: List rIssueTypeAttributes = getRIssueTypeAttributes(activeOnly,
0588: USER);
0589: List userAttributes = new ArrayList();
0590:
0591: for (int i = 0; i < rIssueTypeAttributes.size(); i++) {
0592: Attribute att = ((RIssueTypeAttribute) rIssueTypeAttributes
0593: .get(i)).getAttribute();
0594: userAttributes.add(att);
0595: }
0596: return userAttributes;
0597: }
0598:
0599: /**
0600: * FIXME: can this be done more efficently?
0601: * gets highest sequence number for issueType-attribute map
0602: * so that a new RIssueTypeAttribute can be added at the end.
0603: */
0604: public int getLastAttribute(String attributeType)
0605: throws TorqueException {
0606: List itAttributes = getRIssueTypeAttributes(false,
0607: attributeType);
0608: int last = 0;
0609:
0610: for (int i = 0; i < itAttributes.size(); i++) {
0611: int order = ((RIssueTypeAttribute) itAttributes.get(i))
0612: .getOrder();
0613: if (order > last) {
0614: last = order;
0615: }
0616: }
0617: return last;
0618: }
0619:
0620: /**
0621: * FIXME: can this be done more efficently?
0622: * gets highest sequence number for module-attribute map
0623: * so that a new RIssueTypeOption can be added at the end.
0624: */
0625: public int getLastAttributeOption(Attribute attribute)
0626: throws TorqueException {
0627: List issueTypeOptions = getRIssueTypeOptions(attribute);
0628: int last = 0;
0629:
0630: for (int i = 0; i < issueTypeOptions.size(); i++) {
0631: int order = ((RIssueTypeOption) issueTypeOptions.get(i))
0632: .getOrder();
0633: if (order > last) {
0634: last = order;
0635: }
0636: }
0637: return last;
0638: }
0639:
0640: /**
0641: * Adds issuetype-attribute-option mapping to module.
0642: */
0643: public RIssueTypeOption addRIssueTypeOption(AttributeOption option)
0644: throws TorqueException {
0645: RIssueTypeOption rio = new RIssueTypeOption();
0646: rio.setIssueTypeId(getIssueTypeId());
0647: rio.setOptionId(option.getOptionId());
0648: rio.setOrder(getLastAttributeOption(option.getAttribute()) + 1);
0649: rio.save();
0650: getRIssueTypeOptions(option.getAttribute(), false).add(rio);
0651: return rio;
0652: }
0653:
0654: /**
0655: * Gets associated attribute options.
0656: */
0657: public List getRIssueTypeOptions(Attribute attribute)
0658: throws TorqueException {
0659: return getRIssueTypeOptions(attribute, true);
0660: }
0661:
0662: /**
0663: * Gets associated attribute options.
0664: */
0665: public List getRIssueTypeOptions(Attribute attribute,
0666: boolean activeOnly) throws TorqueException {
0667: List allRIssueTypeOptions = null;
0668: allRIssueTypeOptions = getAllRIssueTypeOptions(attribute);
0669:
0670: if (allRIssueTypeOptions != null) {
0671: if (activeOnly) {
0672: List activeRIssueTypeOptions = new ArrayList(
0673: allRIssueTypeOptions.size());
0674: for (int i = 0; i < allRIssueTypeOptions.size(); i++) {
0675: RIssueTypeOption rio = (RIssueTypeOption) allRIssueTypeOptions
0676: .get(i);
0677: if (rio.getActive()) {
0678: activeRIssueTypeOptions.add(rio);
0679: }
0680: }
0681: allRIssueTypeOptions = activeRIssueTypeOptions;
0682: }
0683: }
0684: return allRIssueTypeOptions;
0685: }
0686:
0687: private List getAllRIssueTypeOptions(Attribute attribute)
0688: throws TorqueException {
0689: List rIssueTypeOpts;
0690: Object obj = ScarabCache.get(this , GET_ALL_R_ISSUETYPE_OPTIONS,
0691: attribute);
0692: if (obj == null) {
0693: List options = attribute.getAttributeOptions(false);
0694: Integer[] optIds = null;
0695: if (options == null) {
0696: optIds = new Integer[0];
0697: } else {
0698: optIds = new Integer[options.size()];
0699: }
0700: for (int i = optIds.length - 1; i >= 0; i--) {
0701: optIds[i] = ((AttributeOption) options.get(i))
0702: .getOptionId();
0703: }
0704:
0705: if (optIds.length > 0) {
0706: Criteria crit = new Criteria();
0707: crit.add(RIssueTypeOptionPeer.ISSUE_TYPE_ID,
0708: getIssueTypeId());
0709: crit.addIn(RIssueTypeOptionPeer.OPTION_ID, optIds);
0710: crit.addJoin(RIssueTypeOptionPeer.OPTION_ID,
0711: AttributeOptionPeer.OPTION_ID);
0712: crit
0713: .addAscendingOrderByColumn(RIssueTypeOptionPeer.PREFERRED_ORDER);
0714: crit
0715: .addAscendingOrderByColumn(AttributeOptionPeer.OPTION_NAME);
0716: rIssueTypeOpts = RIssueTypeOptionPeer.doSelect(crit);
0717: } else {
0718: rIssueTypeOpts = new ArrayList(0);
0719: }
0720: ScarabCache.put(rIssueTypeOpts, this ,
0721: GET_ALL_R_ISSUETYPE_OPTIONS, attribute);
0722: } else {
0723: rIssueTypeOpts = (List) obj;
0724: }
0725: return rIssueTypeOpts;
0726: }
0727:
0728: public RIssueTypeOption getRIssueTypeOption(AttributeOption option)
0729: throws TorqueException {
0730: RIssueTypeOption rio = null;
0731: List rios = getRIssueTypeOptions(option.getAttribute(), false);
0732: Iterator i = rios.iterator();
0733: while (i.hasNext()) {
0734: rio = (RIssueTypeOption) i.next();
0735: if (rio.getAttributeOption().equals(option)) {
0736: break;
0737: }
0738: }
0739:
0740: return rio;
0741: }
0742:
0743: /**
0744: * Gets a list of all of the global Attributes that are not
0745: * Associated with this issue type
0746: */
0747: public List getAvailableAttributes(String attributeType)
0748: throws TorqueException {
0749: List allAttributes = AttributePeer.getAttributes(attributeType);
0750: List availAttributes = new ArrayList();
0751: List rIssueTypeAttributes = getRIssueTypeAttributes(false,
0752: attributeType);
0753: List attrs = new ArrayList();
0754: for (int i = 0; i < rIssueTypeAttributes.size(); i++) {
0755: attrs.add(((RIssueTypeAttribute) rIssueTypeAttributes
0756: .get(i)).getAttribute());
0757: }
0758: for (int i = 0; i < allAttributes.size(); i++) {
0759: Attribute att = (Attribute) allAttributes.get(i);
0760: if (!attrs.contains(att)) {
0761: availAttributes.add(att);
0762: }
0763: }
0764: return availAttributes;
0765: }
0766:
0767: /**
0768: * Gets a list of all of the global attributes options
0769: * that are not associated with this issue type
0770: */
0771: public List getAvailableAttributeOptions(Attribute attribute)
0772: throws TorqueException {
0773: List rIssueTypeOptions = getRIssueTypeOptions(attribute, false);
0774: List issueTypeOptions = new ArrayList();
0775: if (rIssueTypeOptions != null) {
0776: for (int i = 0; i < rIssueTypeOptions.size(); i++) {
0777: issueTypeOptions
0778: .add(((RIssueTypeOption) rIssueTypeOptions
0779: .get(i)).getAttributeOption());
0780: }
0781: }
0782:
0783: List allOptions = attribute.getAttributeOptions(false);
0784: List availOptions = new ArrayList();
0785:
0786: for (int i = 0; i < allOptions.size(); i++) {
0787: AttributeOption option = (AttributeOption) allOptions
0788: .get(i);
0789: if (!issueTypeOptions.contains(option)) {
0790: availOptions.add(option);
0791: }
0792: }
0793: return availOptions;
0794: }
0795:
0796: private MethodResultCache getMethodResult() {
0797: return IssueTypeManager.getMethodResult();
0798: }
0799:
0800: public String toString() {
0801: return '{' + super .toString() + ": name=" + getName() + '}';
0802: }
0803:
0804: /**
0805: * Gets a list of non-user AttributeValues which match a given Module.
0806: * It is used in the MoveIssue2.vm template
0807: */
0808: public List getMatchingAttributeValuesList(Module oldModule,
0809: Module newModule, IssueType newIssueType)
0810: throws TorqueException {
0811: List matchingAttributes = new ArrayList();
0812: List srcActiveAttrs = getActiveAttributes(oldModule);
0813: List destActiveAttrs = newIssueType
0814: .getActiveAttributes(newModule);
0815: for (int i = 0; i < srcActiveAttrs.size(); i++) {
0816: Attribute attr = (Attribute) srcActiveAttrs.get(i);
0817:
0818: if (destActiveAttrs.contains(attr)) {
0819: matchingAttributes.add(attr);
0820: }
0821: }
0822: return matchingAttributes;
0823: }
0824:
0825: /**
0826: * Gets a list of Attributes which do not match a given Module.
0827: * It is used in the MoveIssue2.vm template
0828: */
0829: public List getOrphanAttributeValuesList(Module oldModule,
0830: Module newModule, IssueType newIssueType)
0831: throws TorqueException {
0832: List orphanAttributes = new ArrayList();
0833: List srcActiveAttrs = getActiveAttributes(oldModule);
0834: List destActiveAttrs = newIssueType
0835: .getActiveAttributes(newModule);
0836: for (int i = 0; i < srcActiveAttrs.size(); i++) {
0837: Attribute attr = (Attribute) srcActiveAttrs.get(i);
0838: if (!destActiveAttrs.contains(attr)) {
0839: orphanAttributes.add(attr);
0840: }
0841: }
0842: return orphanAttributes;
0843: }
0844:
0845: /**
0846: * Checks if this Issue Type is system defined.
0847: * Such Issue types are specified in "IssueTypeConfig.properties"
0848: * file in the format "<SCARAB_ISSUE_TYPE.NAME>=system"
0849: *
0850: * @return True if this Issue Type is System defined. False otherwise
0851: */
0852: public boolean isSystemDefined() throws TorqueException {
0853: boolean systemDefined = false;
0854: String name = getName();
0855: if (name != null) {
0856: systemDefined = "system".equalsIgnoreCase(SYSTEM_CONFIG
0857: .getProperty(name));
0858: }
0859: return systemDefined;
0860: }
0861:
0862: /**
0863: * if an RMA is the chosen attribute for email subjects then return it.
0864: * if not explicitly chosen, choose the highest ordered text attribute.
0865: *
0866: * @return the Attribute to use as the email subject,
0867: * or null if no suitable Attribute could be found.
0868: */
0869: public Attribute getDefaultTextAttribute(Module module)
0870: throws TorqueException {
0871: Attribute result = null;
0872: Object obj = ScarabCache.get(this , GET_DEFAULT_TEXT_ATTRIBUTE);
0873: if (obj == null) {
0874: // get related RMAs
0875: Criteria crit = new Criteria().add(
0876: RModuleAttributePeer.MODULE_ID, module
0877: .getModuleId());
0878: crit
0879: .addAscendingOrderByColumn(RModuleAttributePeer.PREFERRED_ORDER);
0880: List rmas = getRModuleAttributes(crit);
0881:
0882: // the code to find the correct attribute could be quite simple by
0883: // looping and calling RMA.isDefaultText(). The code from
0884: // that method can be restructured here to more efficiently
0885: // answer this question.
0886: Iterator i = rmas.iterator();
0887: while (i.hasNext()) {
0888: RModuleAttribute rma = (RModuleAttribute) i.next();
0889: if (rma.getDefaultTextFlag()) {
0890: result = rma.getAttribute();
0891: break;
0892: }
0893: }
0894:
0895: if (result == null) {
0896: // locate the highest ranked text attribute
0897: i = rmas.iterator();
0898: while (i.hasNext()) {
0899: RModuleAttribute rma = (RModuleAttribute) i.next();
0900: Attribute testAttr = rma.getAttribute();
0901: if (testAttr.isTextAttribute()
0902: && getAttributeGroup(module, testAttr)
0903: .getActive()) {
0904: result = testAttr;
0905: break;
0906: }
0907: }
0908: }
0909: ScarabCache.put(result, this , GET_DEFAULT_TEXT_ATTRIBUTE);
0910: } else {
0911: result = (Attribute) obj;
0912: }
0913: return result;
0914: }
0915:
0916: /**
0917: * Array of Attributes used for quick search.
0918: *
0919: * @return an <code>List</code> of Attribute objects
0920: */
0921: public List getQuickSearchAttributes(Module module)
0922: throws TorqueException {
0923: List attributes = null;
0924: Object obj = ScarabCache.get(this , GET_QUICK_SEARCH_ATTRIBUTES,
0925: module);
0926: if (obj == null) {
0927: Criteria crit = new Criteria(3).add(
0928: RModuleAttributePeer.QUICK_SEARCH, true);
0929: addOrderByClause(crit, module);
0930: attributes = getAttributes(crit);
0931: ScarabCache.put(attributes, this ,
0932: GET_QUICK_SEARCH_ATTRIBUTES, module);
0933: } else {
0934: attributes = (List) obj;
0935: }
0936: return attributes;
0937: }
0938:
0939: /**
0940: * gets a list of all of the Attributes in a Module based on the Criteria.
0941: */
0942: private List getAttributes(final Criteria criteria)
0943: throws TorqueException {
0944: final List moduleAttributes = getRModuleAttributes(criteria);
0945: final List attributes = new ArrayList(moduleAttributes.size());
0946: for (int i = 0; i < moduleAttributes.size(); i++) {
0947: attributes.add(((RModuleAttribute) moduleAttributes.get(i))
0948: .getAttribute());
0949: }
0950: return attributes;
0951: }
0952:
0953: /**
0954: * Checks whether the current user can create issues of this issueType
0955: * in the given module. Currently we only check whether the user is
0956: * allowed to create all necessary input (i.e. the required attributes).
0957: * If at least one attribute can not be set by the user due to transition
0958: * constraints, this method returns false, otherwise true.
0959: * @param user
0960: * @param module
0961: * @return
0962: * @throws TorqueException
0963: * @throws ScarabException
0964: */
0965: public boolean canCreateIssueInScope(ScarabUser user, Module module)
0966: throws TorqueException, ScarabException {
0967: boolean result = true;
0968: List requiredAttributes = getRequiredAttributes(module);
0969: Iterator iter = requiredAttributes.iterator();
0970: while (iter.hasNext()) {
0971: Attribute attribute = (Attribute) iter.next();
0972: Workflow workflow = WorkflowFactory.getInstance();
0973: if (attribute.isOptionAttribute()) {
0974: boolean canDoPartial = workflow.canMakeTransitionsFrom(
0975: user, this , attribute, null);
0976: if (!canDoPartial) {
0977: result = false;
0978: break;
0979: }
0980: }
0981: }
0982: return result;
0983: }
0984:
0985: /**
0986: * Checks whether the current user is allowed to set the given attribute
0987: * in the given module and in this issueType. If due to transition rules
0988: * the user is not allowed to set the attribute value, this method returns
0989: * false, otherwise true.
0990: * @param user
0991: * @param module
0992: * @param attribute
0993: * @return
0994: * @throws TorqueException
0995: * @throws ScarabException
0996: */
0997: public boolean canCreateAttributeInScope(ScarabUser user,
0998: Module module, Attribute attribute) throws TorqueException,
0999: ScarabException {
1000: boolean result = true;
1001: Workflow workflow = WorkflowFactory.getInstance();
1002: if (attribute.isOptionAttribute()) {
1003: result = workflow.canMakeTransitionsFrom(user, this ,
1004: attribute, null);
1005: }
1006: return result;
1007: }
1008:
1009: /**
1010: * Array of Attributes which are active and required by this module.
1011: * Whose attribute group's are also active.
1012: * @return an <code>List</code> of Attribute objects
1013: */
1014: public List getRequiredAttributes(Module module)
1015: throws TorqueException {
1016:
1017: List attributes = null;
1018: Object obj = ScarabCache.get(this , GET_REQUIRED_ATTRIBUTES,
1019: module);
1020: if (obj == null) {
1021: Criteria crit = new Criteria(3).add(
1022: RModuleAttributePeer.REQUIRED, true);
1023: crit.add(RModuleAttributePeer.ACTIVE, true);
1024: addOrderByClause(crit, module);
1025: List temp = getAttributes(crit);
1026: List requiredAttributes = new ArrayList();
1027: for (int i = 0; i < temp.size(); i++) {
1028: Attribute att = (Attribute) temp.get(i);
1029: AttributeGroup group = getAttributeGroup(module, att);
1030: if (group != null && group.getActive()) {
1031: requiredAttributes.add(att);
1032: }
1033: }
1034: attributes = requiredAttributes;
1035: ScarabCache.put(attributes, this , GET_REQUIRED_ATTRIBUTES,
1036: module);
1037: } else {
1038: attributes = (List) obj;
1039: }
1040: return attributes;
1041:
1042: }
1043:
1044: /**
1045: * Array of active Attributes for an issue type.
1046: *
1047: * @return an <code>List</code> of Attribute objects
1048: */
1049: public List getActiveAttributes(final Module module)
1050: throws TorqueException {
1051: List attributes = null;
1052: Object obj = ScarabCache.get(this , GET_ACTIVE_ATTRIBUTES,
1053: module);
1054: if (obj == null) {
1055: final Criteria crit = new Criteria(2);
1056: crit.add(RModuleAttributePeer.ACTIVE, true);
1057: addOrderByClause(crit, module);
1058: attributes = getAttributes(crit);
1059: ScarabCache.put(attributes, this , GET_ACTIVE_ATTRIBUTES,
1060: module);
1061: } else {
1062: attributes = (List) obj;
1063: }
1064: return attributes;
1065: }
1066:
1067: private void addOrderByClause(Criteria crit, Module module) {
1068: crit
1069: .addAscendingOrderByColumn(RModuleAttributePeer.PREFERRED_ORDER);
1070: crit
1071: .addAscendingOrderByColumn(RModuleAttributePeer.DISPLAY_VALUE);
1072: crit.add(RModuleAttributePeer.MODULE_ID, module.getModuleId());
1073: }
1074:
1075: private AttributeGroup getAttributeGroup(Module module,
1076: Attribute attribute) throws TorqueException {
1077: AttributeGroup group = null;
1078: Object obj = ScarabCache.get(this , GET_ATTRIBUTE_GROUP, module,
1079: attribute);
1080: if (obj == null) {
1081: Criteria crit = new Criteria().add(
1082: AttributeGroupPeer.ISSUE_TYPE_ID, getIssueTypeId())
1083: .add(AttributeGroupPeer.MODULE_ID,
1084: module.getModuleId()).addJoin(
1085: RAttributeAttributeGroupPeer.GROUP_ID,
1086: AttributeGroupPeer.ATTRIBUTE_GROUP_ID).add(
1087: RAttributeAttributeGroupPeer.ATTRIBUTE_ID,
1088: attribute.getAttributeId());
1089: List results = AttributeGroupPeer.doSelect(crit);
1090: if (results.size() > 0) {
1091: group = (AttributeGroup) results.get(0);
1092: ScarabCache.put(group, this , GET_ATTRIBUTE_GROUP,
1093: module, attribute);
1094: }
1095: } else {
1096: group = (AttributeGroup) obj;
1097: }
1098: return group;
1099: }
1100: }
|