/*
* Copyright 2007 Luigi Dell'Aquila (luigi.dellaquila@assetdata.it)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.reverspring.strategy.annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.reverspring.annotations.SpringConstructor;
import org.reverspring.annotations.SpringDescribe;
import org.reverspring.strategy.DescriptionStrategy;
/**
* this class defines a strategy based on {@link SpringDescribe} and {@link SpringConstructor} annotations
* to describe attributes and constructor to be used in the XML
* @author Luigi Dell'Aquila
*
*/
public class AnnotationDescriptionStrategy implements DescriptionStrategy {
public String getDesiredId(Object obj) {
if (obj == null)
return null;
Method idMethod = null;
for (Method method : obj.getClass().getMethods()) {
SpringDescribe describe = method.getAnnotation(SpringDescribe.class);
if (describe != null && describe.isIdSource() && method.getGenericParameterTypes().length == 0) {
idMethod = method;
break;
}
}
if (idMethod == null)
return null;
try {
Object id = idMethod.invoke(obj, new Object[0]);
if (id != null && !id.toString().trim().equals(""))
return id.toString();
else
return null;
} catch (IllegalArgumentException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (InvocationTargetException e) {
return null;
}
}
public Set<String> getPropertyNames(Object obj) {
Set<String> included = new HashSet<String>();
boolean defaultInclude = false;
SpringDescribe classAnnotation = (SpringDescribe) obj.getClass().getAnnotation(SpringDescribe.class);
if (classAnnotation != null)
defaultInclude = classAnnotation.included();
// look for getters
Method[] methods = obj.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals("getClass"))
continue;
boolean methodInclude = defaultInclude;
String methodName = method.getName();
if (!isValidGetter(method))
continue;
if (!existsSetterForGetter(method, obj.getClass()))
continue;
SpringDescribe annotation = (SpringDescribe) method.getAnnotation(SpringDescribe.class);
if (annotation != null)
methodInclude = annotation.included();
if (!methodInclude)
continue;
String propertyName;
if (methodName.startsWith("get"))
propertyName = methodName.substring(3); // getXXX
else
propertyName = methodName.substring(2); // isXXX
propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
included.add(propertyName);
}
return included;
}
private static boolean isValidGetter(Method method) {
if (!Modifier.isPublic(method.getModifiers()))
return false;
Class<?> attributeType = method.getReturnType();
if (attributeType == null)
return false;
if (method.getParameterTypes().length > 0)
return false;
String name = method.getName();
if ((!name.startsWith("get")) && (!name.startsWith("is")))
return false; // invalid getter
if (name.startsWith("get"))
name = name.substring(3); // getXXX
else {
name = name.substring(2); // isXXX
}
if (name.substring(0, 1).toLowerCase().equals(name.substring(0, 1)))
return false; // after "get" there is a lowercase: ignore
return true;
}
private static boolean existsSetterForGetter(Method getter, Class<?> clazz) {
if (getter == null)
return false;
String setterName = getter.getName();
if (setterName.length() < 4)
return false;
if (setterName.startsWith("get")) // getXXX -> setXXX
setterName = "s" + setterName.substring(1);
else
setterName = "set" + setterName.substring(2); // isXXX -> setXXX
try {
Method setter = clazz.getMethod(setterName, getter.getReturnType());
if (setter == null)
return false;
} catch (Exception e) {
return false;
}
return true;
}
public boolean nested(Object obj, String propertyName) {
if(obj instanceof Map<?, ?>)
return true;
if(obj instanceof List<?>)
return true;
if(obj instanceof Set<?>)
return true;
Class<?> clazz = obj.getClass();
Method method = null;
try {
method = clazz.getMethod(
"get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1), new Class[0]);
} catch (Exception e) {
}
if (method == null) {
try {
method = clazz.getMethod("is" + propertyName.substring(0, 1).toUpperCase()
+ propertyName.substring(1), new Class[0]);
} catch (Exception e) {
}
}
if (method == null)
return false;
SpringDescribe annotation = method.getAnnotation(SpringDescribe.class);
if (annotation == null) {
SpringDescribe classAnnotation = clazz.getAnnotation(SpringDescribe.class);
if (classAnnotation == null)
return false;
return classAnnotation.nested();
}
return annotation.nested();
}
public Constructor<?> getConstructor(Object obj) {
// check annotations
Constructor<?>[] constructors = obj.getClass().getConstructors();
for (Constructor<?> constructor : constructors) {
if (constructor.getAnnotation(SpringConstructor.class) != null)
return constructor;
}
// no annotations found, check for empty constructor
try {
Constructor<?> constructor = obj.getClass().getConstructor();
if (Modifier.isPublic(constructor.getModifiers()))
return constructor;
} catch (Exception e) {
return null;
}
return null;
}
public Object[] getConstructorArgs(Object obj) throws Exception{
if (obj instanceof String)
return new String[] { obj.toString() };
if (obj instanceof Integer)
return new String[] { obj.toString() };
if (obj instanceof Long)
return new String[] { obj.toString() };
if (obj instanceof Short)
return new String[] { obj.toString() };
if (obj instanceof Float)
return new String[] { obj.toString() };
if (obj instanceof Double)
return new String[] { obj.toString() };
if (obj instanceof Boolean)
return new String[] { obj.toString() };
List<Object> params = new ArrayList<Object>();
Constructor<?> constructor = getConstructor(obj);
SpringConstructor constructorAnnotation = constructor.getAnnotation(SpringConstructor.class);
if (constructorAnnotation != null && constructorAnnotation.constructorParamsGetters() != null) {
for (String paramGetter : constructorAnnotation.constructorParamsGetters()) {
Object paramValue = obj.getClass().getMethod(paramGetter, new Class<?>[0]).invoke(obj, new Object[0]);
params.add(paramValue);
}
}
return params.toArray();
}
}