001: package org.andromda.utils.beans.comparators;
002:
003: import java.io.InputStream;
004: import java.io.Serializable;
005:
006: import java.lang.reflect.InvocationTargetException;
007:
008: import java.net.URL;
009:
010: import java.util.Comparator;
011: import java.util.Properties;
012:
013: import org.andromda.core.common.ClassUtils;
014: import org.andromda.core.common.ExceptionUtils;
015: import org.andromda.core.common.Introspector;
016: import org.andromda.utils.beans.SortCriteria;
017: import org.apache.commons.lang.StringUtils;
018:
019: /**
020: * Used by BeanSorter to provide sorting capabilities for
021: * beans. Currently supports sorting by all simple properties
022: *
023: * @see BeanSorter
024: *
025: * @author Chad Brandon
026: */
027: public class BeanComparator implements Comparator, Serializable {
028: /**
029: * Stores the comparator mappings.
030: */
031: private static final Properties comparators = new Properties();
032:
033: static {
034: try {
035: final String comparatorsFile = "Comparators.properties";
036: final URL comparatorsUri = BeanComparator.class
037: .getResource(comparatorsFile);
038: if (comparatorsUri == null) {
039: throw new BeanComparatorException(
040: "The comparators resource '" + comparatorsFile
041: + " could not be loaded");
042: }
043: InputStream stream = comparatorsUri.openStream();
044: comparators.load(stream);
045: stream.close();
046: stream = null;
047: } catch (final Throwable throwable) {
048: throw new RuntimeException(throwable);
049: }
050: }
051:
052: private Comparator comparator = null;
053: private SortCriteria sortCriteria;
054:
055: public BeanComparator(SortCriteria sortCriteria) {
056: ExceptionUtils.checkNull("sortCriteria", sortCriteria);
057: this .sortCriteria = sortCriteria;
058: }
059:
060: // - The following variables are saved since we only need to check some things once
061: // within the method and checking each time slows performance.
062: private boolean areSameType = false;
063:
064: /**
065: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
066: */
067: public int compare(final Object objectA, Object objectB) {
068: final String methodName = "BeanComparator.compare";
069: ExceptionUtils.checkNull("objectA", objectA);
070: ExceptionUtils.checkNull("objectB", objectB);
071:
072: // we'll assume that if the first set of objects are equal types
073: // then all must be (this of course could turn out to be false, but may hinder
074: // performance to check each object, each time).
075: if (!areSameType) {
076: if (objectA.getClass() != objectB.getClass()) {
077: String errMsg = methodName + " - objectA '" + objectA
078: + "' and objectB '" + objectB
079: + " must be of the same type";
080: throw new ClassCastException(errMsg);
081: }
082: areSameType = true;
083: }
084: try {
085: Object aValue;
086: Object bValue;
087: if (this .sortCriteria.getOrdering().equals(
088: SortCriteria.Ordering.DESCENDING)) {
089: // - since its decending switch the objects we are getting the values from,
090: // so the order will be reversed
091: aValue = getProperty(objectB, sortCriteria.getSortBy());
092: bValue = getProperty(objectA, sortCriteria.getSortBy());
093: } else {
094: // - otherwise we assume its ascending
095: aValue = getProperty(objectA, sortCriteria.getSortBy());
096: bValue = getProperty(objectB, sortCriteria.getSortBy());
097: }
098:
099: //we default result to zero (zero result would mean aValue and bValue equal each other)
100: int result = 0;
101:
102: //first sort for null values, null values will always come last
103: if (aValue != null && bValue == null) {
104: result = -1;
105: } else if (aValue == null && bValue != null) {
106: result = 1;
107: } else if (aValue != null && bValue != null) {
108: result = this .getComparator(aValue.getClass()).compare(
109: aValue, bValue);
110: }
111: return result;
112: } catch (final Throwable throwable) {
113: throwable.printStackTrace();
114: throw new ComparatorException(throwable);
115: }
116: }
117:
118: /**
119: * Gets the property specified by propertyName from the bean.
120: * Checks each nested parent to see if its null and if it is
121: * returns null.
122: * @param persistentObject
123: * @param propertyName
124: * @return
125: */
126: private Object getProperty(final Object bean, String propertyName)
127: throws IllegalAccessException, InvocationTargetException,
128: NoSuchMethodException {
129: Object value = null;
130: if (bean != null && StringUtils.isNotEmpty(propertyName)) {
131: int index = getNestedIndex(propertyName);
132: if (index != -1) {
133: String simpleProp = propertyName.substring(0, index);
134: value = Introspector.instance().getProperty(bean,
135: simpleProp);
136: if (value != null) {
137: if (getNestedIndex(propertyName) != -1) {
138: propertyName = propertyName.substring(
139: index + 1, propertyName.length());
140: value = getProperty(value, propertyName);
141: }
142: }
143: } else {
144: value = Introspector.instance().getProperty(bean,
145: propertyName);
146: }
147: }
148: return value;
149: }
150:
151: /**
152: * Gets the index of the given <code>propertyName</code> if
153: * its a nested property (nested meaning names separated by
154: * a '.').
155: * @param propertyName the name of the nested property.
156: * @return the index.
157: */
158: private final int getNestedIndex(final String propertyName) {
159: int index = -1;
160: if (StringUtils.isNotEmpty(propertyName)) {
161: index = propertyName.indexOf('.');
162: }
163: return index;
164: }
165:
166: /**
167: * Retrieves the associated Comparator for the type
168: * @param type tye class of which to retrieve the comparator.
169: * @return appropriate comparator or null if one wasn't defined.
170: */
171: private final Comparator getComparator(final Class type) {
172: try {
173: if (this .comparator == null) {
174: final String comparatorName = comparators
175: .getProperty(type.getName());
176: if (comparatorName != null
177: && comparatorName.length() > 0) {
178: this .comparator = (Comparator) ClassUtils
179: .loadClass(comparatorName).newInstance();
180: } else {
181: throw new ComparatorException(
182: "No comparator defined for the given type '"
183: + type.getName() + "'");
184: }
185: }
186: return this .comparator;
187: } catch (final Throwable throwable) {
188: throw new ComparatorException(throwable);
189: }
190: }
191:
192: /**
193: * Returns the current sortCriteria value
194: * @return String
195: */
196: public SortCriteria getSortCriteria() {
197: return this .sortCriteria;
198: }
199:
200: /**
201: * Sets the new sortCriteria value
202: * @param string
203: */
204: public void setSortCriteria(final SortCriteria sortCriteria) {
205: ExceptionUtils.checkNull("sortCriteria", sortCriteria);
206: this.sortCriteria = sortCriteria;
207: }
208: }
|