All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.officefloor.web.value.retrieve.ValueRetrieverSource Maven / Gradle / Ivy

There is a newer version: 3.40.0
Show newest version
/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2018 Daniel Sagenschneider
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package net.officefloor.web.value.retrieve;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Sources the {@link ValueRetriever}.
 * 
 * @author Daniel Sagenschneider
 */
public class ValueRetrieverSource {

	/**
	 * Retrieves the value from the object.
	 * 
	 * @param object
	 *            {@link Object} containing the value.
	 * @param method
	 *            {@link Method} to retrieve the value.
	 * @return Value retrieved. May be null.
	 * @throws Exception
	 *             If fails to retrieve the value.
	 */
	public static Object retrieveValue(Object object, Method method) throws Exception {
		try {

			// Retrieve the value
			return method.invoke(object);

		} catch (InvocationTargetException ex) {

			// Propagate cause (if possible)
			Throwable cause = ex.getCause();
			if (cause instanceof Exception) {
				throw (Exception) cause;
			} else if (cause instanceof Error) {
				throw (Error) cause;
			} else {
				// Throw original invocation exception
				throw ex;
			}
		}
	}

	/**
	 * Recursively creates the {@link PropertyMetaData} for the type.
	 * 
	 * @param type
	 *            Type to create the {@link PropertyMetaData} instances.
	 * @param isCaseInsensitive
	 *            Indicates if case insensitve property names.
	 * @param metaDataByType
	 *            {@link PropertyMetaData} for the type to allow tracking
	 *            recursive properties (bean or its downstream property
	 *            returning same type as the bean).
	 * @return {@link PropertyMetaData} instances for the type.
	 */
	private static PropertyMetaData[] createPropertyMetaData(Class type, boolean isCaseInsensitive,
			Map, PropertyMetaData[]> metaDataByType) {

		final String GETTER_PREFIX = "get";

		// Determine if have meta-data already for type
		PropertyMetaData[] recursiveMetaData = metaDataByType.get(type);
		if (recursiveMetaData != null) {
			return recursiveMetaData;
		}

		// Iterate over the methods to obtain the properties
		List propertyMethods = new ArrayList();
		NEXT_METHOD: for (Method method : type.getMethods()) {

			// Ignore Object methods
			if (Object.class.equals(method.getDeclaringClass())) {
				continue NEXT_METHOD;
			}

			// Ensure a public method with no parameters
			if (!Modifier.isPublic(method.getModifiers())) {
				continue NEXT_METHOD;
			}
			if (method.getParameterTypes().length != 0) {
				continue NEXT_METHOD;
			}

			// Ensure the method begins with 'get'
			String methodName = method.getName();
			if (!methodName.startsWith(GETTER_PREFIX)) {
				continue NEXT_METHOD;
			}

			// Ensure there is a property name (something after 'get')
			String propertyName = methodName.substring(GETTER_PREFIX.length());
			if ((propertyName == null) || (propertyName.length() == 0)) {
				continue NEXT_METHOD;
			}

			// Ensure returns a value
			Class returnType = method.getReturnType();
			if ((returnType == null) || (returnType == Void.class)) {
				continue NEXT_METHOD;
			}

			// Add the property method
			propertyMethods.add(method);
		}

		// Create the property meta-data array for the type
		PropertyMetaData[] metaData = new PropertyMetaData[propertyMethods.size()];
		metaDataByType.put(type, metaData);

		// Load property meta-data (after adding meta-data for recursion)
		for (int i = 0; i < metaData.length; i++) {

			// Obtain the method and its details
			Method method = propertyMethods.get(i);
			String methodName = method.getName();
			Class returnType = method.getReturnType();

			// Obtain the property name
			String propertyName = methodName.substring(GETTER_PREFIX.length());
			if (isCaseInsensitive) {
				propertyName = propertyName.toLowerCase();
			}

			// Obtain the property meta data for the return type
			PropertyMetaData[] returnTypeMetaData = createPropertyMetaData(returnType, isCaseInsensitive,
					metaDataByType);

			// Create and register the meta data for the property
			PropertyMetaData propertyMetaData = new PropertyMetaData(propertyName, method, returnTypeMetaData);
			metaData[i] = propertyMetaData;
		}

		// Return the meta data
		return metaData;
	}

	/**
	 * Flag indicating if case insensitive.
	 */
	private boolean isCaseInsensitive;

	/**
	 * Instantiate.
	 * 
	 * @param isCaseInsensitive
	 *            Indicates if property name comparison is case insensitive.
	 */
	public ValueRetrieverSource(boolean isCaseInsensitive) {
		this.isCaseInsensitive = isCaseInsensitive;
	}

	/**
	 * Sources the {@link ValueRetriever} for the type.
	 * 
	 * @param 
	 *            Type.
	 * @param type
	 *            Type.
	 * @return {@link ValueRetriever} for the type.
	 */
	public  ValueRetriever sourceValueRetriever(Class type) {

		// Obtain the property meta data
		PropertyMetaData[] metaData = createPropertyMetaData(type, this.isCaseInsensitive,
				new HashMap, PropertyMetaData[]>());

		// Return the value retriever
		return new RootValueRetrieverImpl(metaData, this.isCaseInsensitive);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy