001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.config.schema.utils;
005:
006: import org.apache.xmlbeans.XmlObject;
007:
008: import com.tc.util.Assert;
009:
010: import java.lang.reflect.Array;
011: import java.lang.reflect.InvocationTargetException;
012: import java.lang.reflect.Method;
013: import java.util.ArrayList;
014: import java.util.List;
015:
016: /**
017: * The standard implementation of {@link XmlObjectComparator}.
018: */
019: public class StandardXmlObjectComparator implements XmlObjectComparator {
020:
021: public boolean equals(XmlObject one, XmlObject two) {
022: try {
023: checkEquals(one, two);
024: return true;
025: } catch (NotEqualException nee) {
026: return false;
027: }
028: }
029:
030: public void checkEquals(XmlObject one, XmlObject two)
031: throws NotEqualException {
032: checkEquals(one, two, "");
033: }
034:
035: private void checkEquals(XmlObject one, XmlObject two, String where)
036: throws NotEqualException {
037: Assert.assertNotNull(where);
038:
039: if ((one == null) != (two == null)) {
040: // formatting
041: throw new NotEqualException(
042: where
043: + ": Objects are not both null or not both non-null.");
044: }
045:
046: if (one == null)
047: return;
048:
049: Class oneInterface = getBeanInterface(one);
050: Class twoInterface = getBeanInterface(two);
051:
052: if (!oneInterface.equals(twoInterface)) {
053: // formatting
054: throw new NotEqualException(where + ": Bean interface for "
055: + one + " is " + oneInterface
056: + ", and bean interface for two is " + twoInterface
057: + ".");
058: }
059:
060: Method[] methods = fetchAndFilterMethods(oneInterface);
061: Assert.eval(methods.length > 0);
062:
063: for (int i = 0; i < methods.length; ++i) {
064: Method method = methods[i];
065:
066: String this Where = where + "/"
067: + getPropertyFromMethodName(method.getName());
068:
069: try {
070: Object oneValue = method.invoke(one, null);
071: Object twoValue = method.invoke(two, null);
072:
073: compareValues(this Where, oneValue, twoValue);
074: } catch (IllegalArgumentException iae) {
075: throw Assert
076: .failure(
077: this Where
078: + ": Unable to fetch property from a bean; method "
079: + method + " failed.", iae);
080: } catch (IllegalAccessException iae) {
081: throw Assert
082: .failure(
083: this Where
084: + ": Unable to fetch property from a bean; method "
085: + method + " failed.", iae);
086: } catch (InvocationTargetException ite) {
087: throw Assert
088: .failure(
089: this Where
090: + ": Unable to fetch property from a bean; method "
091: + method + " failed.", ite);
092: }
093: }
094: }
095:
096: private void compareValues(String this Where, Object oneValue,
097: Object twoValue) throws NotEqualException {
098: if ((oneValue == null) != (twoValue == null)) {
099: // formatting
100: throw new NotEqualException(this Where + ": First value "
101: + (oneValue == null ? "is" : "isn't") + " null, "
102: + "but second value "
103: + (twoValue == null ? "is" : "isn't."));
104: }
105:
106: if (oneValue != null) {
107: if ((oneValue instanceof XmlObject)
108: && (twoValue instanceof XmlObject)) {
109: checkEquals((XmlObject) oneValue, (XmlObject) twoValue,
110: this Where);
111: } else if ((oneValue instanceof XmlObject)
112: || (twoValue instanceof XmlObject)) {
113: throw new NotEqualException(
114: this Where
115: + ": One value is an XmlObject and the other isn't; value one is "
116: + oneValue + ", and value two is "
117: + twoValue);
118: } else if (oneValue.getClass().isArray()
119: && twoValue.getClass().isArray()) {
120: if (Array.getLength(oneValue) != Array
121: .getLength(twoValue)) {
122: // formatting
123: throw new NotEqualException(this Where
124: + ": Value one is an array of length "
125: + Array.getLength(oneValue)
126: + ", and value two "
127: + "is an array of length "
128: + Array.getLength(twoValue));
129: }
130:
131: int length = Array.getLength(oneValue);
132: for (int j = 0; j < length; ++j) {
133: compareValues(this Where + "[" + j + "]", Array.get(
134: oneValue, j), Array.get(twoValue, j));
135: }
136: } else {
137: if (!oneValue.equals(twoValue)) {
138: // formatting
139: throw new NotEqualException(
140: this Where
141: + ": Neither value is an XmlObject, and Object.equals() didn't return true; value one is '"
142: + oneValue
143: + "', and value two is '"
144: + twoValue + "'.");
145: }
146: }
147: }
148: }
149:
150: private static String getPropertyFromMethodName(String methodName) {
151: Assert.assertNotBlank(methodName);
152: Assert.eval(methodName.length() >= "get".length());
153:
154: return methodName.substring("get".length(), "get".length() + 1)
155: .toLowerCase()
156: + methodName.substring("get".length() + 1);
157: }
158:
159: private static Class getBeanInterface(Object value) {
160: Class[] interfaces = value.getClass().getInterfaces();
161:
162: if (interfaces.length != 1) {
163: // formatting
164: throw Assert.failure("Class " + value.getClass()
165: + ", the class of object " + value
166: + ", implements " + interfaces.length
167: + " interfaces, not 1. We don't support this yet.");
168: }
169: return interfaces[0];
170: }
171:
172: private static Method[] fetchAndFilterMethods(Class theClass) {
173: Method[] allMethods = theClass.getDeclaredMethods();
174: List out = new ArrayList();
175:
176: for (int i = 0; i < allMethods.length; ++i) {
177: Method method = allMethods[i];
178:
179: if (method.getParameterTypes().length == 0
180: && method.getName().startsWith("get"))
181: out.add(method);
182: }
183:
184: return (Method[]) out.toArray(new Method[out.size()]);
185: }
186:
187: }
|