001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.beans.support;
018:
019: import java.util.Arrays;
020: import java.util.Collections;
021: import java.util.Comparator;
022: import java.util.List;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import org.springframework.beans.BeanWrapperImpl;
028: import org.springframework.beans.BeansException;
029: import org.springframework.util.StringUtils;
030:
031: /**
032: * PropertyComparator performs a comparison of two beans,
033: * evaluating the specified bean property via a BeanWrapper.
034: *
035: * @author Juergen Hoeller
036: * @author Jean-Pierre Pawlak
037: * @since 19.05.2003
038: * @see org.springframework.beans.BeanWrapper
039: */
040: public class PropertyComparator implements Comparator {
041:
042: protected final Log logger = LogFactory.getLog(getClass());
043:
044: private final SortDefinition sortDefinition;
045:
046: private final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(
047: false);
048:
049: /**
050: * Create a new PropertyComparator for the given SortDefinition.
051: * @see MutableSortDefinition
052: */
053: public PropertyComparator(SortDefinition sortDefinition) {
054: this .sortDefinition = sortDefinition;
055: }
056:
057: /**
058: * Create a PropertyComparator for the given settings.
059: * @param property the property to compare
060: * @param ignoreCase whether upper and lower case in String values should be ignored
061: * @param ascending whether to sort ascending (true) or descending (false)
062: */
063: public PropertyComparator(String property, boolean ignoreCase,
064: boolean ascending) {
065: this .sortDefinition = new MutableSortDefinition(property,
066: ignoreCase, ascending);
067: }
068:
069: /**
070: * Return the SortDefinition that this comparator uses.
071: */
072: public final SortDefinition getSortDefinition() {
073: return sortDefinition;
074: }
075:
076: public int compare(Object o1, Object o2) {
077: Object v1 = getPropertyValue(o1);
078: Object v2 = getPropertyValue(o2);
079: if (this .sortDefinition.isIgnoreCase()
080: && (v1 instanceof String) && (v2 instanceof String)) {
081: v1 = ((String) v1).toLowerCase();
082: v2 = ((String) v2).toLowerCase();
083: }
084:
085: int result;
086:
087: // Put an object with null property at the end of the sort result.
088: try {
089: if (v1 != null) {
090: result = (v2 != null ? ((Comparable) v1).compareTo(v2)
091: : -1);
092: } else {
093: result = (v2 != null ? 1 : 0);
094: }
095: } catch (RuntimeException ex) {
096: if (logger.isWarnEnabled()) {
097: logger.warn("Could not sort objects [" + o1 + "] and ["
098: + o2 + "]", ex);
099: }
100: return 0;
101: }
102:
103: return (this .sortDefinition.isAscending() ? result : -result);
104: }
105:
106: /**
107: * Get the SortDefinition's property value for the given object.
108: * @param obj the object to get the property value for
109: * @return the property value
110: */
111: private Object getPropertyValue(Object obj) {
112: // If a nested property cannot be read, simply return null
113: // (similar to JSTL EL). If the property doesn't exist in the
114: // first place, let the exception through.
115: try {
116: this .beanWrapper.setWrappedInstance(obj);
117: return this .beanWrapper
118: .getPropertyValue(this .sortDefinition.getProperty());
119: } catch (BeansException ex) {
120: logger
121: .info(
122: "PropertyComparator could not access property - treating as null for sorting",
123: ex);
124: return null;
125: }
126: }
127:
128: /**
129: * Sort the given List according to the given sort definition.
130: * <p>Note: Contained objects have to provide the given property
131: * in the form of a bean property, i.e. a getXXX method.
132: * @param source the input List
133: * @param sortDefinition the parameters to sort by
134: * @throws java.lang.IllegalArgumentException in case of a missing propertyName
135: */
136: public static void sort(List source, SortDefinition sortDefinition)
137: throws BeansException {
138: if (StringUtils.hasText(sortDefinition.getProperty())) {
139: Collections.sort(source, new PropertyComparator(
140: sortDefinition));
141: }
142: }
143:
144: /**
145: * Sort the given source according to the given sort definition.
146: * <p>Note: Contained objects have to provide the given property
147: * in the form of a bean property, i.e. a getXXX method.
148: * @param source input source
149: * @param sortDefinition the parameters to sort by
150: * @throws java.lang.IllegalArgumentException in case of a missing propertyName
151: */
152: public static void sort(Object[] source,
153: SortDefinition sortDefinition) throws BeansException {
154: if (StringUtils.hasText(sortDefinition.getProperty())) {
155: Arrays.sort(source, new PropertyComparator(sortDefinition));
156: }
157: }
158:
159: }
|