001: package org.tigris.scarab.om;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2005 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by Collab.Net <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of Collab.Net.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of Collab.Net.
047: */
048:
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.ArrayList;
052: import java.sql.Connection;
053:
054: // Turbine classes
055: import org.apache.torque.TorqueException;
056: import org.apache.torque.om.Persistent;
057: import org.apache.torque.util.Criteria;
058: import org.apache.fulcrum.localization.Localization;
059:
060: import org.tigris.scarab.services.cache.ScarabCache;
061: import org.tigris.scarab.tools.localization.L10NKeySet;
062: import org.tigris.scarab.om.Module;
063: import org.tigris.scarab.om.ModuleManager;
064: import org.tigris.scarab.om.IssueTypePeer;
065: import org.tigris.scarab.util.ScarabException;
066: import org.tigris.scarab.workflow.WorkflowFactory;
067:
068: /**
069: * This class represents a RModuleAttribute relationship.
070: *
071: * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
072: * @version $Id: RModuleAttribute.java 9977 2005-12-09 00:40:59Z hair $
073: */
074: public class RModuleAttribute extends BaseRModuleAttribute implements
075: Persistent, Conditioned {
076: private static final String R_MODULE_ATTTRIBUTE = "RModuleAttribute";
077: private static final String GET_RMAS = "getRMAs";
078:
079: public void save(Connection con) throws TorqueException {
080: if (isModified()) {
081: if (isNew()) {
082: super .save(con);
083: } else {
084: RIssueTypeAttribute ria = null;
085: try {
086: ria = getIssueType().getRIssueTypeAttribute(
087: getAttribute());
088: if ((ria != null && ria.getLocked())) {
089: throw new TorqueException(getAttribute()
090: .getName()
091: + " is locked"); //EXCEPTION
092: } else {
093: super .save(con);
094: }
095: } catch (Exception e) {
096: throw new TorqueException("An error has occurred.",
097: e); //EXCEPTION
098: }
099: }
100: }
101: }
102:
103: /**
104: * Throws UnsupportedOperationException. Use
105: * <code>getModule()</code> instead.
106: *
107: * @return a <code>ScarabModule</code> value
108: */
109: public ScarabModule getScarabModule() {
110: throw new UnsupportedOperationException("Should use getModule"); //EXCEPTION
111: }
112:
113: /**
114: * Throws UnsupportedOperationException. Use
115: * <code>setModule(Module)</code> instead.
116: *
117: */
118: public void setScarabModule(ScarabModule module) {
119: throw new UnsupportedOperationException(
120: "Should use setModule(Module). Note module cannot be new."); //EXCEPTION
121: }
122:
123: /**
124: * Use this instead of setScarabModule. Note: module cannot be new.
125: */
126: public void setModule(Module me) throws TorqueException {
127: Integer id = me.getModuleId();
128: if (id == null) {
129: throw new TorqueException("Modules must be saved prior to "
130: + "being associated with other objects."); //EXCEPTION
131: }
132: setModuleId(id);
133: }
134:
135: /**
136: * Module getter. Use this method instead of getScarabModule().
137: *
138: * @return a <code>Module</code> value
139: */
140: public Module getModule() throws TorqueException {
141: Module module = null;
142: Integer id = getModuleId();
143: if (id != null) {
144: module = ModuleManager.getInstance(id);
145: }
146:
147: return module;
148: }
149:
150: /**
151: * Get the Display Value for the attribute. In the event that this
152: * is a new RModuleAttribute that has not been assigned a Display
153: * Value, this method will return the Attribute Name.
154: */
155: public String getDisplayValue() {
156: String dispVal = super .getDisplayValue();
157: if (dispVal == null) {
158: try {
159: dispVal = getAttribute().getName();
160: } catch (Exception e) {
161: getLog().error(e);
162: dispVal = "!Error-Check Logs!";
163: }
164: }
165: return dispVal;
166: }
167:
168: public void delete() throws TorqueException, ScarabException {
169: delete(false);
170: }
171:
172: protected void delete(final boolean overrideLock)
173: throws TorqueException, ScarabException {
174: final Module module = getModule();
175:
176: final IssueType issueType = IssueTypeManager.getInstance(
177: getIssueTypeId(), false);
178: if (issueType.getLocked() && !overrideLock) {
179: throw new ScarabException(
180: L10NKeySet.CannotDeleteAttributeFromLockedIssueType);
181: } else {
182: final Criteria c = new Criteria().add(
183: RModuleAttributePeer.MODULE_ID, getModuleId()).add(
184: RModuleAttributePeer.ISSUE_TYPE_ID,
185: getIssueTypeId())
186: .add(RModuleAttributePeer.ATTRIBUTE_ID,
187: getAttributeId());
188: RModuleAttributePeer.doDelete(c);
189: final Attribute attr = getAttribute();
190: String attributeType = null;
191: attributeType = (attr.isUserAttribute() ? Module.USER
192: : Module.NON_USER);
193: module.getRModuleAttributes(getIssueType(), false,
194: attributeType).remove(this );
195: WorkflowFactory.getInstance().deleteWorkflowsForAttribute(
196: attr, module, getIssueType());
197:
198: // delete module-user-attribute mappings
199: final Criteria crit = new Criteria().add(
200: RModuleUserAttributePeer.ATTRIBUTE_ID,
201: attr.getAttributeId()).add(
202: RModuleUserAttributePeer.MODULE_ID, getModuleId())
203: .add(RModuleUserAttributePeer.ISSUE_TYPE_ID,
204: getIssueTypeId());
205: RModuleUserAttributePeer.doDelete(crit);
206:
207: // delete module-option mappings
208: if (attr.isOptionAttribute()) {
209: final List optionList = module.getRModuleOptions(attr,
210: IssueTypePeer.retrieveByPK(getIssueTypeId()),
211: false);
212: if (optionList != null && !optionList.isEmpty()) {
213: final List optionIdList = new ArrayList(optionList
214: .size());
215: for (int i = 0; i < optionList.size(); i++) {
216: optionIdList.add(((RModuleOption) optionList
217: .get(i)).getOptionId());
218: }
219: final Criteria c2 = new Criteria().add(
220: RModuleOptionPeer.MODULE_ID, getModuleId())
221: .add(RModuleOptionPeer.ISSUE_TYPE_ID,
222: getIssueTypeId()).addIn(
223: RModuleOptionPeer.OPTION_ID,
224: optionIdList);
225: RModuleOptionPeer.doDelete(c2);
226: }
227: }
228: }
229:
230: RModuleAttributeManager.removeInstanceFromCache(this );
231: }
232:
233: private static List getRMAs(Integer moduleId, Integer issueTypeId)
234: throws TorqueException {
235: List result = null;
236: Object obj = ScarabCache.get(R_MODULE_ATTTRIBUTE, GET_RMAS,
237: moduleId, issueTypeId);
238: if (obj == null) {
239: Criteria crit = new Criteria().add(
240: RModuleAttributePeer.MODULE_ID, moduleId).add(
241: RModuleAttributePeer.ISSUE_TYPE_ID, issueTypeId);
242: crit
243: .addAscendingOrderByColumn(RModuleAttributePeer.PREFERRED_ORDER);
244: result = RModuleAttributePeer.doSelect(crit);
245: ScarabCache.put(result, R_MODULE_ATTTRIBUTE, GET_RMAS,
246: moduleId, issueTypeId);
247: } else {
248: result = (List) obj;
249: }
250: return result;
251: }
252:
253: /**
254: * if this RMA is the chosen attribute for email subjects then return
255: * true. if not explicitly chosen, check the other RMA's for this module
256: * and if none is chosen as the email attribute, choose the highest
257: * ordered text attribute.
258: *
259: * @return a <code>boolean</code> value
260: */
261: public boolean getIsDefaultText() throws TorqueException {
262: boolean isDefault = getDefaultTextFlag();
263: if (!isDefault && getAttribute().isTextAttribute()) {
264: // get related RMAs
265: List rmas = getRMAs(getModuleId(), getIssueTypeId());
266:
267: // check if another is chosen
268: boolean anotherIsDefault = false;
269: for (int i = 0; i < rmas.size(); i++) {
270: RModuleAttribute rma = (RModuleAttribute) rmas.get(i);
271: if (rma.getDefaultTextFlag()) {
272: anotherIsDefault = true;
273: break;
274: }
275: }
276:
277: if (!anotherIsDefault) {
278: // locate the default text attribute
279: for (int i = 0; i < rmas.size(); i++) {
280: RModuleAttribute rma = (RModuleAttribute) rmas
281: .get(i);
282: if (rma.getAttribute().isTextAttribute()) {
283: if (rma.getAttributeId().equals(
284: getAttributeId())) {
285: isDefault = true;
286: } else {
287: anotherIsDefault = true;
288: }
289:
290: break;
291: }
292: }
293: }
294: }
295: return isDefault;
296: }
297:
298: /**
299: * This method sets the defaultTextFlag property and also makes sure
300: * that no other related RMA is defined as the default. It should be
301: * used instead of setDefaultTextFlag in application code.
302: *
303: * @param b a <code>boolean</code> value
304: */
305: public void setIsDefaultText(boolean b) throws TorqueException {
306: if (b && !getDefaultTextFlag()) {
307: // get related RMAs
308: List rmas = getRMAs(getModuleId(), getIssueTypeId());
309:
310: // make sure no other rma is selected
311: for (int i = 0; i < rmas.size(); i++) {
312: RModuleAttribute rma = (RModuleAttribute) rmas.get(i);
313: if (rma.getDefaultTextFlag()) {
314: rma.setDefaultTextFlag(false);
315: rma.save();
316: break;
317: }
318: }
319: }
320: setDefaultTextFlag(b);
321: }
322:
323: public List getConditions() throws TorqueException {
324: if (collConditions == null) {
325: Criteria crit = new Criteria();
326: crit.add(ConditionPeer.ATTRIBUTE_ID, this .getAttributeId());
327: crit.add(ConditionPeer.MODULE_ID, this .getModuleId());
328: crit
329: .add(ConditionPeer.ISSUE_TYPE_ID, this
330: .getIssueTypeId());
331: crit.add(ConditionPeer.TRANSITION_ID, null);
332: collConditions = getConditions(crit);
333: }
334: return collConditions;
335: }
336:
337: /**
338: * Returns the array of attributeOptionIds that will force the requiment of this
339: * attribute if set. Used by templates to load the combo.
340: * @return
341: */
342: public Integer[] getConditionsArray() {
343: List conditions = new ArrayList();
344: Integer[] aIDs = null;
345: try {
346: conditions = this .getConditions();
347: aIDs = new Integer[conditions.size()];
348: int i = 0;
349: for (Iterator iter = conditions.iterator(); iter.hasNext(); i++) {
350: aIDs[i] = ((Condition) iter.next()).getOptionId();
351: }
352: } catch (TorqueException e) {
353: this .getLog().error("getConditionsArray: " + e);
354: }
355: return aIDs;
356: }
357:
358: /**
359: * Load the attribute options' IDs from the template combo.
360: * @param aOptionId
361: * @throws TorqueException
362: */
363: public void setConditionsArray(Integer aOptionId[])
364: throws TorqueException {
365: Criteria crit = new Criteria();
366: crit.add(ConditionPeer.ATTRIBUTE_ID, this .getAttributeId());
367: crit.add(ConditionPeer.MODULE_ID, this .getModuleId());
368: crit.add(ConditionPeer.ISSUE_TYPE_ID, this .getIssueTypeId());
369: crit.add(ConditionPeer.TRANSITION_ID, null);
370: ConditionPeer.doDelete(crit);
371: this .getConditions().clear();
372: ConditionManager.clear();
373: if (aOptionId != null) {
374: for (int i = 0; i < aOptionId.length; i++) {
375: if (aOptionId[i].intValue() != 0) {
376: Condition cond = new Condition();
377: cond.setAttribute(this .getAttribute());
378: cond.setOptionId(aOptionId[i]);
379: cond.setTransitionId(null);
380: cond.setIssueTypeId(this .getIssueTypeId());
381: cond.setModuleId(this .getModuleId());
382: this .addCondition(cond);
383: cond.save();
384: }
385: }
386: }
387: }
388:
389: /**
390: * Return true if the given attributeOptionId will make the current
391: * attribute required.
392: * @param optionID
393: * @return
394: * @throws TorqueException
395: */
396: public boolean isRequiredIf(Integer optionID)
397: throws TorqueException {
398: Condition condition = new Condition();
399: condition.setAttribute(this .getAttribute());
400: condition.setModuleId(this .getModuleId());
401: condition.setIssueTypeId(this .getIssueTypeId());
402: condition.setTransitionId(new Integer(0));
403: condition.setOptionId(optionID);
404: return this .getConditions().contains(condition);
405: }
406:
407: /**
408: * Returns true if this object is conditioned (has related conditions)
409: * @return
410: */
411: public boolean isConditioned() {
412: boolean bRdo = false;
413: try {
414: bRdo = this .getConditions().size() > 0;
415: } catch (TorqueException te) {
416: // Nothing to do
417: }
418: return bRdo;
419: }
420: }
|