/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/proctor/resource/legal-notices/Proctor.LICENSE
* or https://proctor.dev.java.net/Proctor.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2009 Sun Microsystems, Inc.
*/
package com.ibm.staf.service.opends.tester;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.xml.bind.JAXBElement;
import java.util.Collection;
import java.util.Collections;
import org.opends.dsml.protocol.DsmlAttr;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
public class Checker {
private static final String JAXB_GENERATED_PACKAGE_NAME = "org.opends.dsml.protocol";
// Key=JAXB classes, Value=List of methods to get attribute
private static final Map<Class, List<Method>> CACHE = new HashMap<Class, List<Method>>();
// Helpfull to keep track of what argument is currently processed
private static final Stack<Method> STACK = new Stack<Method>();
// A list of attribute that are prevented to be checked
private static final Set<String> DO_NOT_CHECK = new HashSet<String>();
static {
DO_NOT_CHECK.add("ResultCode.getDescr"); // ResultCode.getCode is enough
DO_NOT_CHECK.add("LDAPResult.getErrorMessage"); // LDAPResult.getResultCode is enough
DO_NOT_CHECK.add("ErrorResponse.getMessage");
}
private static DsmlAttrCompare dsmlAttrCompare = new DsmlAttrCompare();
static {
// needed otherwise the SearchResultEntry like in test moddn998.res fail
// in the equals() method on DN.decode((String) o1....
DirectoryServer.bootstrapClient();
}
private static List<Method> getAttributes(Class clazz) {
List<Method> result = CACHE.get(clazz);
if (result == null) {
result = new ArrayList<Method>();
for (Method method : clazz.getMethods()) {
String methodName = method.getName();
Class returnType = method.getReturnType();
if (!method.getDeclaringClass().getName().startsWith(JAXB_GENERATED_PACKAGE_NAME)) {
continue;
}
if (((methodName.startsWith("get") && (methodName.length() > 3) && (!returnType.equals(void.class))) || (methodName.startsWith("is") && (methodName.length() > 2) && (returnType.equals(boolean.class)))) && (method.getParameterTypes().length == 0)) {
result.add(method);
}
}
CACHE.put(clazz, result);
}
return result;
}
private static String buildMessage() {
// return "";
StringBuilder sb = new StringBuilder(STACK.peek().toGenericString()).append(" in JAXB ");
int size = STACK.size();
for (int i = 0; i < size; i++) {
sb.append(i == 0 ? "" : ".").append(STACK.get(i).getName()).append("()");
}
return sb.toString();
}
public static boolean equals(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return true;
}
if (o1 == null || o2 == null) {
throw new JAXBCheckerException("One of the two is null : " + buildMessage());
}
// both are not null
Class c1 = o1.getClass();
if (!o2.getClass().equals(c1)) {
throw new JAXBCheckerException("Type mismatch (" + c1.getName() + " vs " + o2.getClass().getName() + ") : " + buildMessage());
}
if (c1.getName().startsWith(JAXB_GENERATED_PACKAGE_NAME)) {
for (Method method : getAttributes(c1)) {
String fullName = method.getDeclaringClass().getName() + "." + method.getName();
String s = fullName.substring(JAXB_GENERATED_PACKAGE_NAME.length() + 1, fullName.length());
if (DO_NOT_CHECK.contains(s)) {
continue;
}
Object r1, r2;
try {
STACK.push(method);
r1 = method.invoke(o1);
r2 = method.invoke(o2);
if (!equals(r1, r2)) {
// if false an exception will be thown
STACK.pop();
return false;
}
STACK.pop();
} catch (Exception e) {
throw (RuntimeException) e;
}
}
} else if (o1 instanceof List) {
// Transering into sets whenever ordering is required
Collection l1;
Collection l2;
l1 = (List) o1;
l2 = (List) o2;
if (l1 != null &&
l1.size() > 1 &&
l1.toArray()[0] instanceof DsmlAttr) {
Collections.sort((List) l1, dsmlAttrCompare);
Collections.sort((List) l2, dsmlAttrCompare);
} else if (l1 != null &&
l1.size() > 1 &&
l1.toArray()[0] instanceof String) {
Collections.sort((List) l1);
Collections.sort((List) l2);
}
/*
if (((List) o2).size() != l2.size()) {
for (Object o : l2) {
System.out.println("collection2 : " + o);
}
for (Object o : (List) o2) {
System.out.println("list2 : " + o);
}
throw (new RuntimeException("object2 size changed"));
}
*/
if (l1.size() != l2.size()) {
for (Object o : l1) {
System.out.println("list1 : " + o);
}
for (Object o : l2) {
System.out.println("list2 : " + o);
}
throw new JAXBCheckerException("List size mismatch (received=" + l1.size() + ", expected=" + l2.size() + "): " + buildMessage());
}
for (int i = 0; i < l1.size(); i++) {
// could be optimized :(
if (!equals(l1.toArray()[i], l2.toArray()[i])) {
// if false an exception will be thown
return false;
}
}
} else if (o1 instanceof JAXBElement) {
return equals(((JAXBElement) o1).getValue(), ((JAXBElement) o2).getValue());
} else if (o1 instanceof String) {
// special case in case a DN must be checked
// This is not a full check for DN but DN.decode(String) needs a DS to run
if (STACK.size() > 0) {
String s = STACK.peek().getName();
if (s.equalsIgnoreCase("getDn") ||
s.equalsIgnoreCase("getMatchedDN")) {
try {
if (!DN.decode((String) o1).equals(DN.decode((String) o2))) {
throw new JAXBCheckerException("DN mismatch : " + o1 + " vs " + o2 + " " + buildMessage());
}
} catch (DirectoryException ex) {
throw new JAXBCheckerException("DN mismatch : " + o1 + " vs " + o2 + " " + buildMessage());
}
// String oo1 = ((String) o1).replace(" ", "");
// String oo2 = ((String) o2).replace(" ", "");
// if (!oo1.equalsIgnoreCase(oo2)) {
// throw new JAXBCheckerException("DN mismatch : " + o1 + " vs " + o2 + " " + buildMessage());
// }
} else if (s.equalsIgnoreCase("getName")) {
if (!((String) o1).equalsIgnoreCase((String) o2)) {
throw new JAXBCheckerException("Attribute mismatch : " + o1 + " vs " + o2 + " " + buildMessage());
}
} else {
if (!o1.equals(o2)) {
throw new JAXBCheckerException("String mismatch : " + o1 + " vs " + o2 + " " + buildMessage());
}
}
}
} else {
if (!o1.equals(o2)) {
if (STACK.size() > 0) {
String s = STACK.peek().getName();
if (s.equalsIgnoreCase("getCode")) {
throw new JAXBCheckerException("Error code mismatch : " + LDAPResultCode.toString((Integer) o1) + "[" + o1 + "] vs " + LDAPResultCode.toString((Integer) o2) + "[" + o2 + "]" + buildMessage());
} else {
throw new JAXBCheckerException("Object mismatch : [class = " + o1.getClass().getSimpleName() + "]" + buildMessage());
}
}
}
}
return true;
}
}