001/* 002 * Copyright 2002-2012 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 * https://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 017package org.springframework.beans.support; 018 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027import org.springframework.beans.BeanWrapperImpl; 028import org.springframework.beans.BeansException; 029import 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 */ 040public class PropertyComparator<T> implements Comparator<T> { 041 042 protected final Log logger = LogFactory.getLog(getClass()); 043 044 private final SortDefinition sortDefinition; 045 046 private final BeanWrapperImpl beanWrapper = new BeanWrapperImpl(false); 047 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, boolean ascending) { 064 this.sortDefinition = new MutableSortDefinition(property, ignoreCase, ascending); 065 } 066 067 /** 068 * Return the SortDefinition that this comparator uses. 069 */ 070 public final SortDefinition getSortDefinition() { 071 return this.sortDefinition; 072 } 073 074 075 @Override 076 @SuppressWarnings("unchecked") 077 public int compare(T o1, T o2) { 078 Object v1 = getPropertyValue(o1); 079 Object v2 = getPropertyValue(o2); 080 if (this.sortDefinition.isIgnoreCase() && (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<Object>) v1).compareTo(v2) : -1); 091 } 092 else { 093 result = (v2 != null ? 1 : 0); 094 } 095 } 096 catch (RuntimeException ex) { 097 if (logger.isWarnEnabled()) { 098 logger.warn("Could not sort objects [" + o1 + "] and [" + 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.getPropertyValue(this.sortDefinition.getProperty()); 118 } 119 catch (BeansException ex) { 120 logger.info("PropertyComparator could not access property - treating as null for sorting", ex); 121 return null; 122 } 123 } 124 125 126 /** 127 * Sort the given List according to the given sort definition. 128 * <p>Note: Contained objects have to provide the given property 129 * in the form of a bean property, i.e. a getXXX method. 130 * @param source the input List 131 * @param sortDefinition the parameters to sort by 132 * @throws java.lang.IllegalArgumentException in case of a missing propertyName 133 */ 134 public static void sort(List<?> source, SortDefinition sortDefinition) throws BeansException { 135 if (StringUtils.hasText(sortDefinition.getProperty())) { 136 Collections.sort(source, new PropertyComparator<Object>(sortDefinition)); 137 } 138 } 139 140 /** 141 * Sort the given source according to the given sort definition. 142 * <p>Note: Contained objects have to provide the given property 143 * in the form of a bean property, i.e. a getXXX method. 144 * @param source input source 145 * @param sortDefinition the parameters to sort by 146 * @throws java.lang.IllegalArgumentException in case of a missing propertyName 147 */ 148 public static void sort(Object[] source, SortDefinition sortDefinition) throws BeansException { 149 if (StringUtils.hasText(sortDefinition.getProperty())) { 150 Arrays.sort(source, new PropertyComparator<Object>(sortDefinition)); 151 } 152 } 153 154}