/*
*
* Copyright 2012 Luca Molino (molino.luca--AT--gmail.com)
*
* 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 com.orientechnologies.orient.object.enhancement;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javassist.util.proxy.MethodFilter;
import com.orientechnologies.common.log.OLogManager;
/**
* @author luca.molino Original implementation
* @author Janos Haber Scala binding
*
*/
public class OObjectMethodFilter implements MethodFilter {
private Map<Method, String> fieldNameCache = new HashMap<Method, String>();
private Map<Method, Boolean> isSetterCache = new HashMap<Method, Boolean>();
private Map<Method, Boolean> isGetterCache = new HashMap<Method, Boolean>();
public boolean isHandled(final Method m) {
final String fieldName = getFieldName(m);
if (fieldName == null)
return false;
try {
if (!OObjectEntitySerializer.isClassField(m.getDeclaringClass(), fieldName))
return false;
return (isSetterMethod(m) || isGetterMethod(m));
} catch (NoSuchFieldException nsfe) {
OLogManager.instance().warn(this, "Error handling the method %s in class %s", nsfe, m.getName(),
m.getDeclaringClass().getName());
return false;
} catch (SecurityException se) {
OLogManager.instance().warn(this, "", se, m.getName(), m.getDeclaringClass().getName());
return false;
}
}
public String getFieldName(final Method m) {
String fieldName = fieldNameCache.get(m);
if (fieldName != null){
return fieldName;
}
final String methodName = m.getName();
final Class<?> clz = m.getDeclaringClass();
if (methodName.startsWith("get"))
fieldName = getFieldName(methodName, "get");
else if (methodName.startsWith("set"))
fieldName = getFieldName(methodName, "set");
else if (methodName.startsWith("is"))
fieldName = getFieldName(methodName, "is");
else if (isScalaClass(clz)) {
fieldName = getScalaFieldName(clz, methodName);
}
if (fieldName != null){
fieldNameCache.put(m, fieldName);
return fieldName;
}
// NO FIELD
return null;
}
protected String getFieldName(final String methodName, final String prefix) {
final StringBuffer fieldName = new StringBuffer();
fieldName.append(Character.toLowerCase(methodName.charAt(prefix.length())));
fieldName.append(methodName.substring(prefix.length() + 1));
return fieldName.toString();
}
public boolean isSetterMethod(final Method m) throws SecurityException, NoSuchFieldException {
Boolean cachedIsSetter = isSetterCache.get(m);
if (cachedIsSetter != null){
return cachedIsSetter;
}
String methodName = m.getName();
Class<?> clz = m.getDeclaringClass();
if (!methodName.startsWith("set") || !checkIfFirstCharAfterPrefixIsUpperCase(methodName, "set")
|| (isScalaClass(clz) && !methodName.endsWith("_$eq"))){
isSetterCache.put(m, false);
return false;
}
if (m.getParameterTypes() != null && m.getParameterTypes().length != 1){
isSetterCache.put(m, false);
return false;
}
if (OObjectEntitySerializer.isTransientField(m.getDeclaringClass(), getFieldName(m))){
isSetterCache.put(m, false);
return false;
}
Class<?>[] parameters = m.getParameterTypes();
Field f = OObjectEntitySerializer.getField(getFieldName(m), m.getDeclaringClass());
if (!f.getType().isAssignableFrom(parameters[0])) {
OLogManager.instance().warn(
this,
"Setter method " + m.toString() + " for field " + f.getName() + " in class " + m.getDeclaringClass().toString()
+ " cannot be bound to proxied instance: parameter class don't match with field type " + f.getType().toString());
isSetterCache.put(m, false);
return false;
}
isSetterCache.put(m, true);
return true;
}
public boolean isGetterMethod(Method m) throws SecurityException, NoSuchFieldException {
Boolean cachedIsGetter = isGetterCache.get(m);
if (cachedIsGetter != null){
return cachedIsGetter;
}
String methodName = m.getName();
int prefixLength;
Class<?> clz = m.getDeclaringClass();
if (methodName.startsWith("get") && checkIfFirstCharAfterPrefixIsUpperCase(methodName, "get"))
prefixLength = "get".length();
else if (methodName.startsWith("is") && checkIfFirstCharAfterPrefixIsUpperCase(methodName, "is"))
prefixLength = "is".length();
else if (isScalaClass(clz) && methodName.equals(getFieldName(m)))
prefixLength = 0;
else {
isGetterCache.put(m, false);
return false;
}
if (m.getParameterTypes() != null && m.getParameterTypes().length > 0){
isGetterCache.put(m, false);
return false;
}
if (methodName.length() <= prefixLength){
isGetterCache.put(m, false);
return false;
}
boolean isGetter = !OObjectEntitySerializer.isTransientField(m.getDeclaringClass(), getFieldName(m));
isGetterCache.put(m, isGetter);
return isGetter;
}
private boolean checkIfFirstCharAfterPrefixIsUpperCase(String methodName, String prefix) {
return methodName.length() > prefix.length() ? Character.isUpperCase(methodName.charAt(prefix.length())) : false;
}
protected boolean isScalaClass(Class<?> clz) {
Annotation[] annotations = OObjectEntitySerializer.getDeclaredAnnotations(clz);
for (Annotation a : annotations) {
if ("scala.reflect.ScalaSignature".contains(a.annotationType().getName())
|| "scala.reflect.ScalaLongSignature".contains(a.getClass().getName())) {
return true;
}
}
return false;
}
protected String getScalaFieldName(Class<?> clz, String name) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
if (name.equals(field.getName() + "_$eq")) {
return field.getName();
} else if (name.equals(field.getName())) {
return field.getName();
}
}
return null;
}
}