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

org.mentabean.BeanConfig Maven / Gradle / Ivy

There is a newer version: 2.2.4
Show newest version
/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 * 
 * MentaBean => http://www.mentabean.org
 * Author: Sergio Oliveira Jr. ([email protected])
 */
package org.mentabean;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.mentabean.event.TriggerDispatcher;
import org.mentabean.event.TriggerListener;
import org.mentabean.type.AutoIncrementType;
import org.mentabean.type.SequenceType;
import org.mentabean.util.PropertiesProxy;

/**
 * A class representing a bean configuration, like table name, primary keys and fields in the database.
 * 
 * @author [email protected]
 */
public class BeanConfig {

	private final Map fieldList = new LinkedHashMap();

	private final Map pkList = new LinkedHashMap();
	
	private final Map> abstractInstances = new LinkedHashMap>();
	
	private final Class beanClass;

	private final String tableName;

	private DBField sequence = null;
	
	private String sequenceName = null;

	private DBField autoincrement = null;
	
	private TriggerDispatcher dispatcher = new TriggerDispatcher();

	/**
	 * Creates a configuration for a bean represented by the given class.
	 * 
	 * @param beanClass
	 *            The bean klass
	 * @param tableName
	 *            The database table where the bean properties will be stored.
	 */
	public BeanConfig(final Class beanClass, final String tableName) {

		this.beanClass = beanClass;

		this.tableName = tableName;
	}

	/**
	 * Return the table name where the bean properties are stored.
	 * 
	 * @return The database table name.
	 */
	public String getTableName() {

		return tableName;
	}

	/**
	 * Return the bean class.
	 * 
	 * @return The bean class.
	 */
	public Class getBeanClass() {

		return beanClass;
	}

	/**
	 * Add the sequence name *in the database* that will be used for this field.
	 * 
	 * NOTE: A field of type SEQUENCE must have been defined before or an IllegalStateException is thrown.
	 * 
	 * @param seqNameInDb the name of the sequence in the database
	 * @return this bean config
	 */
	public BeanConfig addSequenceName(String seqNameInDb) {
		
		if (sequence == null) {
			throw new IllegalStateException("There is no sequence field defined!");
		}
		
		sequenceName = seqNameInDb;
		return this;
	}
	
	/**
	 * Alias for method addSequence
	 * 
	 * @param seqNameInDb
	 * @return this bean config
	 */
	public BeanConfig seq(String seqNameInDb) {
		return addSequenceName(seqNameInDb);
	}

	/**
	 * Returns the name of the sequence in the database.
	 * 
	 * NOTE: The name returned is the name of the sequence in the database.
	 * 
	 * @return the name of the sequence in the database.
	 */
	public String getSequenceName() {
		return sequenceName;
	}
	
	public BeanConfig remove(final String name) {
		
		if (name == null) {
			return remove((Object) null);
		}
		
		fieldList.remove(name);
		pkList.remove(name);
		return this;
	}
	
	public BeanConfig remove(Object propValueFromGetter) {
		return remove(PropertiesProxy.getPropertyName());
	}
	
	private BeanConfig addField(final String name, final String dbName, final DBType type, final boolean isPK) {

		if (!isPK) {

			if (type instanceof SequenceType) {
				throw new IllegalStateException("A sequence type can only be a primary key!");
			}

			if (type instanceof AutoIncrementType) {
				throw new IllegalStateException("A auto-increment type can only be a primary key!");
			}
		}

		final DBField f = new DBField(name, dbName, type, isPK);

		fieldList.remove(name); // just in case we are re-adding it...

		fieldList.put(name, f);
		
		if (isPK) {

			pkList.remove(name); // just in case we are re-adding it...

			pkList.put(name, f);

			if (type instanceof SequenceType) {

				if (sequence != null) {
					throw new IllegalStateException("A bean can have only one sequence field!");
				}

				sequence = f;

			} else if (type instanceof AutoIncrementType) {

				if (autoincrement != null) {
					throw new IllegalStateException("A bean can have only one auto-increment field!");
				}

				autoincrement = f;
			}
		}

		return this;
	}
	
	public DBField getField(String name) {
		
		return fieldList.get(name);
	}

	/**
	 * Return an auto-increment field, if one was configured for this bean.
	 * 
	 * Note: A bean can have only one auto-increment field configured for this bean. Attempting to configure more than one will throw an exception.
	 * 
	 * @return the auto-increment field configured for this bean or null if it was not defined
	 */
	public DBField getAutoIncrementField() {

		return autoincrement;
	}

	/**
	 * Return a sequence field, if one was configured for this bean.
	 * 
	 * Note: A bean can have only one sequence field configured for this bean. Attempting to configure more than one will throw an exception.
	 * 
	 * @return the sequence field configured for this bean or null if it was not defined
	 */
	public DBField getSequenceField() {

		return sequence;
	}

	/**
	 * Add a database field for the given property with the given database type. It assumes that the property name is the SAME as the database column name. If they are different, use the other addField method.
	 * 
	 * @param name
	 *            The bean property name (same as the database column name)
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig field(final String name, final DBType type) {

		if (name == null) {
			return field((Object) null, type);
		}
		
		return addField(name, name, type, false);
	}
	
	public BeanConfig field(Object propValueFromGetter, final DBType type) {
		
		return field(PropertiesProxy.getPropertyName(), type);
	}

	/**
	 * Add a database field for the given property with the given database type.
	 * 
	 * @param name
	 *            The bean property name
	 * @param dbName
	 *            The name of the database column holding this property
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig field(final String name, final String dbName, final DBType type) {
		
		if (name == null) {
			return field((Object) null, dbName, type);
		}

		return addField(name, dbName, type, false);
	}
	
	public BeanConfig field(Object propValueFromGetter, String dbName, final DBType type) {
		
		return field(PropertiesProxy.getPropertyName(), dbName, type);
	}

	/**
	 * Add a bean property that is the primary key in the database. The column name is the same as the property bean name. If they are different use the other pk method. All beans must have a primary key and you can call this method multiple times to support composite primary keys.
	 * 
	 * @param name
	 *            The bean property name
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig pk(final String name, final DBType type) {
		
		if (name == null) {
			return pk((Object) null, type);
		}

		return addField(name, name, type, true);
	}
	
	public BeanConfig pk(Object propValueFromGetter, final DBType type) {
		
		return pk(PropertiesProxy.getPropertyName(), type);
	}

	/**
	 * Add a property that is the primary key in the database. All beans must have a primary key and you can call this method multiple times to support composite primary keys.
	 * 
	 * @param name
	 *            The bean property name
	 * @param dbName
	 *            The name of the database column holding this property
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig pk(final String name, final String dbName, final DBType type) {
		
		if (name == null) {
			return pk((Object) null, dbName, type);
		}

		return addField(name, dbName, type, true);
	}
	
	public BeanConfig pk(Object propValueFromGetter, String dbName, final DBType type) {
		
		return pk(PropertiesProxy.getPropertyName(), dbName, type);
	}

	/**
	 * Return the number of fields configured for this bean. It includes the PK.
	 * 
	 * @return The number of fields configured for this bean.
	 */
	public int getNumberOfFields() {

		return fieldList.size();
	}

	public int getNumberOfPKs() {

		return pkList.size();
	}

	@Override
	public String toString() {

		final StringBuilder sb = new StringBuilder(64);

		sb.append("BeanConfig: ").append(beanClass.getName()).append(" tableName=").append(tableName);

		return sb.toString();
	}

	/**
	 * Return all DBFields configured for this bean. It includes the PK as well.
	 * 
	 * @return An Iterator with all DBFields configured for this bean.
	 */
	public Iterator fields() {

		return fieldList.values().iterator();
	}

	/**
	 * Check whether the primary key was defined.
	 * 
	 * @return true if a primary key was defined.
	 */
	public boolean hasPK() {

		return !pkList.isEmpty();
	}

	/**
	 * Return an iterator with the DBFields for the PK configured for this bean.
	 * 
	 * Note: A bean can have more than one property as its primary key, in that case it has a composite primary key.
	 * 
	 * @return An iterator with the DBFields for the PK.
	 */
	public Iterator pks() {

		return pkList.values().iterator();
	}
	
	public BeanConfig trigger(TriggerListener trigger) {
		
		dispatcher.addTrigger(trigger);
		
		return this;
	}
	
	public BeanConfig remove(TriggerListener trigger) {
		
		dispatcher.removeTrigger(trigger);
		
		return this;
	}
	
	public TriggerDispatcher getDispatcher() {
		return dispatcher;
	}
	
	/**
	 * Configures a class that should be used instead of property type to create instances
	 * through {@link Class#newInstance()} method. It's useful when working with abstract objects
	 * @param abstractProperty - The property name
	 * @param clazz - The concrete class
	 * @return this
	 */
	public BeanConfig abstractInstance(String abstractProperty, Class clazz) {
		
		if (abstractProperty == null) {
			return abstractInstance((Object) abstractProperty, clazz);
		}
		
		abstractInstances.put(abstractProperty, clazz);
		
		return this;
		
	}	
	
	/**
	 * Configures a class that should be used instead of property type to create instances
	 * through {@link Class#newInstance()} method. It's useful when working with abstract objects
	 * @param abstractProperty - The property name (through proxy)
	 * @param clazz - The concrete class
	 * @return this
	 */
	public  BeanConfig abstractInstance(E abstractProperty, Class clazz) {
		
		return abstractInstance(PropertiesProxy.getPropertyName(), clazz);
		
	}
	
	public Class getAbstractProperty(String key) {
		
		return abstractInstances.get(key);
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy