1 /* 2 * Copyright 2004 Sun Microsystems, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 package com.sun.syndication.feed.impl; 18 19 import java.beans.PropertyDescriptor; 20 import java.lang.reflect.Array; 21 import java.lang.reflect.Method; 22 import java.io.Serializable; 23 24 /** 25 * Provides deep <b>Bean</b> equals() and hashCode() functionality for Java Beans. 26 * <p> 27 * It works on all read/write properties, recursively. It support all primitive types, Strings, Collections, 28 * bean-like objects and multi-dimensional arrays of any of them. 29 * <p> 30 * The hashcode is calculated by getting the hashcode of the Bean String representation. 31 * <p> 32 * @author Alejandro Abdelnur 33 * 34 */ 35 public class EqualsBean implements Serializable { 36 37 private static final Object[] NO_PARAMS = new Object[0]; 38 39 private Class _beanClass; 40 private Object _obj; 41 42 /** 43 * Default constructor. 44 * <p> 45 * To be used by classes extending EqualsBean only. 46 * <p> 47 * @param beanClass the class/interface to be used for property scanning. 48 * 49 */ 50 protected EqualsBean(Class beanClass) { 51 _beanClass = beanClass; 52 _obj = this; 53 } 54 55 /** 56 * Creates a EqualsBean to be used in a delegation pattern. 57 * <p> 58 * For example: 59 * <p> 60 * <code> 61 * public class Foo implements FooI { 62 * private EqualsBean _equalsBean; 63 * 64 * public Foo() { 65 * _equalsBean = new EqualsBean(FooI.class); 66 * } 67 * 68 * public boolean equals(Object obj) { 69 * return _equalsBean.beanEquals(obj); 70 * } 71 * 72 * public int hashCode() { 73 * return _equalsBean.beanHashCode(); 74 * } 75 * 76 * } 77 * </code> 78 * <p> 79 * @param beanClass the class/interface to be used for property scanning. 80 * @param obj object bean to test equality. 81 * 82 */ 83 public EqualsBean(Class beanClass,Object obj) { 84 if (!beanClass.isInstance(obj)) { 85 throw new IllegalArgumentException(obj.getClass()+" is not instance of "+beanClass); 86 } 87 _beanClass = beanClass; 88 _obj = obj; 89 } 90 91 /** 92 * Indicates whether some other object is "equal to" this object as defined by the Object equals() method. 93 * <p> 94 * To be used by classes extending EqualsBean. Although it works also for classes using 95 * EqualsBean in a delegation pattern, for correctness those classes should use the 96 * @see #beanEquals(Object) beanEquals method. 97 * <p> 98 * @param obj he reference object with which to compare. 99 * @return <b>true</b> if 'this' object is equal to the 'other' object. 100 * 101 */ 102 public boolean equals(Object obj) { 103 return beanEquals(obj); 104 } 105 106 /** 107 * Indicates whether some other object is "equal to" the object passed in the constructor, 108 * as defined by the Object equals() method. 109 * <p> 110 * To be used by classes using EqualsBean in a delegation pattern, 111 * @see #EqualsBean(Class,Object) constructor. 112 * <p> 113 * @param obj he reference object with which to compare. 114 * @return <b>true</b> if the object passed in the constructor is equal to the 'obj' object. 115 * 116 */ 117 public boolean beanEquals(Object obj) { 118 Object bean1 = _obj; 119 Object bean2 = obj; 120 boolean eq; 121 if (bean1==null && bean2==null) { 122 eq = true; 123 } 124 else 125 if (bean1==null || bean2==null) { 126 eq = false; 127 } 128 else { 129 if (!_beanClass.isInstance(bean2)) { 130 eq = false; 131 } 132 else { 133 eq = true; 134 try { 135 PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(_beanClass); 136 if (pds!=null) { 137 for (int i = 0; eq && i<pds.length; i++) { 138 Method pReadMethod = pds[i].getReadMethod(); 139 if (pReadMethod!=null && // ensure it has a getter method 140 pReadMethod.getDeclaringClass()!=Object.class && // filter Object.class getter methods 141 pReadMethod.getParameterTypes().length==0) { // filter getter methods that take parameters 142 Object value1 = pReadMethod.invoke(bean1, NO_PARAMS); 143 Object value2 = pReadMethod.invoke(bean2, NO_PARAMS); 144 eq = doEquals(value1, value2); 145 } 146 } 147 } 148 } 149 catch (Exception ex) { 150 throw new RuntimeException("Could not execute equals()", ex); 151 } 152 } 153 } 154 return eq; 155 } 156 157 /** 158 * Returns the hashcode for this object. 159 * <p> 160 * It follows the contract defined by the Object hashCode() method. 161 * <p> 162 * The hashcode is calculated by getting the hashcode of the Bean String representation. 163 * <p> 164 * To be used by classes extending EqualsBean. Although it works also for classes using 165 * EqualsBean in a delegation pattern, for correctness those classes should use the 166 * @see #beanHashCode() beanHashCode method. 167 * <p> 168 * @return the hashcode of the bean object. 169 * 170 */ 171 public int hashCode() { 172 return beanHashCode(); 173 } 174 175 /** 176 * Returns the hashcode for the object passed in the constructor. 177 * <p> 178 * It follows the contract defined by the Object hashCode() method. 179 * <p> 180 * The hashcode is calculated by getting the hashcode of the Bean String representation. 181 * <p> 182 * To be used by classes using EqualsBean in a delegation pattern, 183 * @see #EqualsBean(Class,Object) constructor. 184 * <p> 185 * @return the hashcode of the bean object. 186 * 187 */ 188 public int beanHashCode() { 189 return _obj.toString().hashCode(); 190 } 191 192 193 private boolean doEquals(Object obj1, Object obj2) { 194 boolean eq = obj1==obj2; 195 if (!eq && obj1!=null && obj2!=null) { 196 Class classObj1 = obj1.getClass(); 197 Class classObj2 = obj2.getClass(); 198 if (classObj1.isArray() && classObj2.isArray()) { 199 eq = equalsArray(obj1, obj2); 200 } 201 else { 202 eq = obj1.equals(obj2); 203 } 204 } 205 return eq; 206 } 207 208 private boolean equalsArray(Object array1, Object array2) { 209 boolean eq; 210 int length1 = Array.getLength(array1); 211 int length2 = Array.getLength(array2); 212 if (length1==length2) { 213 eq = true; 214 for (int i = 0; eq && i<length1; i++) { 215 Object e1 = Array.get(array1, i); 216 Object e2 = Array.get(array2, i); 217 eq = doEquals(e1, e2); 218 } 219 } 220 else { 221 eq = false; 222 } 223 return eq; 224 } 225 226 } 227