/***
 * 
 * Copyright 2014 Andrew Hall
 *
 * 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.statefulj.common.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReflectionUtils {
	
	private static Pattern fieldNamePattern = Pattern.compile("[g|s]et(.)(.*)");
	
	public static Field getFirstAnnotatedField(
			Class<?> clazz,
			Class<? extends Annotation> annotationClass) { 
		Field match = null;
		if (clazz != null) {
			for(Field field : clazz.getDeclaredFields()) {
				if (field.isAnnotationPresent(annotationClass)) {
					match = field;
					break;
				}
			}
			if (match == null) {
				match = getFirstAnnotatedField(clazz.getSuperclass(), annotationClass);
			}
		}
		
		return match;
	}
	
	public static List<Field> getAllAnnotatedFields(
			Class<?> clazz,
			Class<? extends Annotation> annotationClass) {
		List<Field> fields = new LinkedList<Field>();
		if (clazz != null) {
			fields.addAll(getAllAnnotatedFields(clazz.getSuperclass(), annotationClass));
			for(Field field : clazz.getDeclaredFields()) {
				if (field.isAnnotationPresent(annotationClass)) {
					fields.add(field);
				}
			}
		}
		
		return fields;
	}
	
	public static Method getFirstAnnotatedMethod(
			Class<?> clazz,
			Class<? extends Annotation> annotationClass) {
		Method match = null;
		if (clazz != null) {
			for(Method method : clazz.getDeclaredMethods()) {
				if (method.isAnnotationPresent(annotationClass)) {
					match = method;
					break;
				}
			}
			if (match == null) {
				match = getFirstAnnotatedMethod(clazz.getSuperclass(), annotationClass);
			}
		}
		
		return match;
	}
	public static boolean isGetter(Method method){
		  if(!method.getName().startsWith("get"))      return false;
		  if(method.getParameterTypes().length != 0)   return false;  
		  if(void.class.equals(method.getReturnType())) return false;
		  return true;
	}
	
	public static boolean isSetter(Method method){
	  if(!method.getName().startsWith("set")) return false;
	  if(method.getParameterTypes().length != 1) return false;
	  return true;
	}
	
	public static String toFieldName(Method getterOrSetter) {
		Matcher matcher = fieldNamePattern.matcher(getterOrSetter.getName());
		return (matcher.matches()) ? matcher.group(1).toLowerCase() + matcher.group(2) : null;
	}
		
	public static Field getReferencedField(
			Class<?> clazz,
			Class<? extends Annotation> annotationClass) {
		Field field = getFirstAnnotatedField(clazz, annotationClass);
		if (field == null) {
			Method method = getFirstAnnotatedMethod(clazz, annotationClass);
			if (method != null && (isGetter(method) || isSetter(method))) {
				String fieldName = toFieldName(method);
				try {
					field = (fieldName != null) ? clazz.getDeclaredField(fieldName) : null;
				} catch (Exception e) {
					// Ignore
				}
			}
		}
		return field;
	}
}

@@ -3477,24 +3477,188 @@
 gnore%0A%09%09%09%09%7D%0A
+%09%09%09%09if (field == null) %7B%0A%09%09%09%09%09try %7B%0A%09%09%09%09%09%09field = (fieldName != null) ? clazz.getField(fieldName) : null;%0A%09%09%09%09%09%7D catch (Exception e) %7B%0A%09%09%09%09%09%09// Ignore%0A%09%09%09%09%09%7D%0A%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D%0A%09%09r

Check parent and interfaces for field