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

org.apache.cayenne.reflect.BeanAccessor Maven / Gradle / Ivy

The newest version!
/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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
 *
 *    https://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.apache.cayenne.reflect;

import java.lang.reflect.Method;

/**
 * A property accessor that uses set/get methods following JavaBean naming
 * conventions.
 * 
 * @since 1.2
 */
public class BeanAccessor implements Accessor {

	private static final long serialVersionUID = 606253801447018099L;

	protected String propertyName;
	protected Method readMethod;
	protected Method writeMethod;
	protected Object nullValue;

	public BeanAccessor(Class objectClass, String propertyName, Class propertyType) {
		this( objectClass, propertyName, propertyType, defaultBooleanGetterName( propertyName ), defaultGetterName( propertyName ), defaultSetterName( propertyName ) );
	}

	protected BeanAccessor(Class objectClass, String propertyName, Class propertyType, String booleanGetterName, String getterName, String setterName ) {
		if (objectClass == null) {
			throw new IllegalArgumentException("Null objectClass");
		}

		checkPropertyName(propertyName);
		
		if (booleanGetterName == null) {
			throw new IllegalArgumentException("Null booleanGetterName");
		}
		
		if (getterName ==  null) {
			throw new IllegalArgumentException("Null getterName");
		}
		
		if (setterName == null) {
			throw new IllegalArgumentException("Null setterName");
		}

		this.propertyName = propertyName;
		this.nullValue = PropertyUtils.defaultNullValueForType(propertyType);

		Method[] publicMethods = objectClass.getMethods();

		Method getter = null;
		for (Method method : publicMethods) {
			Class returnType = method.getReturnType();
			// following Java Bean naming conventions, "is" methods are preferred over "get" methods
			if (method.getName().equals(booleanGetterName) && returnType.equals(Boolean.TYPE) && method.getParameterTypes().length == 0) {
				getter = method;
				break;
			}
			// Find the method with the most specific return type.
			// This is the same behavior as Class.getMethod(String, Class...) except that
			// Class.getMethod prefers synthetic methods generated for interfaces
			// over methods with more specific return types in a super class.
			if (method.getName().equals(getterName) && method.getParameterTypes().length == 0) {
				if (returnType.isPrimitive()) {
					getter = returnType.equals(Void.TYPE) ? null : method;
					if (returnType.equals(Boolean.TYPE)) {
						// keep looking for the "is" method
						continue;
					} else {
						// nothing more specific than a primitive, so stop here
						break;
					}
				}
				if (getter == null || getter.getReturnType().isAssignableFrom(returnType)) {
					getter = method;
				}
			}
		}

		if (getter == null) {
			throw new IllegalArgumentException("Property '" + propertyName + "' is not readable");
		}

		this.readMethod = getter;

		// TODO: compare 'propertyType' arg with readMethod.getReturnType()

		for (Method method : publicMethods) {
			if (!method.getName().equals(setterName) || !method.getReturnType().equals(Void.TYPE)) {
				continue;
			}
			Class[] parameterTypes = method.getParameterTypes();
			if (parameterTypes.length != 1) {
				continue;
			}
			if (getter.getReturnType().isAssignableFrom(parameterTypes[0])) {
				this.writeMethod = method;
				break;
			}
		}
	}

	public String getName() {
		return propertyName;
	}

	/**
	 * @since 3.0
	 */
	public Object getValue(Object object) throws PropertyException {

		try {
			return readMethod.invoke(object, (Object[]) null);
		} catch (Throwable th) {
			throw new PropertyException("Error reading property: " + propertyName, this, object, th);
		}
	}

	/**
	 * @since 3.0
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void setValue(Object object, Object newValue) throws PropertyException {

		if (writeMethod == null) {
			throw new PropertyException("Property '" + propertyName + "' is not writable", this, object);
		}

		Class type = writeMethod.getParameterTypes()[0];
		Converter converter = ConverterFactory.factory.getConverter(type);
		try {
			newValue = (converter != null) ? converter.convert(newValue, type) : newValue;
	
			// this will take care of primitives.
			if (newValue == null) {
				newValue = this.nullValue;
			}

			writeMethod.invoke(object, newValue);
		} catch (Throwable th) {
			throw new PropertyException("Error writing property: " + propertyName, this, object, th);
		}
	}
	
	private static String defaultSetterName( String propertyName ) {
		checkPropertyName(propertyName);
		final String capitalized = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
		return "set" + capitalized;
	}

	private static String defaultGetterName( String propertyName ) {
		checkPropertyName(propertyName);
		final String capitalized = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
		return "get" + capitalized;
	}

	private static String defaultBooleanGetterName( String propertyName ) {
		checkPropertyName(propertyName);
		final String capitalized = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
		return "is" + capitalized;
	}

	private static void checkPropertyName(String propertyName) {
		if (propertyName == null) {
			throw new IllegalArgumentException("Null propertyName");
		}

		if (propertyName.length() == 0) {
			throw new IllegalArgumentException("Empty propertyName");
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy