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

com.numdata.oss.db.ReflectedClassHandler Maven / Gradle / Ivy

/*
 * Copyright (c) 2017, Numdata BV, The Netherlands.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Numdata nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NUMDATA BV BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.numdata.oss.db;

import java.lang.reflect.*;

import org.jetbrains.annotations.*;

/**
 * {@link ClassHandler} implementation based on reflection.
 *
 * @author  Peter S. Heijnen
 */
public class ReflectedClassHandler
	extends ClassHandler
{
	/**
	 * Cache for {@link #getTableName} result.
	 */
	private String _tableName = null;

	/**
	 * Cache for {@link #getCreateStatement} result.
	 */
	private String _createStatement = null;

	/**
	 * Java field used for record id.
	 */
	private final Field _idField;

	/**
	 * Create class handler.
	 *
	 * @param   clazz   Class to handle.
	 */
	protected ReflectedClassHandler( @NotNull final Class clazz )
	{
		super( clazz );

		Field idField = null;

		for ( final Field field : clazz.getFields() )
		{
			final FieldHandler fieldHandler = createFieldHandler( field );
			if ( fieldHandler != null )
			{
				addField( fieldHandler );

				if ( ( idField == null ) && isRecordIdField( field ) )
				{
					idField = field;
				}
			}
		}

		_idField = idField;
	}

	/**
	 * Test whether the specified field is the 'record id' field.
	 *
	 * @param   field   Field to test.
	 *
	 * @return  {@code true} if field is 'record id' field;
	 *          {@code false} otherwise.
	 */
	protected boolean isRecordIdField( @NotNull final Field field )
	{
		final TableRecord tableRecord = _clazz.getAnnotation( TableRecord.class );
		final String recordId = ( tableRecord != null ) ? tableRecord.recordId() : "ID";

		return recordId.equals( field.getName() );
	}

	/**
	 * Create {@link FieldHandler} for a {@link Field}. This may be overridden
	 * to implement special handlers. This may also return {@code null} to
	 * indicate that the field is not handled (i.e. it is ignored).
	 *
	 * @param   field   Field to test.
	 *
	 * @return  {@link FieldHandler} for field;
	 *          {@code null} if field is not handled.
	 */
	@Nullable
	protected FieldHandler createFieldHandler( @NotNull final Field field )
	{
		final FieldHandler result;

		final Class type = field.getType();
		final int modifiers = field.getModifiers();

		if ( Modifier.isPublic( modifiers ) &&
		     !Modifier.isStatic( modifiers ) &&
		     !Modifier.isTransient( modifiers ) &&
		     !Modifier.isVolatile( modifiers ) &&
		     ( !Modifier.isFinal( modifiers ) || !type.isPrimitive() ) ) // primitives must be final
		{
			result = new ReflectedFieldHandler( field );
		}
		else
		{
			result = null;
		}

		return result;
	}

	@NotNull
	@Override
	public String getTableName()
	{
		String result = _tableName;
		if ( result == null )
		{
			final Class clazz = _clazz;

			final TableRecord tableRecord = clazz.getAnnotation( TableRecord.class );
			if ( tableRecord != null )
			{
				result = tableRecord.tableName();
			}
			else /* fall back to 'old-school' field constant using reflection */
			{
				final String fieldName = "TABLE_NAME";

				try
				{
					final Field tableNameField = clazz.getField( fieldName );
					result = (String)tableNameField.get( null );
				}
				catch ( IllegalAccessException e )
				{
					throw new RuntimeException( "Access denied to '" + fieldName + "' constant in class '" + clazz.getName() + '\'', e );
				}
				catch ( NoSuchFieldException e )
				{
					throw new RuntimeException( "Missing '" + fieldName + "' constant in class '" + clazz.getName() + '\'', e );
				}
				catch ( NullPointerException e )
				{
					throw new RuntimeException( "The '" + fieldName + "' field of '" + clazz.getName() + "' is not static", e );
				}
				catch ( SecurityException e )
				{
					throw new RuntimeException( "Reflection access to '" + fieldName + "' field of '" + clazz.getName() + "' denied (" + e + ')', e );
				}
			}

			_tableName = result;
		}

		return result;
	}

	@NotNull
	@Override
	public String getCreateStatement()
	{
		String result = _createStatement;
		if ( result == null )
		{
			final Class clazz = _clazz;

			final String fieldName = "MYSQL_CREATE_STATEMENT";

			try
			{
				final Field createStatementField = clazz.getField( fieldName );
				result = (String)createStatementField.get( null );
			}
			catch ( NoSuchFieldException e )
			{
				throw new RuntimeException( "No create '" + fieldName + " constant in class '" + clazz.getName() + '\'', e );
			}
			catch ( IllegalAccessException e )
			{
				throw new RuntimeException( "Access denied to '" + fieldName + " constant in class '" + clazz.getName() + '\'', e );
			}

			_createStatement = result;
		}

		return result;
	}

	@Override
	public boolean hasRecordId()
	{
		return ( _idField != null );
	}

	@NotNull
	@Override
	public String getRecordIdColumn()
	{
		final Field idField = _idField;
		if ( idField == null )
		{
			throw new IllegalArgumentException( _clazz.getName() + " class has no record ID" );
		}

		return idField.getName();
	}

	@Override
	public long getRecordId( @NotNull final Object object )
	{
		final Field idField = _idField;
		if ( idField == null )
		{
			throw new IllegalArgumentException( _clazz.getName() + " class has no record ID" );
		}

		try
		{
			return idField.getLong( object );
		}
		catch ( IllegalAccessException e )
		{
			throw new IllegalArgumentException( "Error getting " + idField.getName() + " field from " + object, e );
		}
	}

	@Override
	public void setRecordId( @NotNull final Object object, final long id )
	{
		final Field idField = _idField;
		if ( idField == null )
		{
			throw new IllegalArgumentException( _clazz.getName() + " class has no record ID" );
		}

		try
		{
			final Class idType = idField.getType();
			if ( ( idType == int.class ) && ( (long)(int)id == id ) )
			{
				idField.setInt( object, (int)id );
			}
			else
			{
				idField.setLong( object, id );
			}
		}
		catch ( IllegalAccessException e )
		{
			throw new IllegalArgumentException( "Error getting " + idField.getName() + " field from " + object, e );
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy