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.util.List;
0052: import java.util.Iterator;
0053: import java.util.LinkedList;
0054: import java.util.Collections;
0055: import java.util.ArrayList;
0056: import java.util.Map;
0057: import java.util.HashMap;
0058: import java.util.Locale;
0059: import java.util.Vector;
0060:
0061: import org.apache.regexp.RECompiler;
0062: import org.apache.regexp.REProgram;
0063: import org.apache.regexp.RESyntaxException;
0064: import com.workingdogs.village.Record;
0065:
0066: // Turbine classes
0067: import org.apache.torque.NoRowsException;
0068: import org.apache.torque.TorqueException;
0069: import org.apache.torque.om.ComboKey;
0070: import org.apache.torque.om.SimpleKey;
0071: import org.apache.torque.om.BaseObject;
0072: import org.apache.torque.manager.MethodResultCache;
0073: import org.apache.torque.util.Criteria;
0074: import org.apache.fulcrum.security.entity.Group;
0075: import org.apache.fulcrum.localization.Localization;
0076: import org.apache.turbine.Turbine;
0077: import org.tigris.scarab.tools.localization.L10NKey;
0078:
0079: // Scarab classes
0080: import org.tigris.scarab.tools.localization.L10NKeySet;
0081: import org.tigris.scarab.tools.localization.L10NMessage;
0082: import org.tigris.scarab.tools.localization.Localizable;
0083: import org.tigris.scarab.util.ScarabException;
0084: import org.tigris.scarab.util.ValidationException;
0085: import org.tigris.scarab.util.ScarabConstants;
0086: import org.tigris.scarab.services.security.ScarabSecurity;
0087: import org.tigris.scarab.services.cache.ScarabCache;
0088: import org.tigris.scarab.workflow.WorkflowFactory;
0089: import org.tigris.scarab.reports.ReportBridge;
0090:
0091: /**
0092: * <p>
0093: * The ScarabModule class is the focal point for dealing with
0094: * Modules. It implements the concept of a Module which is a
0095: * single module and is the base interface for all Modules. In code,
0096: * one should <strong>never reference ScarabModule directly</strong>
0097: * -- use its Module interface instead. This allows us to swap
0098: * out Module implementations by modifying the Scarab.properties
0099: * file.
0100: * </p>
0101: *
0102: * <p>This class is the base class for
0103: * <code>org.tigris.scarab.om.ScarabModule</code>. BaseScarabModule extends
0104: * this class and that definition is defined in the scarab-schema.xml
0105: * which is used by Torque to generated BaseScarabModule.</p>
0106: *
0107: * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
0108: * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
0109: * @version $Id: AbstractScarabModule.java 10382 2006-12-13 17:02:35Z dabbous $
0110: */
0111: public abstract class AbstractScarabModule extends BaseObject implements
0112: Module, Comparable {
0113: private static int moduleCodeLength;
0114: // the following Strings are method names that are used in caching results
0115: protected static final String GET_R_MODULE_ATTRIBUTES = "getRModuleAttributes";
0116: protected static final String GET_DEDUPE_GROUPS_WITH_ATTRIBUTES = "getDedupeGroupsWithAttributes";
0117: protected static final String GET_SAVED_REPORTS = "getSavedReports";
0118: protected static final String GET_DEFAULT_RMODULE_USERATTRIBUTES = "getDefaultRModuleUserAttributes";
0119: protected static final String GET_ISSUE_TYPES = "getIssueTypes";
0120: protected static final String GET_NAV_ISSUE_TYPES = "getNavIssueTypes";
0121: protected static final String GET_ALL_R_MODULE_OPTIONS = "getAllRModuleOptions";
0122: protected static final String GET_LEAF_R_MODULE_OPTIONS = "getLeafRModuleOptions";
0123: protected static final String GET_R_MODULE_ISSUE_TYPES = "getRModuleIssueTypes";
0124: protected static final String GET_R_MODULE_ISSUE_TYPE = "getRModuleIssueType";
0125: protected static final String GET_TEMPLATE_TYPES = "getTemplateTypes";
0126: protected static final String GET_UNAPPROVED_QUERIES = "getUnapprovedQueries";
0127: protected static final String GET_UNAPPROVED_TEMPLATES = "getUnapprovedTemplates";
0128: protected static final String GET_AVAILABLE_ISSUE_TYPES = "getAvailableIssueTypes";
0129:
0130: private static final L10NKey VALIDATION_MESSAGE = new L10NKey(
0131: "ModuleValidationMessage");
0132:
0133: private List parentModules = null;
0134:
0135: /** set to true while the setInitialAttributesAndIssueTypes() method is in process */
0136: private boolean isInitializing = false;
0137:
0138: /**
0139: * Should be called when the parentage is modified.
0140: */
0141: protected void resetAncestors() {
0142: parentModules = null;
0143: }
0144:
0145: /**
0146: * The 'long' name of the module, includes the parents.
0147: */
0148: private String name = null;
0149:
0150: /**
0151: * @see org.tigris.scarab.om.Module#getUsers(String)
0152: */
0153: public abstract ScarabUser[] getUsers(String permission)
0154: throws TorqueException;
0155:
0156: /**
0157: * @see org.tigris.scarab.om.Module#getUsers(String)
0158: */
0159: public abstract ScarabUser[] getUsers(List permissions)
0160: throws TorqueException;
0161:
0162: /**
0163: * @return The unadorned real name of this module; never
0164: * <code>null</code>.
0165: * @see #getName()
0166: */
0167: public abstract String getRealName();
0168:
0169: public abstract Integer getModuleId();
0170:
0171: /**
0172: * This method is an implementation of the Group.getName() method
0173: * and returns a module along with its ancestors
0174: */
0175: public String getName() {
0176: if (name == null) {
0177: boolean isRoot = getModuleId().equals(ROOT_ID);
0178: if (isRoot) {
0179: return getRealName();
0180: }
0181: StringBuffer sb = new StringBuffer();
0182: List parents = null;
0183: try {
0184: parents = getAncestors();
0185: } catch (Exception e) {
0186: e.printStackTrace();
0187: getLog().error(e);
0188: return null;
0189: }
0190: Iterator itr = parents.iterator();
0191: boolean firstTime = true;
0192: while (itr.hasNext()) {
0193: Module me = (Module) itr.next();
0194: if (!firstTime) {
0195: sb.append(NAME_DELIMINATOR.getMessage());
0196: }
0197: sb.append(me.getRealName());
0198: firstTime = false;
0199: }
0200: // Make sure we have parents and if we are root,
0201: // don't show ourselves again.
0202: if (parents.size() >= 1 && !isRoot) {
0203: sb.append(NAME_DELIMINATOR.getMessage());
0204: }
0205: // If we are root, don't show ourselves again.
0206: if (!isRoot) {
0207: sb.append(getRealName());
0208: }
0209: name = sb.toString();
0210: }
0211: return name;
0212: }
0213:
0214: /**
0215: * This method is an implementation of the Group.setName() method
0216: */
0217: public void setName(String name) {
0218: this .name = name;
0219: }
0220:
0221: /**
0222: * Creates a new Issue.
0223: */
0224: public Issue getNewIssue(IssueType issueType)
0225: throws TorqueException {
0226: Issue issue = Issue.getNewInstance(this , issueType);
0227: issue.setDeleted(false);
0228: return issue;
0229: }
0230:
0231: /**
0232: * Returns this ModuleEntities ancestors in ascending order.
0233: * It does not return the 0 parent though.
0234: */
0235: public synchronized List getAncestors() throws TorqueException {
0236: if (parentModules == null) {
0237: parentModules = new ArrayList();
0238: Module parent = getParent();
0239: if (parent != null && !parent.getModuleId().equals(ROOT_ID)
0240: && !isEndlessLoop(parent)) {
0241: addAncestors(parent);
0242: }
0243: }
0244: return parentModules;
0245: }
0246:
0247: /**
0248: * recursive helper method for getAncestors()
0249: */
0250: private void addAncestors(Module module) throws TorqueException {
0251: if (!module.getParentId().equals(ROOT_ID)) {
0252: addAncestors(module.getParent());
0253: }
0254: parentModules.add(module);
0255: }
0256:
0257: /**
0258: * check for endless loops where Module A > Module B > Module A
0259: */
0260: public boolean isEndlessLoop(Module parent) throws TorqueException {
0261: if (parent.getModuleId() != ROOT_ID) {
0262: Module parentParent = parent.getParent();
0263: if (parentParent != null && parentParent == this ) {
0264: return true;
0265: }
0266: }
0267: return false;
0268: }
0269:
0270: /**
0271: * Creates new attribute group.
0272: */
0273: public AttributeGroup createNewGroup(IssueType issueType)
0274: throws TorqueException {
0275: return issueType.createNewGroup(this );
0276: }
0277:
0278: /**
0279: * This method is used within Wizard1.vm to get a list of attribute
0280: * groups which are marked as dedupe and have a list of attributes
0281: * in them.
0282: */
0283: public List getDedupeGroupsWithAttributes(IssueType issueType)
0284: throws TorqueException {
0285: List result = null;
0286: if (issueType == null) {
0287: result = new Vector(0);
0288: } else {
0289: Object obj = getMethodResult().get(this ,
0290: GET_DEDUPE_GROUPS_WITH_ATTRIBUTES, issueType);
0291: if (obj == null) {
0292: List attributeGroups = issueType.getAttributeGroups(
0293: this , true);
0294: result = new ArrayList(attributeGroups.size());
0295: for (Iterator itr = attributeGroups.iterator(); itr
0296: .hasNext();) {
0297: AttributeGroup ag = (AttributeGroup) itr.next();
0298: if (ag.getDedupe() && !ag.getAttributes().isEmpty()) {
0299: result.add(ag);
0300: }
0301: }
0302: getMethodResult().put(result, this ,
0303: GET_DEDUPE_GROUPS_WITH_ATTRIBUTES, issueType);
0304: } else {
0305: result = (List) obj;
0306: }
0307: }
0308: return result;
0309: }
0310:
0311: /**
0312: * List of active dedupe attribute groups associated with this module.
0313: */
0314: public List getDedupeAttributeGroups(IssueType issueType)
0315: throws TorqueException {
0316: return getDedupeAttributeGroups(issueType, true);
0317: }
0318:
0319: /**
0320: * List of attribute groups associated with this module.
0321: */
0322: public List getDedupeAttributeGroups(IssueType issueType,
0323: boolean activeOnly) throws TorqueException {
0324: List groups = issueType.getAttributeGroups(this , activeOnly);
0325: List dedupeGroups = new ArrayList();
0326: for (int i = 0; i < groups.size(); i++) {
0327: AttributeGroup group = (AttributeGroup) groups.get(i);
0328: if (group.getDedupe()) {
0329: dedupeGroups.add(group);
0330: }
0331: }
0332: return dedupeGroups;
0333: }
0334:
0335: /**
0336: * Gets the sequence where the dedupe screen fits between groups.
0337: */
0338: public int getDedupeSequence(IssueType issueType)
0339: throws TorqueException {
0340: return issueType.getDedupeSequence(this );
0341: }
0342:
0343: public ScarabUser[] getEligibleIssueReporters()
0344: throws TorqueException {
0345: return getUsers(ScarabSecurity.ISSUE__ENTER);
0346: }
0347:
0348: /**
0349: * The users who are possible candidates as values for the given
0350: * attribute. An eligible user is determined by checking for users that
0351: * have the permission associated with the attribute.
0352: *
0353: * @param attribute an <code>Attribute</code> value
0354: * @return a <code>ScarabUser[]</code> value
0355: * @exception ScarabException if the attribute has no associated permission
0356: * @exception Exception if an error occurs
0357: */
0358: public ScarabUser[] getEligibleUsers(Attribute attribute)
0359: throws TorqueException, ScarabException {
0360: ScarabUser[] users = null;
0361: if (attribute.isUserAttribute()) {
0362: String permission = attribute.getPermission();
0363: if (permission == null) {
0364: throw new ScarabException(
0365: L10NKeySet.ExceptionNoAttributePermission,
0366: attribute.getName());
0367: } else {
0368: users = getUsers(permission);
0369: }
0370: }
0371: return users;
0372: }
0373:
0374: /**
0375: * Set this module's immediate parent module
0376: */
0377: public abstract void setParent(Module v) throws TorqueException;
0378:
0379: /**
0380: * Get this module's immediate parent module
0381: */
0382: public abstract Module getParent() throws TorqueException;
0383:
0384: /**
0385: * List of saved reports associated with this module and
0386: * created by this user.
0387: */
0388: public List getSavedReports(final ScarabUser user)
0389: throws TorqueException, ScarabException {
0390: List reports = null;
0391: final Object obj = ScarabCache.get(this , GET_SAVED_REPORTS,
0392: user);
0393: if (obj == null) {
0394: final Criteria crit = new Criteria().add(
0395: ReportPeer.DELETED, 0);
0396: final Criteria.Criterion cc = crit.getNewCriterion(
0397: ReportPeer.SCOPE_ID, Scope.MODULE__PK,
0398: Criteria.EQUAL);
0399: cc.and(crit.getNewCriterion(ReportPeer.MODULE_ID,
0400: getModuleId(), Criteria.EQUAL));
0401: final Criteria.Criterion personalcc = crit.getNewCriterion(
0402: ReportPeer.SCOPE_ID, Scope.PERSONAL__PK,
0403: Criteria.EQUAL);
0404: personalcc.and(crit.getNewCriterion(ReportPeer.USER_ID,
0405: user.getUserId(), Criteria.EQUAL));
0406: final Criteria.Criterion personalmodulecc = crit
0407: .getNewCriterion(ReportPeer.MODULE_ID,
0408: getModuleId(), Criteria.EQUAL);
0409: personalmodulecc.or(crit.getNewCriterion(
0410: ReportPeer.MODULE_ID, null, Criteria.EQUAL));
0411: personalcc.and(personalmodulecc);
0412: cc.or(personalcc);
0413: crit.add(cc);
0414: crit.addAscendingOrderByColumn(ReportPeer.SCOPE_ID);
0415: final List torqueReports = ReportPeer.doSelect(crit);
0416: // create ReportBridge's from torque Reports.
0417: if (!torqueReports.isEmpty()) {
0418: reports = new ArrayList(torqueReports.size());
0419: for (Iterator i = torqueReports.iterator(); i.hasNext();) {
0420: final Report torqueReport = (Report) i.next();
0421: try {
0422: reports.add(new ReportBridge(torqueReport));
0423: } catch (org.xml.sax.SAXException e) {
0424: getLog()
0425: .warn(
0426: "Could not parse the report id="
0427: + torqueReport
0428: .getReportId()
0429: + ", so it has been marked as deleted.");
0430: torqueReport.setDeleted(true);
0431: torqueReport.save();
0432: } catch (Exception e) {
0433: throw new ScarabException(
0434: L10NKeySet.ExceptionGeneral, e);
0435: }
0436: }
0437: } else {
0438: reports = Collections.EMPTY_LIST;
0439: }
0440:
0441: ScarabCache.put(reports, this , GET_SAVED_REPORTS, user);
0442: } else {
0443: reports = (List) obj;
0444: }
0445: return reports;
0446: }
0447:
0448: /**
0449: * Gets a list of attributes for this module with a specific
0450: * issue type.
0451: */
0452: public List getAttributes(final IssueType issueType)
0453: throws TorqueException {
0454: final Criteria crit = new Criteria();
0455: crit.add(RModuleAttributePeer.ISSUE_TYPE_ID, issueType
0456: .getIssueTypeId());
0457: return getAttributes(crit);
0458: }
0459:
0460: /**
0461: * gets a list of all of the Attributes in a Module based on the Criteria.
0462: */
0463: public List getAttributes(final Criteria criteria)
0464: throws TorqueException {
0465: final List moduleAttributes = getRModuleAttributes(criteria);
0466: final List attributes = new ArrayList(moduleAttributes.size());
0467: for (int i = 0; i < moduleAttributes.size(); i++) {
0468: attributes.add(((RModuleAttribute) moduleAttributes.get(i))
0469: .getAttribute());
0470: }
0471: return attributes;
0472: }
0473:
0474: /**
0475: * gets a list of all of the User Attributes in a Module.
0476: */
0477: public List getUserAttributes(IssueType issueType)
0478: throws TorqueException {
0479: return getUserAttributes(issueType, true);
0480: }
0481:
0482: /**
0483: * gets a list of all of the User Attributes in a Module.
0484: */
0485: public List getUserAttributes(IssueType issueType,
0486: boolean activeOnly) throws TorqueException {
0487: List rModuleAttributes = getRModuleAttributes(issueType,
0488: activeOnly, USER);
0489: List userAttributes = new ArrayList();
0490:
0491: for (int i = 0; i < rModuleAttributes.size(); i++) {
0492: Attribute att = ((RModuleAttribute) rModuleAttributes
0493: .get(i)).getAttribute();
0494: userAttributes.add(att);
0495: }
0496: return userAttributes;
0497: }
0498:
0499: /**
0500: * gets a list of permissions associated with the User Attributes
0501: * that are active for this Module.
0502: */
0503: public List getUserPermissions(IssueType issueType)
0504: throws TorqueException {
0505: List userAttrs = getUserAttributes(issueType, true);
0506: List permissions = new ArrayList();
0507: for (int i = 0; i < userAttrs.size(); i++) {
0508: String permission = ((Attribute) userAttrs.get(i))
0509: .getPermission();
0510: if (!permissions.contains(permission)) {
0511: permissions.add(permission);
0512: }
0513: }
0514: return permissions;
0515: }
0516:
0517: /**
0518: * FIXME: can this be done more efficently?
0519: * gets highest sequence number for module-attribute map
0520: * so that a new RModuleAttribute can be added at the end.
0521: */
0522: public int getLastAttribute(IssueType issueType,
0523: String attributeType) throws TorqueException {
0524: List moduleAttributes = getRModuleAttributes(issueType, false,
0525: attributeType);
0526: int last = 0;
0527:
0528: for (int i = 0; i < moduleAttributes.size(); i++) {
0529: int order = ((RModuleAttribute) moduleAttributes.get(i))
0530: .getOrder();
0531: if (order > last) {
0532: last = order;
0533: }
0534: }
0535: return last;
0536: }
0537:
0538: /**
0539: * FIXME: can this be done more efficently?
0540: * gets highest sequence number for module-attribute map
0541: * so that a new RModuleAttribute can be added at the end.
0542: */
0543: public int getLastAttributeOption(Attribute attribute,
0544: IssueType issueType) throws TorqueException {
0545: List moduleOptions = getRModuleOptions(attribute, issueType);
0546: int last = 0;
0547: for (int i = 0; i < moduleOptions.size(); i++) {
0548: int order = ((RModuleOption) moduleOptions.get(i))
0549: .getOrder();
0550: if (order > last) {
0551: last = order;
0552: }
0553: }
0554: return last;
0555: }
0556:
0557: /*
0558: * shift all the module options by 1 for all the non-active options with
0559: * an order higher or equal than offset
0560: */
0561: public void shiftAttributeOption(Attribute attribute,
0562: IssueType issueType, int offset) throws TorqueException {
0563: List moduleOptions = getRModuleOptions(attribute, issueType,
0564: false);
0565: RModuleOption rmo;
0566: for (int i = 0; i < moduleOptions.size(); i++) {
0567: rmo = (RModuleOption) moduleOptions.get(i);
0568: int order = rmo.getOrder();
0569: if (order >= offset && !rmo.getActive()) {
0570: rmo.setOrder(order + 1);
0571: rmo.save();
0572: }
0573: }
0574: }
0575:
0576: /**
0577: * gets a list of all of the global Attributes that are not
0578: * associated with this module and issue type
0579: */
0580: public List getAvailableAttributes(IssueType issueType,
0581: String attributeType) throws TorqueException {
0582: List allAttributes = AttributePeer.getAttributes(attributeType);
0583: List availAttributes = new ArrayList();
0584: List rModuleAttributes = getRModuleAttributes(issueType, false,
0585: attributeType);
0586: List moduleAttributes = new ArrayList();
0587: if (rModuleAttributes.isEmpty()) {
0588: availAttributes = allAttributes;
0589: } else {
0590: for (int i = 0; i < rModuleAttributes.size(); i++) {
0591: moduleAttributes
0592: .add(((RModuleAttribute) rModuleAttributes
0593: .get(i)).getAttribute());
0594: }
0595:
0596: for (int i = 0; i < allAttributes.size(); i++) {
0597: Attribute att = (Attribute) allAttributes.get(i);
0598: if (!moduleAttributes.contains(att)) {
0599: availAttributes.add(att);
0600: }
0601: }
0602: }
0603: return availAttributes;
0604: }
0605:
0606: /**
0607: * gets a list of all of the Attribute options that are not
0608: * associated with this module and attribute.
0609: */
0610: public List getAvailableAttributeOptions(Attribute attribute,
0611: IssueType issueType) throws TorqueException {
0612: List rModuleOptions = getRModuleOptions(attribute, issueType,
0613: false);
0614: List moduleOptions = new ArrayList();
0615: if (rModuleOptions != null) {
0616: for (int i = 0; i < rModuleOptions.size(); i++) {
0617: moduleOptions.add(((RModuleOption) rModuleOptions
0618: .get(i)).getAttributeOption());
0619: }
0620: }
0621:
0622: List allOptions = attribute.getAttributeOptions(true);
0623: List availOptions = new ArrayList();
0624:
0625: for (int i = 0; i < allOptions.size(); i++) {
0626: AttributeOption option = (AttributeOption) allOptions
0627: .get(i);
0628: if (!moduleOptions.contains(option) && !option.getDeleted()) {
0629: availOptions.add(option);
0630: }
0631: }
0632: return availOptions;
0633: }
0634:
0635: /**
0636: * Returns default issue list attributes for this module.
0637: */
0638: public List getDefaultRModuleUserAttributes(IssueType issueType)
0639: throws TorqueException {
0640: List result = null;
0641: Object obj = ScarabCache.get(this ,
0642: GET_DEFAULT_RMODULE_USERATTRIBUTES, issueType);
0643: if (obj == null) {
0644: result = new LinkedList();
0645: Attribute[] attributes = new Attribute[3];
0646: int count = 0;
0647: attributes[count++] = issueType
0648: .getDefaultTextAttribute(this );
0649: if (attributes[0] == null) {
0650: count = 0;
0651: }
0652: List rma1s = getRModuleAttributes(issueType, true, NON_USER);
0653: Iterator i = rma1s.iterator();
0654: while (i.hasNext()) {
0655: Attribute a = ((RModuleAttribute) i.next())
0656: .getAttribute();
0657: if (!a.isTextAttribute() || attributes[0] == null) {
0658: attributes[count++] = a;
0659: break;
0660: }
0661: }
0662:
0663: List rma2s = getRModuleAttributes(issueType, true, USER);
0664: i = rma2s.iterator();
0665: while (i.hasNext() && count < 3) {
0666: Attribute a = ((RModuleAttribute) i.next())
0667: .getAttribute();
0668: attributes[count++] = a;
0669: }
0670:
0671: // if we still have less than 3 attributes, give the non user
0672: // attributes another try
0673: i = rma1s.iterator();
0674: while (i.hasNext() && count < 3) {
0675: Attribute a = ((RModuleAttribute) i.next())
0676: .getAttribute();
0677: if (!a.equals(attributes[0])
0678: && !a.equals(attributes[1])) {
0679: attributes[count++] = a;
0680: }
0681: }
0682:
0683: for (int j = 0; j < attributes.length; j++) {
0684: if (attributes[j] != null) {
0685: RModuleUserAttribute rmua = RModuleUserAttributeManager
0686: .getInstance();
0687: rmua.setAttribute(attributes[j]);
0688: rmua.setIssueType(issueType);
0689: rmua.setOrder(j + 1);
0690: result.add(rmua);
0691: }
0692: }
0693: ScarabCache.put(result, this ,
0694: GET_DEFAULT_RMODULE_USERATTRIBUTES, issueType);
0695: } else {
0696: result = (List) obj;
0697: }
0698: return result;
0699: }
0700:
0701: /**
0702: * This method is useful for getting an issue object
0703: * by a String id. It has some logic in it for appending
0704: * the Module Code as well as stripping spaces off the
0705: * id value using the String.trim() method.
0706: * @deprecated use IssueManager.getIssueById(String id, String defaultCode)
0707: */
0708: public Issue getIssueById(String id) throws TorqueException {
0709: return IssueManager.getIssueById(id, getCode());
0710: }
0711:
0712: /**
0713: * gets a list of the Issue Types for this module. only shows
0714: * active issue types
0715: */
0716: public List getIssueTypes() throws TorqueException {
0717: return getIssueTypes(true);
0718: }
0719:
0720: /**
0721: * gets a list of the Issue Types for this module. only shows
0722: * active issue types
0723: */
0724: public List getIssueTypes(boolean activeOnly)
0725: throws TorqueException {
0726: List types = null;
0727: Boolean activeOnlyValue = activeOnly ? Boolean.TRUE
0728: : Boolean.FALSE;
0729: Object obj = ScarabCache.get(this , GET_ISSUE_TYPES,
0730: activeOnlyValue);
0731: if (obj == null) {
0732: Criteria crit = new Criteria();
0733: crit.addJoin(RModuleIssueTypePeer.ISSUE_TYPE_ID,
0734: IssueTypePeer.ISSUE_TYPE_ID);
0735: crit.add(RModuleIssueTypePeer.MODULE_ID, getModuleId());
0736: if (activeOnly) {
0737: crit.add(RModuleIssueTypePeer.ACTIVE, true);
0738: }
0739: crit.add(IssueTypePeer.PARENT_ID, 0);
0740: crit.add(IssueTypePeer.DELETED, 0);
0741: crit
0742: .addAscendingOrderByColumn(RModuleIssueTypePeer.PREFERRED_ORDER);
0743: types = IssueTypePeer.doSelect(crit);
0744: ScarabCache.put(types, this , "getIssueTypes",
0745: activeOnlyValue);
0746: } else {
0747: types = (List) obj;
0748: }
0749: return types;
0750: }
0751:
0752: /**
0753: * gets a list of the Issue Types for this module.
0754: * that get listed in the left navigation. only shows active issue types.
0755: */
0756: public List getNavIssueTypes() throws TorqueException {
0757: List types = null;
0758: Object obj = getMethodResult().get(this , GET_NAV_ISSUE_TYPES);
0759: if (obj == null) {
0760: Criteria crit = new Criteria();
0761: crit.addJoin(RModuleIssueTypePeer.ISSUE_TYPE_ID,
0762: IssueTypePeer.ISSUE_TYPE_ID);
0763: crit.add(RModuleIssueTypePeer.MODULE_ID, getModuleId());
0764: crit.add(RModuleIssueTypePeer.ACTIVE, true);
0765: crit.add(RModuleIssueTypePeer.DISPLAY, true);
0766: crit.add(IssueTypePeer.PARENT_ID, 0);
0767: crit.add(IssueTypePeer.DELETED, 0);
0768: crit
0769: .addAscendingOrderByColumn(RModuleIssueTypePeer.PREFERRED_ORDER);
0770: types = IssueTypePeer.doSelect(crit);
0771: getMethodResult().put(types, this , GET_NAV_ISSUE_TYPES);
0772: } else {
0773: types = (List) obj;
0774: }
0775: return types;
0776: }
0777:
0778: /**
0779: * gets a list of all of the issue types that are not associated with
0780: * this module
0781: */
0782: public List getAvailableIssueTypes() throws TorqueException {
0783: List availIssueTypes = null;
0784: Object obj = ScarabCache.get(this , GET_AVAILABLE_ISSUE_TYPES);
0785: if (obj == null) {
0786: availIssueTypes = new ArrayList();
0787: List allIssueTypes = IssueTypePeer.getAllIssueTypes(false);
0788: List currentIssueTypes = getIssueTypes(false);
0789: Iterator iter = allIssueTypes.iterator();
0790: while (iter.hasNext()) {
0791: IssueType issueType = (IssueType) iter.next();
0792: if (IssueTypePeer.getRootKey().equals(
0793: issueType.getParentId())
0794: && !IssueTypePeer.getRootKey().equals(
0795: issueType.getIssueTypeId())
0796: && !currentIssueTypes.contains(issueType)) {
0797: availIssueTypes.add(issueType);
0798: }
0799: }
0800: ScarabCache.put(availIssueTypes, this ,
0801: GET_AVAILABLE_ISSUE_TYPES);
0802: } else {
0803: availIssueTypes = (List) obj;
0804: }
0805: return availIssueTypes;
0806: }
0807:
0808: /**
0809: * Returns RModuleAttributes associated with this module through the
0810: * foreign key in the schema. This method will return an empty list, if the
0811: * RModuleAttributes are inherited from its parent. Will not return an
0812: * RModuleAttribute if the Attribute is deleted. NOTE: Do not try to add caching
0813: * to this method as it seems to break things when an attribute is changed on
0814: * an existing issue. (JSS)
0815: */
0816: public List getRModuleAttributes(Criteria crit)
0817: throws TorqueException {
0818: crit.add(RModuleAttributePeer.MODULE_ID, getModuleId());
0819: crit.addJoin(RModuleAttributePeer.ATTRIBUTE_ID,
0820: AttributePeer.ATTRIBUTE_ID);
0821: crit.add(AttributePeer.DELETED, false);
0822: return RModuleAttributePeer.doSelect(crit);
0823: }
0824:
0825: /**
0826: * Overridden method.
0827: */
0828: public abstract List getRModuleOptions(Criteria crit)
0829: throws TorqueException;
0830:
0831: /**
0832: * Adds module-attribute mapping to module.
0833: */
0834: public RModuleAttribute addRModuleAttribute(
0835: final IssueType issueType, final Attribute attribute)
0836: throws TorqueException, ScarabException {
0837: String attributeType = null;
0838: attributeType = (attribute.isUserAttribute() ? USER : NON_USER);
0839:
0840: final RModuleAttribute rma = new RModuleAttribute();
0841: rma.setModuleId(getModuleId());
0842: rma.setIssueTypeId(issueType.getIssueTypeId());
0843: rma.setAttributeId(attribute.getAttributeId());
0844: rma.setOrder(getLastAttribute(issueType, attributeType) + 1);
0845: rma.setConditionsArray(attribute.getConditionsArray());
0846: rma.save();
0847: getRModuleAttributes(issueType, false, attributeType).add(rma);
0848:
0849: // Add to template type
0850: final IssueType templateType = IssueTypeManager.getInstance(
0851: issueType.getTemplateId(), false);
0852: final RModuleAttribute rma2 = new RModuleAttribute();
0853: rma2.setModuleId(getModuleId());
0854: rma2.setIssueTypeId(templateType.getIssueTypeId());
0855: rma2.setAttributeId(attribute.getAttributeId());
0856: rma2
0857: .setOrder(getLastAttribute(templateType, attributeType) + 1);
0858: rma2.save();
0859: return rma;
0860: }
0861:
0862: /**
0863: * Adds module-attribute-option mapping to module.
0864: */
0865: public RModuleOption addRModuleOption(IssueType issueType,
0866: AttributeOption option) throws TorqueException {
0867: RModuleOption rmo = new RModuleOption();
0868: rmo.setModuleId(getModuleId());
0869: rmo.setIssueTypeId(issueType.getIssueTypeId());
0870: rmo.setOptionId(option.getOptionId());
0871: rmo.setDisplayValue(option.getName());
0872: rmo.setOrder(getLastAttributeOption(option.getAttribute(),
0873: issueType) + 1);
0874: return rmo;
0875: }
0876:
0877: public RModuleAttribute getRModuleAttribute(Attribute attribute,
0878: IssueType issueType) throws TorqueException {
0879: RModuleAttribute rma = null;
0880: if (attribute != null && issueType != null) {
0881: List rmas = null;
0882: if (attribute.isUserAttribute()) {
0883: rmas = getRModuleAttributes(issueType, false, USER);
0884: } else {
0885: rmas = getRModuleAttributes(issueType, false, NON_USER);
0886: }
0887: Iterator i = rmas.iterator();
0888: while (i.hasNext()) {
0889: rma = (RModuleAttribute) i.next();
0890: if (rma.getAttribute().equals(attribute)) {
0891: break;
0892: } else {
0893: rma = null;
0894: }
0895: }
0896: }
0897: return rma;
0898: }
0899:
0900: /**
0901: * Overridden method. Calls the super method and if no results are returned
0902: * the call is passed on to the parent module.
0903: */
0904: public List getRModuleAttributes(IssueType issueType)
0905: throws TorqueException {
0906: return getRModuleAttributes(issueType, false);
0907: }
0908:
0909: /**
0910: * Returns true if module has attributes associated with issue type.
0911: */
0912: public boolean hasAttributes(final IssueType issueType)
0913: throws TorqueException, DataSetException {
0914: final Criteria crit = new Criteria();
0915: crit.add(RModuleAttributePeer.ISSUE_TYPE_ID, issueType
0916: .getIssueTypeId());
0917: crit.add(RModuleAttributePeer.MODULE_ID, getModuleId());
0918: crit.addSelectColumn("count("
0919: + RModuleAttributePeer.ATTRIBUTE_ID + ")");
0920: return ((Record) IssuePeer.doSelectVillageRecords(crit).get(0))
0921: .getValue(1).asInt() > 0;
0922: }
0923:
0924: /**
0925: * Overridden method. Calls the super method and if no results are
0926: * returned the call is passed on to the parent module.
0927: */
0928: public List getRModuleAttributes(IssueType issueType,
0929: boolean activeOnly) throws TorqueException {
0930: return getRModuleAttributes(issueType, activeOnly, "all");
0931: }
0932:
0933: public List getRModuleAttributes(IssueType issueType,
0934: boolean activeOnly, String attributeType)
0935: throws TorqueException {
0936: List rmas = null;
0937: Boolean activeBool = (activeOnly ? Boolean.TRUE : Boolean.FALSE);
0938: Object obj = getMethodResult().get(this ,
0939: GET_R_MODULE_ATTRIBUTES, issueType, activeBool,
0940: attributeType);
0941: if (obj == null) {
0942: Criteria crit = new Criteria();
0943: crit.add(RModuleAttributePeer.ISSUE_TYPE_ID, issueType
0944: .getIssueTypeId());
0945: crit.add(RModuleAttributePeer.MODULE_ID, getModuleId());
0946: crit
0947: .addAscendingOrderByColumn(RModuleAttributePeer.PREFERRED_ORDER);
0948: crit
0949: .addAscendingOrderByColumn(RModuleAttributePeer.DISPLAY_VALUE);
0950:
0951: if (activeOnly) {
0952: crit.add(RModuleAttributePeer.ACTIVE, true);
0953: }
0954:
0955: crit.addJoin(AttributePeer.ATTRIBUTE_ID,
0956: RModuleAttributePeer.ATTRIBUTE_ID);
0957: if (USER.equals(attributeType)) {
0958: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID,
0959: AttributeTypePeer.USER_TYPE_KEY);
0960: } else if (NON_USER.equals(attributeType)) {
0961: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID,
0962: AttributeTypePeer.USER_TYPE_KEY,
0963: Criteria.NOT_EQUAL);
0964: }
0965:
0966: rmas = RModuleAttributePeer.doSelect(crit);
0967: getMethodResult().put(rmas, this , GET_R_MODULE_ATTRIBUTES,
0968: issueType, activeBool, attributeType);
0969: } else {
0970: rmas = (List) obj;
0971: }
0972: return rmas;
0973: }
0974:
0975: /**
0976: * gets a list of all of the Attributes in this module.
0977: */
0978: public List getAllAttributes() throws TorqueException {
0979: return getAttributes(new Criteria());
0980: }
0981:
0982: /**
0983: * Return the list of option attributes (ATTRIBUTE_ID=5).
0984: * @return
0985: * @throws TorqueException
0986: */
0987: public List getAllOptionAttributes() throws TorqueException {
0988: Criteria crit = new Criteria();
0989: crit.addJoin(RModuleAttributePeer.ATTRIBUTE_ID,
0990: AttributePeer.ATTRIBUTE_ID);
0991: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID, 5);
0992: crit.add(AttributePeer.DELETED, false);
0993: crit.add(RModuleAttributePeer.ACTIVE, true);
0994: crit.add(RModuleAttributePeer.MODULE_ID, getModuleId());
0995: crit
0996: .addAscendingOrderByColumn(RModuleAttributePeer.DISPLAY_VALUE);
0997: crit.setDistinct();
0998: List result = AttributePeer.doSelect(crit);
0999: return result;
1000: }
1001:
1002: /**
1003: * Return the list of attribute options for attribute with given attributeId.
1004: * @return
1005: * @throws TorqueException
1006: * @throws ScarabException
1007: */
1008: public List getAllAttributeOptions(Integer attributeId)
1009: throws TorqueException, ScarabException {
1010: List result;
1011: if (attributeId == null) {
1012: this
1013: .getLog()
1014: .warn(
1015: "No attribute specified while fetching attribute options.");
1016: result = Collections.EMPTY_LIST;
1017: } else {
1018: int id = attributeId.intValue();
1019: Attribute attribute = Attribute.getInstance(id);
1020: if (attribute == null) {
1021: this .getLog().warn(
1022: "No options found for Attribute ["
1023: + attributeId + "]");
1024: // L10NMessage msg = new
1025: // L10NMessage(L10NKeySet.AttributeNotInSession,""+attributeId);
1026: // throw new ScarabException(msg);
1027: result = Collections.EMPTY_LIST;
1028: } else {
1029: // Integer attributeId = attribute.getAttributeId();
1030:
1031: Criteria crit = new Criteria();
1032: crit.add(AttributeOptionPeer.ATTRIBUTE_ID, attributeId);
1033: crit.add(AttributeOptionPeer.DELETED, false);
1034: result = AttributeOptionPeer.doSelect(crit);
1035: }
1036: }
1037: return result;
1038: }
1039:
1040: /**
1041: * gets a list of all of the active Attributes. ordered by name
1042: */
1043: public List getActiveAttributesByName(IssueType issueType,
1044: String attributeType) throws TorqueException {
1045: Criteria crit = new Criteria();
1046: crit.add(RModuleAttributePeer.MODULE_ID, getModuleId());
1047: crit.add(RModuleAttributePeer.ISSUE_TYPE_ID, issueType
1048: .getIssueTypeId());
1049: crit.addJoin(RModuleAttributePeer.ATTRIBUTE_ID,
1050: AttributePeer.ATTRIBUTE_ID);
1051: crit.add(AttributePeer.DELETED, false);
1052: crit.add(RModuleAttributePeer.ACTIVE, true);
1053: if (USER.equals(attributeType)) {
1054: crit.add(AttributePeer.ATTRIBUTE_TYPE_ID,
1055: AttributeTypePeer.USER_TYPE_KEY);
1056: crit.addJoin(AttributePeer.ATTRIBUTE_ID,
1057: RModuleAttributePeer.ATTRIBUTE_ID);
1058: } else if (NON_USER.equals(attributeType)) {
1059: crit
1060: .add(AttributePeer.ATTRIBUTE_TYPE_ID,
1061: AttributeTypePeer.USER_TYPE_KEY,
1062: Criteria.NOT_EQUAL);
1063: }
1064: crit
1065: .addAscendingOrderByColumn(RModuleAttributePeer.DISPLAY_VALUE);
1066: return AttributePeer.doSelect(crit);
1067: }
1068:
1069: public List getRModuleOptions(Attribute attribute,
1070: IssueType issueType) throws TorqueException {
1071: return getRModuleOptions(attribute, issueType, true);
1072: }
1073:
1074: public List getRModuleOptions(Attribute attribute,
1075: IssueType issueType, boolean activeOnly)
1076: throws TorqueException {
1077: List allRModuleOptions = null;
1078: allRModuleOptions = getAllRModuleOptions(attribute, issueType);
1079:
1080: if (allRModuleOptions != null) {
1081: if (activeOnly) {
1082: List activeRModuleOptions = new ArrayList(
1083: allRModuleOptions.size());
1084: for (int i = 0; i < allRModuleOptions.size(); i++) {
1085: RModuleOption rmo = (RModuleOption) allRModuleOptions
1086: .get(i);
1087: if (rmo.getActive()) {
1088: activeRModuleOptions.add(rmo);
1089: }
1090: }
1091: allRModuleOptions = activeRModuleOptions;
1092: }
1093: }
1094: return allRModuleOptions;
1095: }
1096:
1097: private List getAllRModuleOptions(Attribute attribute,
1098: IssueType issueType) throws TorqueException {
1099: if (attribute == null) {
1100: // during initilaization of a new query, no
1101: // attributes are available.
1102: // This check avoids a NLP
1103: return null;
1104: }
1105:
1106: List rModOpts = null;
1107: Object obj = ScarabCache.get(this , GET_ALL_R_MODULE_OPTIONS,
1108: attribute, issueType);
1109: if (obj == null) {
1110: List options = attribute.getAttributeOptions(true);
1111: Integer[] optIds = null;
1112: if (options == null) {
1113: optIds = new Integer[0];
1114: } else {
1115: optIds = new Integer[options.size()];
1116: }
1117: for (int i = optIds.length - 1; i >= 0; i--) {
1118: optIds[i] = ((AttributeOption) options.get(i))
1119: .getOptionId();
1120: }
1121:
1122: if (optIds.length > 0) {
1123: Criteria crit = new Criteria();
1124: crit.add(RModuleOptionPeer.ISSUE_TYPE_ID, issueType
1125: .getIssueTypeId());
1126: crit.add(RModuleOptionPeer.MODULE_ID, getModuleId());
1127: crit.addIn(RModuleOptionPeer.OPTION_ID, optIds);
1128: crit
1129: .addAscendingOrderByColumn(RModuleOptionPeer.PREFERRED_ORDER);
1130: crit
1131: .addAscendingOrderByColumn(RModuleOptionPeer.DISPLAY_VALUE);
1132: rModOpts = getRModuleOptions(crit);
1133: }
1134: ScarabCache.put(rModOpts, this , GET_ALL_R_MODULE_OPTIONS,
1135: attribute, issueType);
1136: } else {
1137: rModOpts = (List) obj;
1138: }
1139: return rModOpts;
1140: }
1141:
1142: public RModuleOption getRModuleOption(AttributeOption option,
1143: IssueType issueType) throws TorqueException {
1144: RModuleOption rmo = null;
1145: List rmos = getRModuleOptions(option.getAttribute(), issueType,
1146: false);
1147:
1148: RModuleOption testRMO = null;
1149: for (Iterator i = rmos.iterator(); i.hasNext();) {
1150: testRMO = (RModuleOption) i.next();
1151: if (testRMO.getAttributeOption().equals(option)) {
1152: rmo = testRMO;
1153: break;
1154: }
1155: }
1156: return rmo;
1157: }
1158:
1159: /**
1160: * Gets the modules list of attribute options. Uses the
1161: * RModuleOption table to do the join. returns null if there
1162: * is any error.
1163: */
1164: public List getAttributeOptions(Attribute attribute,
1165: IssueType issueType) throws TorqueException {
1166: List attributeOptions = null;
1167: try {
1168: List rModuleOptions = getOptionTree(attribute, issueType,
1169: false);
1170: attributeOptions = new ArrayList(rModuleOptions.size());
1171: for (int i = 0; i < rModuleOptions.size(); i++) {
1172: attributeOptions.add(((RModuleOption) rModuleOptions
1173: .get(i)).getAttributeOption());
1174: }
1175: } catch (Exception e) {
1176: }
1177: return attributeOptions;
1178: }
1179:
1180: public List getLeafRModuleOptions(Attribute attribute,
1181: IssueType issuetype) throws TorqueException {
1182: try {
1183: return getLeafRModuleOptions(attribute, issuetype, true);
1184: } catch (Exception e) {
1185: e.printStackTrace();
1186: }
1187: return null;
1188: }
1189:
1190: public List getLeafRModuleOptions(Attribute attribute,
1191: IssueType issueType, boolean activeOnly)
1192: throws TorqueException {
1193: List rModOpts = null;
1194: Boolean activeBool = (activeOnly ? Boolean.TRUE : Boolean.FALSE);
1195: Object obj = getMethodResult().get(this ,
1196: GET_LEAF_R_MODULE_OPTIONS, attribute, issueType,
1197: activeBool);
1198: if (obj == null) {
1199: rModOpts = getRModuleOptions(attribute, issueType,
1200: activeOnly);
1201: if (rModOpts != null) {
1202:
1203: // put options in a map for searching
1204: Map optionsMap = new HashMap(
1205: (int) (rModOpts.size() * 1.5));
1206: for (int i = rModOpts.size() - 1; i >= 0; i--) {
1207: RModuleOption rmo = (RModuleOption) rModOpts.get(i);
1208: optionsMap.put(rmo.getOptionId(), null);
1209: }
1210:
1211: // remove options with descendants in the list
1212: for (int i = rModOpts.size() - 1; i >= 0; i--) {
1213: AttributeOption option = ((RModuleOption) rModOpts
1214: .get(i)).getAttributeOption();
1215: List descendants = option.getChildren();
1216: if (descendants != null) {
1217: for (int j = descendants.size() - 1; j >= 0; j--) {
1218: AttributeOption descendant = (AttributeOption) descendants
1219: .get(j);
1220: if (optionsMap.containsKey(descendant
1221: .getOptionId())) {
1222: rModOpts.remove(i);
1223: break;
1224: }
1225: }
1226: }
1227: }
1228: getMethodResult().put(rModOpts, this ,
1229: GET_LEAF_R_MODULE_OPTIONS, attribute,
1230: issueType, activeBool);
1231: }
1232: } else {
1233: rModOpts = (List) obj;
1234: }
1235:
1236: return rModOpts;
1237: }
1238:
1239: /**
1240: * Gets a list of active RModuleOptions which have had their level
1241: * within the options for this module set.
1242: *
1243: * @param attribute an <code>Attribute</code> value
1244: * @return a <code>List</code> value
1245: * @exception TorqueException if an error occurs
1246: */
1247: public List getOptionTree(Attribute attribute, IssueType issueType)
1248: throws TorqueException {
1249: return getOptionTree(attribute, issueType, true);
1250: }
1251:
1252: /**
1253: * Gets a list of RModuleOptions which have had their level
1254: * within the options for this module set.
1255: *
1256: * @param attribute an <code>Attribute</code> value
1257: * @param activeOnly a <code>boolean</code> value
1258: * @return a <code>List</code> value
1259: * @exception TorqueException if an error occurs
1260: */
1261: public List getOptionTree(Attribute attribute, IssueType issueType,
1262: boolean activeOnly) throws TorqueException {
1263: // I think this code should place an option that had multiple parents -
1264: // OSX and Mac,BSD is usual example - into the list in multiple places
1265: // and it should have the level set differently for the two locations.
1266: // The code is currently only placing the option in the list once.
1267: // Since the behavior is not well spec'ed, leaving as it is. - jdm
1268:
1269: List moduleOptions = null;
1270: moduleOptions = getRModuleOptions(attribute, issueType,
1271: activeOnly);
1272: if (moduleOptions == null) {
1273: return moduleOptions;
1274: }
1275:
1276: int size = moduleOptions.size();
1277: List[] ancestors = new List[size];
1278:
1279: // find all ancestors
1280: for (int i = size - 1; i >= 0; i--) {
1281: AttributeOption option = ((RModuleOption) moduleOptions
1282: .get(i)).getAttributeOption();
1283: ancestors[i] = option.getAncestors();
1284: }
1285:
1286: for (int i = 0; i < size; i++) {
1287: RModuleOption moduleOption = (RModuleOption) moduleOptions
1288: .get(i);
1289: int level = 1;
1290: if (ancestors[i] != null) {
1291: // Set level for first ancestor as the option is only
1292: // shown once.
1293: for (int j = ancestors[i].size() - 1; j >= 0; j--) {
1294: AttributeOption ancestor = (AttributeOption) ancestors[i]
1295: .get(j);
1296:
1297: for (int k = 0; k < i; k++) {
1298: RModuleOption potentialParent = (RModuleOption) moduleOptions
1299: .get(k);
1300: if (ancestor.getOptionId().equals(
1301: potentialParent.getOptionId())
1302: && !ancestor.getOptionId().equals(
1303: moduleOption.getOptionId())) {
1304: moduleOption.setLevel(level++);
1305: }
1306: }
1307: }
1308: }
1309: }
1310:
1311: return moduleOptions;
1312: }
1313:
1314: /**
1315: * This method is implemented in ScarabModule
1316: */
1317: public abstract List getRModuleIssueTypes() throws TorqueException;
1318:
1319: public List getRModuleIssueTypes(String sortColumn,
1320: String sortPolarity) throws TorqueException {
1321: List types = null;
1322: Object obj = ScarabCache.get(this , GET_R_MODULE_ISSUE_TYPES);
1323: if (obj == null) {
1324: Criteria crit = new Criteria();
1325: crit.add(RModuleIssueTypePeer.MODULE_ID, getModuleId())
1326: .addJoin(RModuleIssueTypePeer.ISSUE_TYPE_ID,
1327: IssueTypePeer.ISSUE_TYPE_ID).add(
1328: IssueTypePeer.PARENT_ID, 0).add(
1329: IssueTypePeer.DELETED, 0);
1330: if (sortColumn.equals("name")) {
1331: if (sortPolarity.equals("desc")) {
1332: crit.addDescendingOrderByColumn(IssueTypePeer.NAME);
1333: } else {
1334: crit.addAscendingOrderByColumn(IssueTypePeer.NAME);
1335: }
1336: } else {
1337: // sortColumn defaults to sequence #
1338: if (sortPolarity.equals("desc")) {
1339: crit
1340: .addDescendingOrderByColumn(RModuleIssueTypePeer.PREFERRED_ORDER);
1341: } else {
1342: crit
1343: .addAscendingOrderByColumn(RModuleIssueTypePeer.PREFERRED_ORDER);
1344: }
1345: }
1346: types = RModuleIssueTypePeer.doSelect(crit);
1347: ScarabCache.put(types, this , GET_R_MODULE_ISSUE_TYPES);
1348: } else {
1349: types = (List) obj;
1350: }
1351: return types;
1352: }
1353:
1354: /**
1355: * Adds attribute options to a module.
1356: */
1357: public void addAttributeOption(final IssueType issueType,
1358: final AttributeOption option) throws TorqueException,
1359: ScarabException {
1360: final RModuleOption rmo = addRModuleOption(issueType, option);
1361: rmo.save();
1362: shiftAttributeOption(option.getAttribute(), issueType, rmo
1363: .getOrder());
1364:
1365: // add module-attributeoption mappings to template type
1366: final IssueType templateType = IssueTypeManager
1367: .getInstance(issueType.getTemplateId());
1368: final RModuleOption rmo2 = addRModuleOption(templateType,
1369: option);
1370: rmo2.save();
1371: //FIXME: is it useful to shift options for the templateType?
1372: //shiftAttributeOption(option.getAttribute(), templateType, rmo.getOrder());
1373:
1374: //if the cache is not cleared, when two options are added at the same time,
1375: //getLastAttributeOption does not take into account the newest active options.
1376: ScarabCache.clear();
1377: }
1378:
1379: public void setRmaBasedOnIssueType(final RIssueTypeAttribute ria)
1380: throws TorqueException, ScarabException {
1381: final RModuleAttribute rma = new RModuleAttribute();
1382: rma.setModuleId(getModuleId());
1383: rma.setIssueTypeId(ria.getIssueTypeId());
1384: rma.setAttributeId(ria.getAttributeId());
1385: rma.setActive(ria.getActive());
1386: rma.setRequired(ria.getRequired());
1387: rma.setOrder(ria.getOrder());
1388: rma.setQuickSearch(ria.getQuickSearch());
1389: rma.setDefaultTextFlag(ria.getDefaultTextFlag());
1390: rma.save();
1391: final RModuleAttribute rma2 = rma.copy();
1392: rma2.setModuleId(getModuleId());
1393: rma2.setIssueTypeId(ria.getIssueType().getTemplateId());
1394: rma2.setAttributeId(ria.getAttributeId());
1395: rma2.setActive(ria.getActive());
1396: rma2.setRequired(ria.getRequired());
1397: rma2.setOrder(ria.getOrder());
1398: rma2.setQuickSearch(ria.getQuickSearch());
1399: rma2.setDefaultTextFlag(ria.getDefaultTextFlag());
1400: rma2.save();
1401: }
1402:
1403: public void setRmoBasedOnIssueType(final RIssueTypeOption rio)
1404: throws TorqueException, ScarabException {
1405: final RModuleOption rmo = new RModuleOption();
1406: rmo.setModuleId(getModuleId());
1407: rmo.setIssueTypeId(rio.getIssueTypeId());
1408: rmo.setOptionId(rio.getOptionId());
1409: rmo.setActive(rio.getActive());
1410: rmo.setOrder(rio.getOrder());
1411: rmo.setWeight(rio.getWeight());
1412: rmo.save();
1413: final RModuleOption rmo2 = rmo.copy();
1414: rmo2.setModuleId(getModuleId());
1415: rmo2.setIssueTypeId(rio.getIssueType().getTemplateId());
1416: rmo2.setOptionId(rio.getOptionId());
1417: rmo2.setActive(rio.getActive());
1418: rmo2.setOrder(rio.getOrder());
1419: rmo2.setWeight(rio.getWeight());
1420: rmo2.save();
1421: }
1422:
1423: protected String getValidationMessage(String typeName, String detail) {
1424: final L10NMessage msg = new L10NMessage(VALIDATION_MESSAGE,
1425: typeName, getName(), detail);
1426: return msg.getMessage();
1427: }
1428:
1429: public boolean includesIssueType(final IssueType issueType)
1430: throws TorqueException, DataSetException {
1431: final Criteria crit = new Criteria();
1432: crit.add(RModuleIssueTypePeer.MODULE_ID, getModuleId());
1433: crit.add(RModuleIssueTypePeer.ISSUE_TYPE_ID, issueType
1434: .getIssueTypeId());
1435: return RModuleIssueTypePeer.count(crit) > 0;
1436: }
1437:
1438: /**
1439: * Adds an issue type to a module
1440: * Copies properties from the global issue type's settings
1441: */
1442: public void addIssueType(final IssueType issueType)
1443: throws TorqueException, ValidationException,
1444: DataSetException, ScarabException {
1445: // do some validation, refuse to add an issue type that is in a bad
1446: // state
1447: if (issueType == null) {
1448: throw new ValidationException(
1449: L10NKeySet.ExceptionIntegrityCheckFailure, "NULL",
1450: getName(), "Issue type was null");
1451: }
1452:
1453: // check that the issueType is not already added.
1454: if (includesIssueType(issueType)) {
1455: throw new ValidationException(
1456: L10NKeySet.ExceptionDuplicateIssueType, issueType,
1457: getName());
1458: }
1459:
1460: final String typeName = issueType.getName();
1461: // check attribute groups
1462: final List testGroups = issueType.getAttributeGroups(null,
1463: false);
1464: try {
1465: if (testGroups == null) {
1466: final Localizable l10nMessage = new L10NMessage(
1467: L10NKeySet.IssueTypeWasNull);
1468: throw new ValidationException(
1469: L10NKeySet.ExceptionIntegrityCheckFailure,
1470: typeName, getName(), l10nMessage);
1471: } else {
1472: for (Iterator i = testGroups.iterator(); i.hasNext();) {
1473: final AttributeGroup group = (AttributeGroup) i
1474: .next();
1475: // check attributes
1476: final List attrs = group.getAttributes();
1477: if (attrs != null) {
1478: for (Iterator j = attrs.iterator(); j.hasNext();) {
1479: // check attribute-attribute group maps
1480: final Attribute attr = (Attribute) j.next();
1481: if (attr == null) {
1482: final L10NMessage l10nMessage = new L10NMessage(
1483: L10NKeySet.AttributesContainsNull);
1484: throw new ValidationException(
1485: L10NKeySet.ExceptionIntegrityCheckFailure,
1486: typeName, getName(),
1487: l10nMessage);
1488: }
1489:
1490: // TODO: add workflow validation
1491:
1492: final RAttributeAttributeGroup raag = group
1493: .getRAttributeAttributeGroup(attr);
1494: if (raag == null) {
1495: final L10NMessage l10nMessage = new L10NMessage(
1496: L10NKeySet.AttributeMappingIsMissing,
1497: attr.getName());
1498: throw new ValidationException(
1499: L10NKeySet.ExceptionIntegrityCheckFailure,
1500: typeName, getName(),
1501: l10nMessage);
1502:
1503: }
1504:
1505: // check attribute-issue type maps
1506: final RIssueTypeAttribute ria = issueType
1507: .getRIssueTypeAttribute(attr);
1508: if (ria == null) {
1509: final L10NMessage l10nMessage = new L10NMessage(
1510: L10NKeySet.AttributeToIssueTypeMappingIsMissing,
1511: attr.getName());
1512: throw new ValidationException(
1513: L10NKeySet.ExceptionIntegrityCheckFailure,
1514: typeName, getName(),
1515: l10nMessage);
1516: }
1517:
1518: // check options
1519: final List rios = issueType
1520: .getRIssueTypeOptions(attr, false);
1521: if (rios != null) {
1522: for (Iterator k = rios.iterator(); k
1523: .hasNext();) {
1524: if (k.next() == null) {
1525: final L10NMessage l10nMessage = new L10NMessage(
1526: L10NKeySet.ListOfOptionsMissing,
1527: attr.getName());
1528: throw new ValidationException(
1529: L10NKeySet.ExceptionIntegrityCheckFailure,
1530: typeName, getName(),
1531: l10nMessage);
1532: }
1533: }
1534: }
1535: }
1536: }
1537: }
1538: }
1539: } catch (ValidationException ve) {
1540: throw ve;
1541: } catch (Exception e) {
1542: throw new ScarabException(L10NKeySet.ExceptionGeneral, e
1543: .getMessage(), e);
1544: }
1545:
1546: // okay we passed, start modifying tables
1547:
1548: // add module-issue type mapping
1549: final RModuleIssueType rmit = new RModuleIssueType();
1550: rmit.setModuleId(getModuleId());
1551: rmit.setIssueTypeId(issueType.getIssueTypeId());
1552: rmit.setActive(true);
1553: rmit.setDisplay(false);
1554: rmit.setOrder(getRModuleIssueTypes().size() + 1);
1555: rmit.setDedupe(issueType.getDedupe());
1556: rmit.save();
1557:
1558: // add user attributes
1559: final List userRIAs = issueType.getRIssueTypeAttributes(false,
1560: "user");
1561: for (int m = 0; m < userRIAs.size(); m++) {
1562: final RIssueTypeAttribute userRia = (RIssueTypeAttribute) userRIAs
1563: .get(m);
1564: setRmaBasedOnIssueType(userRia);
1565: }
1566:
1567: // add workflow
1568: WorkflowFactory.getInstance().addIssueTypeWorkflowToModule(
1569: this , issueType);
1570:
1571: // add attribute groups
1572: final List groups = issueType.getAttributeGroups(null, false);
1573: if (groups.isEmpty()) {
1574: // Create default groups
1575: final AttributeGroup ag = createNewGroup(issueType);
1576: ag.setOrder(1);
1577: ag.setDedupe(true);
1578: ag.setDescription(null);
1579: ag.save();
1580: final AttributeGroup ag2 = createNewGroup(issueType);
1581: ag2.setOrder(3);
1582: ag2.setDedupe(false);
1583: ag2.setDescription(null);
1584: ag2.save();
1585: } else {
1586: // Inherit attribute groups from issue type
1587: for (int i = 0; i < groups.size(); i++) {
1588: final AttributeGroup group = (AttributeGroup) groups
1589: .get(i);
1590: final AttributeGroup moduleGroup = group.copyGroup();
1591: moduleGroup.setModuleId(getModuleId());
1592: moduleGroup.setIssueTypeId(issueType.getIssueTypeId());
1593: moduleGroup.save();
1594:
1595: // add attributes
1596: final List attrs = group.getAttributes();
1597: if (attrs != null) {
1598: for (int j = 0; j < attrs.size(); j++) {
1599: // save attribute-attribute group maps
1600: final Attribute attr = (Attribute) attrs.get(j);
1601: final RAttributeAttributeGroup raag = group
1602: .getRAttributeAttributeGroup(attr);
1603: final RAttributeAttributeGroup moduleRaag = new RAttributeAttributeGroup();
1604: moduleRaag
1605: .setAttributeId(raag.getAttributeId());
1606: moduleRaag.setOrder(raag.getOrder());
1607: moduleRaag.setGroupId(moduleGroup
1608: .getAttributeGroupId());
1609: moduleRaag.save();
1610:
1611: // save attribute-module maps
1612: final RIssueTypeAttribute ria = issueType
1613: .getRIssueTypeAttribute(attr);
1614: setRmaBasedOnIssueType(ria);
1615:
1616: // save options
1617: final List rios = issueType
1618: .getRIssueTypeOptions(attr, false);
1619: if (rios != null) {
1620: for (int k = 0; k < rios.size(); k++) {
1621: final RIssueTypeOption rio = (RIssueTypeOption) rios
1622: .get(k);
1623: setRmoBasedOnIssueType(rio);
1624: }
1625: }
1626: }
1627: }
1628: }
1629: }
1630: }
1631:
1632: public RModuleIssueType getRModuleIssueType(
1633: final IssueType issueType) throws TorqueException {
1634: RModuleIssueType rmit = null;
1635: try {
1636: final SimpleKey[] keys = { SimpleKey.keyFor(getModuleId()),
1637: SimpleKey.keyFor(issueType.getIssueTypeId()) };
1638: rmit = RModuleIssueTypeManager.getInstance(new ComboKey(
1639: keys));
1640: } catch (NoRowsException e) {
1641: // ignore and return null, if the rmit does not exist
1642: }
1643: return rmit;
1644: }
1645:
1646: public List getTemplateTypes() throws TorqueException,
1647: ScarabException {
1648: List templateTypes = new ArrayList();
1649: final Object obj = ScarabCache.get(this , GET_TEMPLATE_TYPES);
1650: if (obj == null) {
1651: final Criteria crit = new Criteria();
1652: crit.add(RModuleIssueTypePeer.MODULE_ID, getModuleId())
1653: .addJoin(RModuleIssueTypePeer.ISSUE_TYPE_ID,
1654: IssueTypePeer.ISSUE_TYPE_ID).add(
1655: IssueTypePeer.DELETED, 0);
1656: final List rmits = RModuleIssueTypePeer.doSelect(crit);
1657: for (int i = 0; i < rmits.size(); i++) {
1658: final RModuleIssueType rmit = (RModuleIssueType) rmits
1659: .get(i);
1660: final IssueType templateType = rmit.getIssueType()
1661: .getTemplateIssueType();
1662: templateTypes.add(templateType);
1663: }
1664: ScarabCache.put(templateTypes, this , GET_TEMPLATE_TYPES);
1665: } else {
1666: templateTypes = (List) obj;
1667: }
1668: return templateTypes;
1669: }
1670:
1671: /**
1672: * Determines whether this module allows users to vote many times for
1673: * the same issue. This feature needs schema change to allow a
1674: * configuration screen. Currently only one vote per issue is supported
1675: *
1676: * @return false
1677: */
1678: public boolean allowsMultipleVoting() {
1679: return false;
1680: }
1681:
1682: /**
1683: * How many votes does the user have left to cast. Currently always
1684: * returns 1, so a user has unlimited voting rights. Should look to
1685: * UserVote for the answer when implemented properly.
1686: */
1687: public int getUnusedVoteCount(ScarabUser user) {
1688: return 1;
1689: }
1690:
1691: /**
1692: * Returns list of queries needing approval.
1693: */
1694: public List getUnapprovedQueries() throws TorqueException {
1695: List queries = null;
1696: Object obj = ScarabCache.get(this , GET_UNAPPROVED_QUERIES);
1697: if (obj == null) {
1698: Criteria crit = new Criteria(3);
1699: crit.add(QueryPeer.APPROVED, 0).add(QueryPeer.DELETED, 0)
1700: .add(QueryPeer.MODULE_ID, getModuleId());
1701: queries = QueryPeer.doSelect(crit);
1702: ScarabCache.put(queries, this , GET_UNAPPROVED_QUERIES);
1703: } else {
1704: queries = (List) obj;
1705: }
1706: return queries;
1707: }
1708:
1709: /**
1710: * Returns list of enter issue templates needing approval.
1711: */
1712: public List getUnapprovedTemplates() throws TorqueException {
1713: List templates = null;
1714: Object obj = ScarabCache.get(this , GET_UNAPPROVED_TEMPLATES);
1715: if (obj == null) {
1716: Criteria crit = new Criteria(3);
1717: crit.add(IssueTemplateInfoPeer.APPROVED, 0).addJoin(
1718: IssuePeer.ISSUE_ID, IssueTemplateInfoPeer.ISSUE_ID)
1719: .add(IssuePeer.DELETED, 0).add(IssuePeer.MOVED, 0)
1720: .add(IssuePeer.MODULE_ID, getModuleId());
1721: templates = IssuePeer.doSelect(crit);
1722: ScarabCache.put(templates, this , GET_UNAPPROVED_TEMPLATES);
1723: } else {
1724: templates = (List) obj;
1725: }
1726: return templates;
1727: }
1728:
1729: /**
1730: * for a new module: inherit issue types from parent module and
1731: * from the issue types marked as default
1732: * parent configuration takes precedence over default
1733: */
1734: protected void setInitialAttributesAndIssueTypes()
1735: throws TorqueException, DataSetException, ScarabException {
1736: isInitializing = true;
1737: ValidationException ve = null;
1738: try {
1739: // Add defaults for issue types and attributes
1740: // from parent module
1741: final Module parentModule = ModuleManager
1742: .getInstance(getParentId());
1743: inheritFromParent(parentModule);
1744:
1745: final List defaultIssueTypes = IssueTypePeer
1746: .getDefaultIssueTypes();
1747: for (int i = 0; i < defaultIssueTypes.size(); i++) {
1748: final IssueType defaultIssueType = (IssueType) defaultIssueTypes
1749: .get(i);
1750: if (!includesIssueType(defaultIssueType)) {
1751: try {
1752: addIssueType(defaultIssueType);
1753: } catch (ValidationException e) {
1754: // if one issue type is bad, continue with the rest, if
1755: // more than one bad issue type is found, stop.
1756: if (ve == null) {
1757: ve = e;
1758: } else {
1759: ve = new ValidationException(
1760: L10NKeySet.ExceptionMultipleProblems,
1761: ve.getMessage(), e);//WORK: what about the stack trace ?
1762: isInitializing = false;
1763: throw ve;
1764: }
1765: }
1766: }
1767: }
1768: } finally {
1769: isInitializing = false;
1770: }
1771: if (ve != null) {
1772: throw ve;
1773: }
1774: }
1775:
1776: /**
1777: * sets up attributes and issue types for this module based on.
1778: * the parent module
1779: */
1780: protected void inheritFromParent(final Module parentModule)
1781: throws TorqueException, ScarabException {
1782: final Integer newModuleId = getModuleId();
1783: AttributeGroup ag1;
1784: AttributeGroup ag2;
1785: RModuleAttribute rma1 = null;
1786: RModuleAttribute rma2 = null;
1787:
1788: //save RModuleAttributes for template types.
1789: final List templateTypes = parentModule.getTemplateTypes();
1790: for (int i = 0; i < templateTypes.size(); i++) {
1791: final IssueType it = (IssueType) templateTypes.get(i);
1792: final List rmas = parentModule.getRModuleAttributes(it);
1793: for (int j = 0; j < rmas.size(); j++) {
1794: rma1 = (RModuleAttribute) rmas.get(j);
1795: rma2 = rma1.copy();
1796: rma2.setModuleId(newModuleId);
1797: rma2.setAttributeId(rma1.getAttributeId());
1798: rma2.setIssueTypeId(rma1.getIssueTypeId());
1799: getLog().debug(
1800: "[ASM] Saving rma for new template type: "
1801: + rma2.getModuleId() + "-"
1802: + rma2.getIssueTypeId() + "-"
1803: + rma2.getAttributeId());
1804: rma2.save();
1805: }
1806: }
1807:
1808: // set module-issue type mappings
1809: final List rmits = parentModule.getRModuleIssueTypes();
1810: for (int i = 0; i < rmits.size(); i++) {
1811: final RModuleIssueType rmit1 = (RModuleIssueType) rmits
1812: .get(i);
1813: final RModuleIssueType rmit2 = rmit1.copy();
1814: rmit2.setModuleId(newModuleId);
1815: rmit2.save();
1816: final IssueType issueType = rmit1.getIssueType();
1817:
1818: // set attribute group defaults
1819: final List attributeGroups = issueType.getAttributeGroups(
1820: parentModule, true);
1821: for (int j = 0; j < attributeGroups.size(); j++) {
1822: ag1 = (AttributeGroup) attributeGroups.get(j);
1823: ag2 = ag1.copy();
1824: ag2.setModuleId(newModuleId);
1825: ag2.getRAttributeAttributeGroups().clear(); // are saved later
1826: ag2.save();
1827:
1828: final List attributes = ag1.getAttributes();
1829: for (int k = 0; k < attributes.size(); k++) {
1830: final Attribute attribute = (Attribute) attributes
1831: .get(k);
1832:
1833: // set attribute-attribute group defaults
1834: final RAttributeAttributeGroup raag1 = ag1
1835: .getRAttributeAttributeGroup(attribute);
1836: final RAttributeAttributeGroup raag2 = raag1.copy();
1837: raag2.setGroupId(ag2.getAttributeGroupId());
1838: raag2.setAttributeId(raag1.getAttributeId());
1839: raag2.setOrder(raag1.getOrder());
1840: raag2.save();
1841: }
1842: }
1843:
1844: // set module-attribute defaults
1845: final List rmas = parentModule
1846: .getRModuleAttributes(issueType);
1847: if (rmas != null && rmas.size() > 0) {
1848: for (int j = 0; j < rmas.size(); j++) {
1849: rma1 = (RModuleAttribute) rmas.get(j);
1850: rma2 = rma1.copy();
1851: rma2.setModuleId(newModuleId);
1852: rma2.setAttributeId(rma1.getAttributeId());
1853: rma2.setIssueTypeId(rma1.getIssueTypeId());
1854: rma2.save();
1855:
1856: // set module-option mappings
1857: final Attribute attribute = rma1.getAttribute();
1858: if (attribute.isOptionAttribute()) {
1859: final List rmos = parentModule
1860: .getRModuleOptions(attribute, issueType);
1861: if (rmos != null && rmos.size() > 0) {
1862: for (int m = 0; m < rmos.size(); m++) {
1863: final RModuleOption rmo1 = (RModuleOption) rmos
1864: .get(m);
1865: final RModuleOption rmo2 = rmo1.copy();
1866: rmo2.setOptionId(rmo1.getOptionId());
1867: rmo2.setModuleId(newModuleId);
1868: rmo2.setIssueTypeId(issueType
1869: .getIssueTypeId());
1870: rmo2.save();
1871:
1872: // Save module-option mappings for template types
1873: final RModuleOption rmo3 = rmo1.copy();
1874: rmo3.setOptionId(rmo1.getOptionId());
1875: rmo3.setModuleId(newModuleId);
1876: rmo3.setIssueTypeId(issueType
1877: .getTemplateId());
1878: rmo3.save();
1879: }
1880: }
1881: }
1882: }
1883: }
1884: }
1885: }
1886:
1887: /**
1888: * Determines whether this module is accepting new issues. This default
1889: * implementation allows new issues if the module has not been deleted.
1890: */
1891: public boolean allowsNewIssues() {
1892: return !getDeleted();
1893: }
1894:
1895: /**
1896: * Determines whether this module accepts issues. This default
1897: * implementation does allow issues.
1898: */
1899: public boolean allowsIssues() {
1900: return true;
1901: }
1902:
1903: /**
1904: * Returns true if no issue types are associated with this module, or if the module
1905: * is currently getting its initial values set.
1906: */
1907: public boolean isInitializing() throws TorqueException {
1908: return isInitializing;
1909: }
1910:
1911: /**
1912: * @see org.tigris.scarab.om.Module#isGlobalModule()
1913: */
1914: public boolean isGlobalModule() {
1915: return Module.ROOT_ID.equals(getModuleId());
1916: }
1917:
1918: // FIXME! should localize
1919: private static final String REGEX_PREFIX = "([:alpha:]+\\d+)|(issue|bug|artifact";
1920: private static final String REGEX_SUFFIX = ")\\s*#?([:alpha:]*\\d+)";
1921:
1922: public String getIssueRegexString() throws TorqueException {
1923: // regex = /(issue|bug)\s+#?\d+/i
1924: List rmitsList = getRModuleIssueTypes();
1925: StringBuffer regex = new StringBuffer(30 + 10 * rmitsList
1926: .size());
1927: regex.append(REGEX_PREFIX);
1928: Iterator rmits = rmitsList.iterator();
1929: while (rmits.hasNext()) {
1930: regex.append('|').append(
1931: ((RModuleIssueType) rmits.next()).getDisplayName());
1932: }
1933: regex.append(REGEX_SUFFIX);
1934: return regex.toString();
1935: }
1936:
1937: /**
1938: * @see org.tigris.scarab.om.Module#getIssueRegex()
1939: */
1940: public REProgram getIssueRegex() throws TorqueException {
1941: String regex = getIssueRegexString();
1942: RECompiler rec = new RECompiler();
1943: REProgram rep = null;
1944: try {
1945: rep = rec.compile(regex);
1946: } catch (RESyntaxException e) {
1947: getLog().error("Could not compile regex: " + regex, e);
1948: try {
1949: rep = rec.compile(REGEX_PREFIX + REGEX_SUFFIX);
1950: } catch (RESyntaxException ee) {
1951: // this should not happen, but it might when we localize
1952: getLog().error("Could not compile standard regex", ee);
1953: try {
1954: rep = rec.compile("[:alpha:]+\\d+");
1955: } catch (RESyntaxException eee) {
1956: // this will never happen, but log it, just in case
1957: getLog().error("Could not compile simple id regex",
1958: eee);
1959: }
1960: }
1961: }
1962: // FIXME: we should cache the above result
1963: return rep;
1964: }
1965:
1966: /**
1967: * All emails related to this module will have a copy sent to
1968: * this address. A system-wide default email address can be specified in
1969: * Scarab.properties with the key: scarab.email.archive.toAddress
1970: */
1971: public abstract String getArchiveEmail();
1972:
1973: /**
1974: * Simple implementation returns the single configured default locale
1975: * from TR.props. Will be replaced by a way to set this per module.
1976: *
1977: * @return a Locale selected for the Fulcrum Localization context
1978: */
1979: public Locale getLocale() {
1980: return ScarabConstants.DEFAULT_LOCALE;
1981: }
1982:
1983: /**
1984: * The default address that is used to fill out either the From or
1985: * ReplyTo header on emails related to this module. In many cases
1986: * the From field is taken as the user who acted that resulted in the
1987: * email, but replies should still go to the central location for
1988: * the module, so in this address would be used in the ReplyTo field.
1989: *
1990: * @return a <code>String[]</code> of length=2 where the first element
1991: * is a name such as "Scarab System" and the second is an email address.
1992: */
1993: public String[] getSystemEmail() {
1994: String name = Turbine.getConfiguration().getString(
1995: "scarab.email.default.fromName");
1996: if (name == null || name.length() == 0) {
1997: name = Localization.format(
1998: ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
1999: "DefaultEmailNameForModule", getRealName()
2000: .toUpperCase());
2001: }
2002:
2003: String email = Turbine.getConfiguration().getString(
2004: "scarab.email.default.fromAddress");
2005:
2006: if (email == null || email.length() == 0) {
2007: email = getArchiveEmail();
2008: }
2009: if (email == null || email.length() == 0) {
2010: email = "help@localhost";
2011: }
2012: String[] result = { name, email };
2013: return result;
2014: }
2015:
2016: /**
2017: * Used for ordering Groups.
2018: *
2019: * @param obj The Object to compare to.
2020: * @return -1 if the name of the other object is lexically greater than
2021: * this group, 1 if it is lexically lesser, 0 if they are equal.
2022: */
2023: public int compareTo(Object obj) {
2024: //TODO [HD] what about using instanceof, or
2025: // probably better delete the whole
2026: // if-block (see note n ScarabModule.compereTo()
2027: if (this .getClass() != obj.getClass()) {
2028: throw new ClassCastException(); //EXCEPTION
2029: }
2030: String name1 = ((Group) obj).getName();
2031: String name2 = this .getName();
2032:
2033: return name2.compareTo(name1);
2034: }
2035:
2036: public String toString() {
2037: String name = getName();
2038: if (name == null) {
2039: name = getRealName();
2040: }
2041: if (name == null) {
2042: name = getClass().getName();
2043: }
2044: return name;
2045: }
2046:
2047: private MethodResultCache getMethodResult() {
2048: return ModuleManager.getMethodResult();
2049: }
2050:
2051: }
|