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: // JDK classes
050: import com.workingdogs.village.DataSetException;
051: import java.util.Collections;
052: import java.util.HashMap;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.util.ArrayList;
056: import java.util.Date;
057: import com.workingdogs.village.Record;
058:
059: // Turbine classes
060: import org.apache.torque.NoRowsException;
061: import org.apache.torque.TooManyRowsException;
062: import org.apache.torque.TorqueException;
063: import org.apache.torque.om.Persistent;
064: import org.apache.torque.om.ObjectKey;
065: import org.apache.torque.om.SimpleKey;
066: import org.apache.torque.util.Criteria;
067:
068: // Scarab classes
069: import org.tigris.scarab.om.ScarabUserManager;
070:
071: import org.tigris.scarab.services.cache.ScarabCache;
072: import org.tigris.scarab.tools.localization.L10NKey;
073: import org.tigris.scarab.util.ScarabException;
074:
075: /**
076: * This class represents the SCARAB_R_OPTION_OPTION table.
077: * Please note that this class caches several pieces of data depending
078: * on the methods called. If you would like to clear these caches,
079: * it is a good idea to call the doRemoveCaches() method after making
080: * any modifications to the ROptionOption, ParentChildAttributeOption,
081: * and AttributeOption objects.
082: *
083: * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
084: * @version $Id: Attribute.java 10042 2006-04-11 11:28:39Z jorgeuriarte $
085: */
086: public class Attribute extends BaseAttribute implements Persistent,
087: Conditioned {
088: private static final String CLASS_NAME = "Attribute";
089:
090: /** Class name used as part of a cache key when the method is static */
091: private static final String ATTRIBUTE = CLASS_NAME;
092: /** Method name used as part of a cache key */
093: private static final String GET_INSTANCE = "getInstance";
094: /** Method name used as part of a cache key */
095: private static final String GET_ALL_ATTRIBUTE_TYPES = "getAllAttributeTypes";
096: /** Method name used as part of a cache key */
097: private static final String GET_COMPATIBLE_ATTRIBUTE_TYPES = "getCompatibleAttributeTypes";
098: /** Method name used as part of a cache key */
099: private static final String GET_ATTRIBUTE_TYPE = "getAttributeType";
100: /** Method name used as part of a cache key */
101: private static final String GET_ALL_ATTRIBUTES = "getAllAttributes";
102: /** Method name used as part of a cache key */
103: private static final String GET_ALL_ATTRIBUTE_OPTIONS = "getAllAttributeOptions";
104: /** Method name used as part of a cache key */
105: private static final String GET_ORDERED_ROPTIONOPTION_LIST = "getOrderedROptionOptionList";
106:
107: private static final String SELECT_ONE = "select-one";
108: private static final String USER_ATTRIBUTE = "user";
109: private static final String[] TEXT_TYPES = { "string", "email",
110: "long-string", "date" };
111: private static final String INTEGER_ATTRIBUTE = "integer";
112:
113: private List orderedROptionOptionList = null;
114: private List orderedAttributeOptionList = null;
115: private List parentChildAttributeOptions = null;
116:
117: private HashMap optionsMap;
118: private List attributeOptionsWithDeleted;
119: private List attributeOptionsWithoutDeleted;
120:
121: private static final L10NKey DEFAULT = new L10NKey("Default");
122:
123: /**
124: * Must call getInstance()
125: */
126: protected Attribute() {
127: }
128:
129: static String getCacheKey(ObjectKey key) {
130: String keyString = key.getValue().toString();
131: return new StringBuffer(CLASS_NAME.length()
132: + keyString.length()).append(CLASS_NAME).append(
133: keyString).toString();
134: }
135:
136: /**
137: * Return an instance based on the passed in attribute id as an int
138: * It will return a cached instance if possible.
139: */
140: public static Attribute getInstance(int id) throws TorqueException {
141: return AttributeManager.getInstance(new Integer(id));
142: }
143:
144: /**
145: * Return an instance based on the passed in
146: * attribute name as a String. It will return
147: * the first match if the number of Attributes found > 0
148: * Note: The business logic dicates that there should
149: * never be duplicate Attributes. Therefore, the checkForDuplicate
150: * method will return true if the number of Attributes found
151: * is > 0
152: */
153: public static Attribute getInstance(final String attributeName)
154: throws TorqueException {
155: Attribute result = null;
156: // TODO Should attributes even be cached by name? What if the name is changed?
157: Object obj = ScarabCache.get(ATTRIBUTE, GET_INSTANCE,
158: attributeName.toLowerCase());
159: if (obj == null) {
160: final Criteria crit = new Criteria();
161: crit.add(AttributePeer.ATTRIBUTE_NAME, attributeName);
162: crit.setIgnoreCase(true);
163: final List attributes = AttributePeer.doSelect(crit);
164: if (attributes.size() > 0) {
165: result = (Attribute) attributes.get(0);
166: ScarabCache.put(result, ATTRIBUTE, GET_INSTANCE,
167: attributeName.toLowerCase());
168: }
169: } else {
170: result = (Attribute) obj;
171: }
172: return result;
173: }
174:
175: /**
176: * Checks to see if there is another attribute with the same name
177: * already in the database. Returns true if there is another
178: * Attribute of the same name.
179: */
180: public static boolean checkForDuplicate(String attributeName)
181: throws TorqueException {
182: return (getInstance(attributeName) != null);
183: }
184:
185: public static boolean checkForDuplicate(String attributeName,
186: Attribute attribute) throws TorqueException {
187: return (checkForDuplicate(attributeName) && !attributeName
188: .equals(attribute.getName()));
189: }
190:
191: /**
192: * Helper method that takes a Integer
193: */
194: public String getCreatedUserName() throws TorqueException {
195: final Integer userId = getCreatedBy();
196: String userName = null;
197: if (userId == null || userId.intValue() == 0) {
198: userName = DEFAULT.getMessage();
199: } else {
200: final ScarabUser su = ScarabUserManager
201: .getInstance(SimpleKey.keyFor(userId));
202: userName = su.getName();
203: }
204: return userName;
205: }
206:
207: /**
208: * Clears the internal caches for this object
209: */
210: void doRemoveCaches() {
211: setOrderedROptionOptionList(null);
212: setOrderedAttributeOptionList(null);
213: setParentChildAttributeOptions(null);
214: }
215:
216: /**
217: * Little method to return a List of all Attribute Type's.
218: * It is here for convenience with regards to needing this
219: * functionality from within a Template.
220: */
221: public static List getAllAttributeTypes() throws TorqueException {
222: List result = null;
223: Object obj = ScarabCache
224: .get(ATTRIBUTE, GET_ALL_ATTRIBUTE_TYPES);
225: if (obj == null) {
226: result = AttributeTypePeer.doSelect(new Criteria());
227: ScarabCache.put(result, ATTRIBUTE, GET_ALL_ATTRIBUTE_TYPES);
228: } else {
229: result = (List) obj;
230: }
231: return result;
232: }
233:
234: /**
235: * Method to return compatible Attribute Type's.
236: * if the attribute has not been used at all, all types are
237: * compatible. if issues have been entered which use the
238: * attribute only text types are compatible with each other
239: * It is here for convenience with regards to needing this
240: * functionality from within a Template.
241: */
242: public List getCompatibleAttributeTypes() throws TorqueException,
243: DataSetException {
244: List result = null;
245: final Object obj = ScarabCache.get(this ,
246: GET_COMPATIBLE_ATTRIBUTE_TYPES);
247: if (obj == null) {
248: boolean inUse = !isNew();
249: if (inUse) {
250: // check to see if attribute really has been used
251: final Criteria crit = new Criteria();
252: crit.add(AttributeValuePeer.ATTRIBUTE_ID,
253: getAttributeId());
254: inUse = AttributeValuePeer.count(crit) > 0;
255: }
256: if (inUse) {
257: if (isTextAttribute()) {
258: final Criteria crit = new Criteria();
259: crit.addIn(AttributeTypePeer.ATTRIBUTE_TYPE_ID,
260: AttributeTypePeer.TEXT_PKS);
261: result = AttributeTypePeer.doSelect(crit);
262: } else {
263: result = Collections.EMPTY_LIST;
264: }
265: } else {
266: result = getAllAttributeTypes();
267: }
268: ScarabCache.put(result, this ,
269: GET_COMPATIBLE_ATTRIBUTE_TYPES);
270: } else {
271: result = (List) obj;
272: }
273: return result;
274: }
275:
276: /**
277: * Override the base class to provide caching of AttributeType objects
278: * and save a hit to the database.
279: */
280: public AttributeType getAttributeType() throws TorqueException {
281: AttributeType result = null;
282: Object obj = ScarabCache.get(this , GET_ATTRIBUTE_TYPE);
283: if (obj == null) {
284: result = super .getAttributeType();
285: ScarabCache.put(result, this , GET_ATTRIBUTE_TYPE);
286: } else {
287: result = (AttributeType) obj;
288: }
289: return result;
290: }
291:
292: /**
293: * get a list of all of the Attributes in the database
294: */
295: public static List getAllAttributes() throws TorqueException {
296: List result = null;
297: Object obj = ScarabCache.get(ATTRIBUTE, GET_ALL_ATTRIBUTES);
298: if (obj == null) {
299: result = AttributePeer.doSelect(new Criteria());
300: ScarabCache.put(result, ATTRIBUTE, GET_ALL_ATTRIBUTES);
301: } else {
302: result = (List) obj;
303: }
304: return result;
305: }
306:
307: public boolean isOptionAttribute() throws TorqueException {
308: if (getTypeId() != null) {
309: return getAttributeType().getAttributeClass().getName()
310: .equals(SELECT_ONE);
311: }
312: return false;
313: }
314:
315: public boolean isUserAttribute() throws TorqueException {
316: if (getTypeId() != null) {
317: return getAttributeType().getAttributeClass().getName()
318: .equals(USER_ATTRIBUTE);
319: }
320: return false;
321: }
322:
323: public boolean isTextAttribute() throws TorqueException {
324: boolean isText = false;
325: if (getTypeId() != null) {
326: for (int i = 0; i < TEXT_TYPES.length && !isText; i++) {
327: isText = TEXT_TYPES[i].equals(getAttributeType()
328: .getName());
329: }
330: }
331: return isText;
332: }
333:
334: public boolean isIntegerAttribute() throws TorqueException {
335: return getTypeId() != null
336: && INTEGER_ATTRIBUTE.equals(getAttributeType()
337: .getName());
338: }
339:
340: public boolean isDateAttribute() throws TorqueException {
341: boolean isDate = false;
342: if (getTypeId() != null) {
343: isDate = "date".equals(getAttributeType().getName());
344: }
345: return isDate;
346: }
347:
348: /**
349: * This method is special. Don't use it.
350: * It is used to generate the mappings for r_option_option
351: * table mappings because AO's have to have a mapping in
352: * there in order to be looked up using the getOrderedROptionOptionList()
353: * method. This method has already been run and the output was
354: * used to copy/paste into the scarab-default-data.sql file.
355: * It is being kept here in case it is needed again someday.
356: public static void createROptionOptionMapping()
357: throws TorqueException
358: {
359: List attributes = Attribute.getAllAttributes();
360: for (int i=0; i<attributes.size();i++)
361: {
362: Attribute attr = (Attribute) attributes.get(i);
363: if (attr.getName().equals("Operating System") ||
364: attr.getName().equals("Null Attribute"))
365: {
366: continue;
367: }
368: System.out.println ("Attribute: " + attr.getName());
369: List attributeOptions = attr.getAttributeOptions();
370: Iterator itr = attributeOptions.iterator();
371: int counter = 1;
372: while (itr.hasNext())
373: {
374: AttributeOption ao = (AttributeOption) itr.next();
375: System.out.println ("\tAttribute Option: " + ao.getName());
376: ROptionOption roo = ROptionOption.getInstance();
377: roo.setOption1Id(new NumberKey(0));
378: roo.setOption2Id(ao.getOptionId());
379: roo.setRelationshipId(new NumberKey(1));
380: roo.setPreferredOrder(counter++);
381: roo.setDeleted(false);
382: roo.save();
383: }
384: }
385: }
386: */
387:
388: /****************************************************************************/
389: /* Attribute Option Methods */
390: /****************************************************************************/
391:
392: /**
393: * Gets one of the options belonging to this attribute. if the
394: * PrimaryKey does not belong to an option in this attribute
395: * null is returned.
396: *
397: * @param pk a <code>Integer</code> value
398: * @return an <code>AttributeOption</code> value
399: */
400: public AttributeOption getAttributeOption(Integer pk)
401: throws TorqueException {
402: if (optionsMap == null) {
403: buildOptionsMap();
404: }
405: return (AttributeOption) optionsMap.get(pk);
406: }
407:
408: /**
409: * Get an option by String id.
410: * @throws TorqueException if optionId is empty
411: */
412: public AttributeOption getAttributeOption(String optionID)
413: throws TorqueException {
414: if (optionID == null || optionID.length() == 0) {
415: throw new TorqueException("optionId is empty"); //EXCEPTION
416: }
417: return getAttributeOption(new Integer(optionID));
418: }
419:
420: /**
421: * Used internally to get a list of Attribute Options
422: */
423: private List getAllAttributeOptions() throws TorqueException {
424: List result = null;
425: Object obj = ScarabCache.get(this , GET_ALL_ATTRIBUTE_OPTIONS);
426: if (obj == null) {
427: Criteria crit = new Criteria();
428: crit.addJoin(AttributeOptionPeer.OPTION_ID,
429: ROptionOptionPeer.OPTION2_ID);
430: crit.add(AttributeOptionPeer.ATTRIBUTE_ID, this
431: .getAttributeId());
432: crit
433: .addAscendingOrderByColumn(ROptionOptionPeer.PREFERRED_ORDER);
434: result = AttributeOptionPeer.doSelect(crit);
435: ScarabCache.put(result, this , GET_ALL_ATTRIBUTE_OPTIONS);
436: } else {
437: result = (List) obj;
438: }
439: return result;
440: }
441:
442: /**
443: * package protected method to set the value of the cached
444: * list. Generally, this is used to set it to null.
445: */
446: void setParentChildAttributeOptions(List value) {
447: parentChildAttributeOptions = value;
448: }
449:
450: /**
451: * This returns a list of ParentChildAttributeOption objects
452: * which have been populated with combined join data from
453: * ROptionOption and the AttributeOption table.
454: *
455: * @return a List of ParentChildAttributeOption objects
456: */
457: public List getParentChildAttributeOptions() throws TorqueException {
458: if (parentChildAttributeOptions == null) {
459: List rooList = getOrderedROptionOptionList();
460: List aoList = getOrderedAttributeOptionList();
461: parentChildAttributeOptions = new ArrayList(rooList.size());
462: for (int i = 0; i < rooList.size(); i++) {
463: ROptionOption roo = (ROptionOption) rooList.get(i);
464: AttributeOption ao = (AttributeOption) aoList.get(i);
465:
466: ParentChildAttributeOption pcao = ParentChildAttributeOption
467: .getInstance(roo.getOption1Id(), roo
468: .getOption2Id());
469: pcao.setParentId(roo.getOption1Id());
470: pcao.setOptionId(roo.getOption2Id());
471: pcao.setPreferredOrder(roo.getPreferredOrder());
472: pcao.setWeight(roo.getWeight());
473: pcao.setName(ao.getName());
474: pcao.setDeleted(ao.getDeleted());
475: pcao.setAttributeId(this .getAttributeId());
476: parentChildAttributeOptions.add(pcao);
477: }
478: }
479: return parentChildAttributeOptions;
480: }
481:
482: /**
483: * package protected method to set the value of the cached
484: * list. Generally, this is used to set it to null.
485: */
486: void setOrderedROptionOptionList(List value) {
487: orderedROptionOptionList = value;
488: }
489:
490: /**
491: * Creates an ordered List of ROptionOption which are
492: * children within this Attribute. The list is ordered according
493: * to the preferred order.
494: *
495: * @return a List of ROptionOption's
496: */
497: public List getOrderedROptionOptionList() throws TorqueException {
498: List result = null;
499: Object obj = ScarabCache.get(this ,
500: GET_ORDERED_ROPTIONOPTION_LIST);
501: if (obj == null) {
502: if (orderedROptionOptionList == null) {
503: Criteria crit = new Criteria();
504: crit.addJoin(AttributeOptionPeer.OPTION_ID,
505: ROptionOptionPeer.OPTION2_ID);
506: crit.add(AttributeOptionPeer.ATTRIBUTE_ID,
507: getAttributeId());
508: crit
509: .addAscendingOrderByColumn(ROptionOptionPeer.PREFERRED_ORDER);
510: orderedROptionOptionList = ROptionOptionPeer
511: .doSelect(crit);
512: }
513: result = orderedROptionOptionList;
514: ScarabCache.put(result, this ,
515: GET_ORDERED_ROPTIONOPTION_LIST);
516: } else {
517: result = (List) obj;
518: }
519: return result;
520: }
521:
522: /**
523: * package protected method to set the value of the cached
524: * list. Generally, this is used to set it to null.
525: */
526: void setOrderedAttributeOptionList(List value) {
527: orderedAttributeOptionList = value;
528: }
529:
530: /**
531: * Creates an ordered List of AttributeOptions which are
532: * children within this Attribute. The list is ordered according
533: * to the preferred order.
534: *
535: * @return a List of AttributeOption's
536: */
537: public List getOrderedAttributeOptionList() throws TorqueException {
538: if (orderedAttributeOptionList == null) {
539: Criteria crit = new Criteria();
540: crit.addJoin(AttributeOptionPeer.OPTION_ID,
541: ROptionOptionPeer.OPTION2_ID);
542: crit.add(AttributeOptionPeer.ATTRIBUTE_ID, this
543: .getAttributeId());
544: crit
545: .addAscendingOrderByColumn(ROptionOptionPeer.PREFERRED_ORDER);
546: orderedAttributeOptionList = AttributeOptionPeer
547: .doSelect(crit);
548: }
549: return orderedAttributeOptionList;
550: }
551:
552: /**
553: * Get a list of all attribute options or just the ones
554: * that have not been marked as deleted.
555: */
556: public List getAttributeOptions(boolean includeDeleted)
557: throws TorqueException {
558: List allOptions = getAllAttributeOptions();
559: List nonDeleted = new ArrayList(allOptions.size());
560: if (includeDeleted) {
561: return allOptions;
562: } else {
563: for (int i = 0; i < allOptions.size(); i++) {
564: AttributeOption option = (AttributeOption) allOptions
565: .get(i);
566: if (!option.getDeleted()) {
567: nonDeleted.add(option);
568: }
569: }
570: return nonDeleted;
571: }
572: }
573:
574: /**
575: * Build a list of options.
576: */
577: public synchronized void buildOptionsMap() throws TorqueException {
578: if (getAttributeType().getAttributeClass().getName().equals(
579: SELECT_ONE)) {
580: // synchronized method due to getattributeOptionsWithDeleted, this needs
581: // further investigation !FIXME!
582: attributeOptionsWithDeleted = this .getAllAttributeOptions();
583: optionsMap = new HashMap(
584: (int) (1.25 * attributeOptionsWithDeleted.size() + 1));
585:
586: attributeOptionsWithoutDeleted = new ArrayList(
587: attributeOptionsWithDeleted.size());
588: for (int i = 0; i < attributeOptionsWithDeleted.size(); i++) {
589: AttributeOption option = (AttributeOption) attributeOptionsWithDeleted
590: .get(i);
591: optionsMap.put(option.getOptionId(), option);
592: if (!option.getDeleted()) {
593: attributeOptionsWithoutDeleted
594: .add(attributeOptionsWithDeleted.get(i));
595: }
596: }
597: }
598: }
599:
600: /**
601: * Override autogenerated base class method
602: * There should be no actitivites being returned
603: */
604: public List getActivitys() throws TorqueException {
605: return null;
606: }
607:
608: /**
609: * Copy the Attribute and its options
610: * Make sure the new options have a row in the option join table
611: */
612: public Attribute copyAttribute(ScarabUser user)
613: throws TorqueException {
614: Attribute newAttribute = new Attribute();
615: newAttribute.setName(getName() + " (copy)");
616: newAttribute.setDescription(getDescription());
617: newAttribute.setTypeId(getTypeId());
618: newAttribute.setPermission(getPermission());
619: newAttribute.setRequiredOptionId(getRequiredOptionId());
620: newAttribute.setConditionsArray(getConditionsArray());
621: newAttribute.setAction(getAction());
622: newAttribute.setCreatedBy(user.getUserId());
623: newAttribute.setCreatedDate(new Date());
624: newAttribute.setDeleted(getDeleted());
625: newAttribute.save();
626:
627: List attributeOptions = getAttributeOptions();
628: for (int i = 0; i < attributeOptions.size(); i++) {
629: AttributeOption option = (AttributeOption) attributeOptions
630: .get(i);
631: AttributeOption newOption = new AttributeOption();
632: newOption.setOptionId(option.getOptionId());
633: newOption.setAttributeId(newAttribute.getAttributeId());
634: newOption.setName(option.getName());
635: newOption.setDeleted(option.getDeleted());
636: newOption.save();
637:
638: // Copy options's record in R_OPTION_OPTION table
639: List roos = option.getROptionOptionsRelatedByOption2Id();
640: for (int j = 0; j < roos.size(); j++) {
641: ROptionOption roo = (ROptionOption) roos.get(j);
642: ROptionOption newRoo = new ROptionOption();
643: newRoo.setOption2Id(newOption.getOptionId());
644: newRoo.setOption1Id(roo.getOption1Id());
645: newRoo.setRelationshipId(roo.getRelationshipId());
646: newRoo.setWeight(roo.getWeight());
647: newRoo.setPreferredOrder(roo.getPreferredOrder());
648: newRoo.save();
649: }
650: }
651: return newAttribute;
652: }
653:
654: /**
655: * @return Whether this attribute is mapped to any modules.
656: */
657: public boolean hasModuleMappings() throws TorqueException,
658: DataSetException {
659: return hasMapping((Module) null, (IssueType) null);
660: }
661:
662: /**
663: * @param module <code>null</code> to ignore this criterion.
664: * @param issueType <code>null</code> to ignore this criterion.
665: * @return Whether this attribute is already mapped to the
666: * specified {@link Module} and {@link IssueType}.
667: */
668: public boolean hasMapping(final Module module,
669: final IssueType issueType) throws TorqueException,
670: DataSetException {
671: final Criteria crit = new Criteria();
672: crit.add(RModuleAttributePeer.ATTRIBUTE_ID, getAttributeId());
673: if (module != null) {
674: crit.add(RModuleAttributePeer.MODULE_ID, module
675: .getModuleId());
676: }
677: if (issueType != null) {
678: crit.add(RModuleAttributePeer.ISSUE_TYPE_ID, issueType
679: .getIssueTypeId());
680: }
681: crit.addSelectColumn("count("
682: + RModuleAttributePeer.ATTRIBUTE_ID + ")");
683: return ((Record) IssuePeer.doSelectVillageRecords(crit).get(0))
684: .getValue(1).asInt() > 0;
685: }
686:
687: /**
688: * Refers to global issue types.
689: *
690: * @return Whether this attribute is mapped to any issue types.
691: * @see #hasGlobalMapping(IssueType)
692: */
693: public boolean hasGlobalIssueTypeMappings() throws TorqueException,
694: DataSetException {
695: return hasGlobalMapping((IssueType) null);
696: }
697:
698: /**
699: * Refers to global issue types.
700: *
701: * @param issueType A specific {@link IssueType} to find
702: * associated attributes for, or <code>null</code> to ignore this
703: * criterion.
704: * @return Whether there are any mappings for this attribute.
705: */
706: public boolean hasGlobalMapping(IssueType issueType)
707: throws TorqueException, DataSetException {
708: final Criteria crit = new Criteria();
709: crit
710: .add(RIssueTypeAttributePeer.ATTRIBUTE_ID,
711: getAttributeId());
712: if (issueType != null) {
713: crit.add(RIssueTypeAttributePeer.ISSUE_TYPE_ID, issueType
714: .getIssueTypeId());
715: }
716: crit.addSelectColumn("count("
717: + RIssueTypeAttributePeer.ATTRIBUTE_ID + ')');
718: return ((Record) IssuePeer.doSelectVillageRecords(crit).get(0))
719: .getValue(1).asInt() > 0;
720: }
721:
722: /**
723: * Delete mappings with all modules and issue types.
724: */
725: public void deleteModuleMappings() throws TorqueException,
726: ScarabException {
727: Criteria crit = new Criteria();
728: crit.add(RAttributeAttributeGroupPeer.ATTRIBUTE_ID,
729: getAttributeId());
730: crit.addJoin(RAttributeAttributeGroupPeer.GROUP_ID,
731: AttributeGroupPeer.ATTRIBUTE_GROUP_ID);
732: crit.add(AttributeGroupPeer.MODULE_ID, (Object) null,
733: Criteria.NOT_EQUAL);
734: final List raags = RAttributeAttributeGroupPeer.doSelect(crit);
735: for (Iterator i = raags.iterator(); i.hasNext();) {
736: ((RAttributeAttributeGroup) i.next()).delete();
737: }
738:
739: crit = new Criteria();
740: crit.add(RModuleAttributePeer.ATTRIBUTE_ID, getAttributeId());
741: final List rmas = RModuleAttributePeer.doSelect(crit);
742: for (int i = 0; i < rmas.size(); i++) {
743: final RModuleAttribute rma = (RModuleAttribute) rmas.get(i);
744: rma.delete(true);
745: }
746: ScarabCache.clear();
747: }
748:
749: /**
750: * Delete mappings with global issue types.
751: */
752: public void deleteIssueTypeMappings() throws TorqueException {
753: Criteria crit = new Criteria();
754: crit.add(RAttributeAttributeGroupPeer.ATTRIBUTE_ID,
755: getAttributeId());
756: crit.addJoin(RAttributeAttributeGroupPeer.GROUP_ID,
757: AttributeGroupPeer.ATTRIBUTE_GROUP_ID);
758: crit.add(AttributeGroupPeer.MODULE_ID, null);
759: List raags = RAttributeAttributeGroupPeer.doSelect(crit);
760: for (Iterator i = raags.iterator(); i.hasNext();) {
761: ((RAttributeAttributeGroup) i.next()).delete();
762: }
763:
764: crit = new Criteria();
765: crit
766: .add(RIssueTypeAttributePeer.ATTRIBUTE_ID,
767: getAttributeId());
768: List rias = RIssueTypeAttributePeer.doSelect(crit);
769: for (Iterator i = rias.iterator(); i.hasNext();) {
770: ((RIssueTypeAttribute) i.next()).delete();
771: }
772:
773: ScarabCache.clear();
774: }
775:
776: /**
777: * Refers to Global Issue Types
778: * @return A list of global Issue Types, this attribute is associated with.
779: */
780: private List getAssociatedIssueTypes() throws TorqueException {
781: Criteria crit = new Criteria();
782: crit
783: .add(RIssueTypeAttributePeer.ATTRIBUTE_ID,
784: getAttributeId());
785: crit.addJoin(RIssueTypeAttributePeer.ISSUE_TYPE_ID,
786: IssueTypePeer.ISSUE_TYPE_ID);
787: List issueTypeList = IssueTypePeer.doSelect(crit);
788: return issueTypeList;
789: }
790:
791: /**
792: * Checks if this attribute is associated with atleast one of the
793: * global issue types that is system defined.
794: *
795: * @return True if the attribute is associated with a System defined
796: * global Issue Type.False otherwise.
797: */
798:
799: public boolean isSystemDefined() throws TorqueException {
800: boolean systemDefined = false;
801: List issueTypeList = getAssociatedIssueTypes();
802: for (Iterator i = issueTypeList.iterator(); i.hasNext();) {
803: if (((IssueType) i.next()).isSystemDefined()) {
804: systemDefined = true;
805: break;
806: }
807: }
808: return systemDefined;
809: }
810:
811: /**
812: * Gets the attributeOption that will force this attribute to be required, in case of being set.
813: * @return
814: */
815: public AttributeOption getRequiredOption() {
816: AttributeOption option = null;
817: try {
818: option = AttributeOptionPeer.retrieveByPK(this
819: .getRequiredOptionId());
820: } catch (NoRowsException e) {
821: // Nothing to do. Ignore.
822: } catch (TooManyRowsException e) {
823: // Nothing to do. Ignore.
824: } catch (TorqueException e) {
825: e.printStackTrace();
826: }
827: return option;
828: }
829:
830: /**
831: * Returns the array of attributeOptionIds that will force the requiment of this
832: * attribute if set. Used by templates to load the combo.
833: * @return
834: */
835: public Integer[] getConditionsArray() {
836: List conditions = new ArrayList();
837: Integer[] aIDs = null;
838: try {
839: conditions = this .getConditions();
840: aIDs = new Integer[conditions.size()];
841: int i = 0;
842: for (Iterator iter = conditions.iterator(); iter.hasNext(); i++) {
843: Condition cond = (Condition) iter.next();
844: aIDs[i] = cond.getOptionId();
845: }
846: } catch (TorqueException e) {
847: this .getLog().error("getConditionsArray: " + e);
848: }
849: return aIDs;
850: }
851:
852: public List getConditions() throws TorqueException {
853: if (collConditions == null) {
854: Criteria crit = new Criteria();
855: crit.add(ConditionPeer.ATTRIBUTE_ID, this .getAttributeId());
856: crit.add(ConditionPeer.MODULE_ID, null);
857: crit.add(ConditionPeer.TRANSITION_ID, null);
858: crit.add(ConditionPeer.ISSUE_TYPE_ID, null);
859: collConditions = getConditions(crit);
860: }
861: return collConditions;
862: }
863:
864: /**
865: * Load the attribute options' IDs from the template combo.
866: * @param aOptionId
867: * @throws TorqueException
868: */
869: public void setConditionsArray(Integer aOptionId[])
870: throws TorqueException {
871: Criteria crit = new Criteria();
872: crit.add(ConditionPeer.ATTRIBUTE_ID, this .getAttributeId());
873: crit.add(ConditionPeer.MODULE_ID, null);
874: crit.add(ConditionPeer.ISSUE_TYPE_ID, null);
875: crit.add(ConditionPeer.TRANSITION_ID, null);
876: ConditionPeer.doDelete(crit);
877: this .save();
878: this .getConditions().clear();
879: ConditionManager.clear();
880: if (aOptionId != null)
881: for (int i = 0; i < aOptionId.length; i++) {
882: if (aOptionId[i].intValue() != 0) {
883: Condition cond = new Condition();
884: cond.setAttributeId(this .getAttributeId());
885: cond.setOptionId(aOptionId[i]);
886: cond.setModuleId(null);
887: cond.setIssueTypeId(null);
888: cond.setTransitionId(null);
889: this .addCondition(cond);
890: cond.save();
891: }
892: }
893: }
894:
895: /**
896: * Return true if the given attributeOptionId will make the current
897: * attribute required.
898: * @param optionID
899: * @return
900: * @throws TorqueException
901: */
902: public boolean isRequiredIf(Integer optionID)
903: throws TorqueException {
904: Condition cond = new Condition();
905: cond.setAttributeId(this .getAttributeId());
906: cond.setOptionId(optionID);
907: cond.setModuleId(null);
908: cond.setIssueTypeId(null);
909: cond.setTransitionId(null);
910: return this .getConditions().contains(cond);
911: }
912:
913: public boolean isConditioned() {
914: boolean bRdo = false;
915: try {
916: bRdo = this .getConditions().size() > 0;
917: } catch (TorqueException te) {
918: // Nothing to do
919: }
920: return bRdo;
921: }
922:
923: /**
924: * Returns the transitions defined for this Attribute
925: */
926: public List getTransitions() {
927: // Reuses existing cache-enabled method
928: return TransitionManager.getAllTransitions(this);
929: }
930:
931: }
|