/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.ejb.entity.beans;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;

import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;

import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

//***************************************************************************
//* Class Definition and Members                                            *
//***************************************************************************

/**
 * Abstract Entity Bean which all Entity Beans should inherit from.
 * This class provides an <b>id property which is mapped to the id column of the 
 * referenced db table. This column has to be the primary key of the table</b>.
 *	
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 * @author Nico Mack nico.mack(at)tudor.lu
 *	 
 * @Version
 * <br>$Log: GECAMedEntityBean.java,v $
 * <br>Revision 1.28  2014-02-18 06:31:10  ferring
 * <br>Logger changed
 * <br>
 * <br>Revision 1.27  2013-09-30 10:44:03  ferring
 * <br>update can now also be performed on objects that are instance of this GECAMedEntityBean
 * <br>
 * <br>Revision 1.26  2013-07-15 06:18:37  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.25  2013-03-22 10:26:26  ferring
 * <br>Method added to copy the value of all fields from one object to another
 * <br>
 * <br>Revision 1.24  2013-01-25 14:44:10  ferring
 * <br>logging added and errors corrected
 * <br>
 * <br>Revision 1.23  2011-02-01 13:05:30  ferring
 * <br>just small update
 * <br>
 * <br>Revision 1.22  2009-10-26 16:11:45  mack
 * <br>Improved bean cloning
 * <br>
 * <br>Revision 1.21  2009-06-04 14:41:59  mack
 * <br>Added a couple of useful reflection methods
 * <br>
 * <br>Revision 1.20  2009-01-09 14:00:33  mack
 * <br>Fixed a typo in error message
 * <br>
 * <br>Revision 1.19  2008-11-17 16:26:12  mack
 * <br>The getProperty method now also looks for methods starting with IS in case there is no getter starting with GET.
 * <br>
 * <br>Revision 1.18  2008-10-07 09:33:30  heinemann
 * <br>GECAMedEntityBean now implements Comparable
 * <br>
 * <br>Revision 1.17  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.16  2008-07-21 13:38:44  mack
 * <br>Fixed a couple of bugs
 * <br>
 * <br>Revision 1.15  2008-07-21 10:13:29  mack
 * <br>Added support for non assignable properties
 * <br>
 * <br>Revision 1.14  2008-04-16 15:56:12  mack
 * <br>Minor improvement to clone method. Method no longer complains if setter for a given getter does not exist
 * <br>
 * <br>Revision 1.13  2008-04-11 15:32:31  mack
 * <br>Implemented generic clone method
 * <br>
 * <br>Revision 1.12  2008-03-21 17:09:46  mack
 * <br>Added newInstanceOf method
 * <br>
 * <br>Revision 1.11  2008-03-17 16:05:20  mack
 * <br>Added methods to set properties via reflection.
Added logging support
 * <br>
 * <br>Revision 1.10  2008-01-15 09:29:39  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 */

@MappedSuperclass
public class GECAMedEntityBean implements Serializable, Comparable<GECAMedEntityBean>
	{
	private static final long serialVersionUID = 1L;

	private Integer	m_Id;
	
	private Hashtable <String,Object> m_NonAssignables;
	
	private transient Logger m_Logger = Logger.getLogger(GECAMedEntityBean.class);
	

//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************
//---------------------------------------------------------------------------

public GECAMedEntityBean ()
	{
	this.setLoggerForClass (this.getClass());
	}

//---------------------------------------------------------------------------
/**
 * Creates an new instance of this CoreEntityBean and initializes all
 * properties of new instance that have a setter method with values from
 * this instance's properties.
 * @returns an initialized copy of this instance. Please note that you
 * <b>MUST</b> cast the returned Object to the respective type of cloned
 * class.
 */
//---------------------------------------------------------------------------

public Object clone () {
	return this.clone(false);
}

public Object clone (boolean quiet)
	{
	GECAMedEntityBean	l_Clone = null;
	
	try	{
		l_Clone = newInstanceOf (this.getClass().getName());
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Failed to clone myself!",p_Exception);	
		return null;
		}
				
	l_Clone = this.update(l_Clone, quiet);
	
	return l_Clone;
	}

//---------------------------------------------------------------------------
/**
 * Initializes all properties of specified target instance that have a setter 
 * method with values from this instance's properties.
 * @param p_Target specifies the instance of the object to update. Target
 * MUST be of same class as this instance.
 * @returns an updated copy of the specified target instance. Please note that
 * you <b>MUST</b> cast the returned Object to the respective type of cloned
 * class.
 */
//---------------------------------------------------------------------------
public GECAMedEntityBean update (GECAMedEntityBean p_Target) {
	return this.update(p_Target, false);
}

public GECAMedEntityBean update (GECAMedEntityBean p_Target, boolean quiet)
	{
	Collection <String>	l_Properties;
	Iterator <String>	l_PropertyIterator;
	String				l_Property;
	
	if ((p_Target == null) || (!p_Target.getClass().isInstance(this)))
		{
		this.log(Level.ERROR, "Failed to update target!");	
		return null;
		}
				
	l_Properties = this.getProperties();
	if (l_Properties != null)
		{
		l_PropertyIterator = l_Properties.iterator();
		while (l_PropertyIterator.hasNext())
			{
			l_Property = l_PropertyIterator.next();
			try	{
				p_Target.setProperty (l_Property, 
								      this.getPropertyType(l_Property), 
								      this.getProperty(l_Property));	
				}
			// Can be ignored. If there ain't no setter, then skip it.
			catch (NoSuchMethodException p_Exception){}	
			catch (IllegalArgumentException p_Exception) {}
			catch (Exception p_Exception)
				{
				if (quiet) {
					this.log(Level.INFO, "Failed to copy property " + l_Property + "!");	
				} else {
					this.log(Level.ERROR, "Failed to copy property " + l_Property + "!",p_Exception);
				}
				}
			}
		}
	
	return p_Target;
	}


@SuppressWarnings("unchecked")
public <T extends GECAMedEntityBean> T updateFields (T target)
{
	return updateFields(target, getSuperClasses((Class<? super T>) target.getClass()));
}


public <T extends GECAMedEntityBean> T updateFields (T target, Class<? super T> ... clazzes)
{
	int fieldModifiers;
	
	
	for (Class<?> clazz : clazzes)
	{
		for (Field field : clazz.getDeclaredFields())
		{
			try
			{
				field.setAccessible(true);
				fieldModifiers	= field.getModifiers();
				if (field.isAccessible() 
						&& !Modifier.isFinal(fieldModifiers)
						&& !Modifier.isStatic(fieldModifiers))
					field.set(target, field.get(this));
			}
			catch (Exception e)
			{
				log(Level.ERROR, "Failed to copy property " + field.getName() + ":\n" + e.getMessage());
			}
		}
	}
	
	return target;
}


@SuppressWarnings("unchecked")
public static <T extends GECAMedEntityBean> Class<? super T>[] getSuperClasses (Class<? super T> clazz)
{
	List<Class<? super T>>	clazzList	= new LinkedList<Class<? super T>>();
	
	
	while (clazz != null)
	{
		clazzList.add(clazz);
		clazz	= clazz.getSuperclass();
	}
	
	return clazzList.toArray(new Class[clazzList.size()]);
}


//***************************************************************************
//* Primitives                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * writes the specified message with the specified log level into the log
 * file.
 * @param p_Level specifies the log level the specified message should have.
 * @param p_Message specifies the message to be written to log file.
 */
//---------------------------------------------------------------------------

@Transient
protected void log (Level p_Level, String p_Message)
	{
	this.log(p_Level,p_Message,null);
	}

//---------------------------------------------------------------------------
/**
 * writes the specified message with the specified log level into the log
 * file. The stack trace of the specified exception will be logged to.
 * @param p_Level specifies the log level the specified message should have.
 * @param p_Message specifies the message to be written to log file.
 * @param p_Exception specifies the exception to log stack trace of.
 */
//---------------------------------------------------------------------------

@Transient
protected void log (Level p_Level, String p_Message, Exception p_Exception)
	{
	StackTraceElement[] l_StackTrace; 
	String				l_MethodName;
		                  
	if (m_Logger == null) this.setLoggerForClass(this.getClass());
	
	l_StackTrace = new Throwable().getStackTrace();
	l_MethodName = l_StackTrace[1].getMethodName();
		
	if (l_MethodName.equals("log")) l_MethodName = l_StackTrace[2].getMethodName();
	p_Message = "\n" + l_MethodName + " => " + p_Message;
			
	if (p_Exception != null) m_Logger.log (p_Level,p_Message,p_Exception);
						else m_Logger.log (p_Level,p_Message);
	}

//---------------------------------------------------------------------------
/**
 * sets the logger for the specified class.
 * @param p_ExtendingClass specfies the extending class to set logger for.
 */
//---------------------------------------------------------------------------

@Transient
protected void setLoggerForClass (Class<?> p_ExtendingClass)
	{
	m_Logger = Logger.getLogger(p_ExtendingClass);
	}

//---------------------------------------------------------------------------
/**
 * Returns the logger object for this bean.
 * @return the current logger object.
 */
//---------------------------------------------------------------------------

@Transient
protected Category getLogger ()
	{
	return m_Logger;
	}

//---------------------------------------------------------------------------
/**
 * capitalizes the first character of the specified string.
 * @param p_String specifies the string to capitalize first character of.
 * @return the specified string with the first character in uppercase if
 * specified string is not <code>null</code>.
 */
//---------------------------------------------------------------------------

@Transient
private String capitalize (String p_String) 
	{
	if ((p_String != null) && (p_String.length() > 0))
		{
		return p_String.substring(0, 1).toUpperCase() + p_String.substring(1);
		}
	else return p_String;	
	}
	
//---------------------------------------------------------------------------
/**
 * Converts the content of the specified String to an Object of the specified
 * class. Accepted classes are:
 * <ul>
 * <li>Integer</li>
 * <li>Long</li>
 * <li>Float</li>
 * <li>Double</li>
 * <li>BigDecimal</li>
 * <li>Boolean</li>
 * <li>Character</li>
 * </ul>
 * @param p_TargetClass specifies the class of the desired return object.
 * @param p_Text specifies the value this object should have.
 * @return An object of the desired type having its value set to the one
 * specified by p_Text if possible, <code>null</code> otherwise. In case
 * an unsupported return type is specified, the specified string object
 * will simply be returned.
 */
//---------------------------------------------------------------------------

protected Object cast (Class<?> p_TargetClass, String p_Text)
	{
	Object	l_Cast = null;

	try	{
		if (p_TargetClass.equals(Integer.class))
			{
			l_Cast = Integer.parseInt(p_Text); 
			return l_Cast;
			}
		
		else if (p_TargetClass.equals(Long.class))
			{
			l_Cast = Long.parseLong(p_Text);
			return l_Cast;
			}
		
		else if (p_TargetClass.equals(Float.class))
			{
			l_Cast = Float.parseFloat(p_Text);	
			return l_Cast;
			}
		
		else if (p_TargetClass.equals(Double.class))
			{
			l_Cast = Double.parseDouble(p_Text);
			return l_Cast;
			}
		
		else if (p_TargetClass.equals(BigDecimal.class))
			{
			l_Cast = new BigDecimal (p_Text);
			return l_Cast;
			}
		
		else if (p_TargetClass.equals(Boolean.class))
			{
			l_Cast = Boolean.parseBoolean(p_Text);
			return l_Cast;
			}		
		
		else if (p_TargetClass.equals(Character.class))
			{
			l_Cast = Character.valueOf(p_Text.charAt(0));
			return l_Cast;
			}		
		
		else l_Cast = (String) p_Text;
		}
	catch (NumberFormatException p_Exception)
		{
		l_Cast = null;
		}

	return l_Cast;
	}

//---------------------------------------------------------------------------
/**
 * Returns the value of the specified property. The method relies on
 * reflection to call the getter method for the specified property.
 * @param p_Property specifies the name of the property to get value of.
 * @return The return value of the getter method for the specified property,
 * if available. Method returns <code>null</code> if either specified
 * property is <code>null</code> or no getter exists.
 */
//---------------------------------------------------------------------------

@Transient
public Object getProperty (String p_Property) throws Exception {
	return this.getProperty(p_Property, false);
}

@Transient
public Object getProperty (String p_Property, boolean quiet) throws Exception 
	{
	Class<?>[]  l_ParameterTypes = null;
	Object[] 	l_Parameters     = null;
	String	 	l_MethodName;	
	Method	 	l_Getter;
	
	if (p_Property == null) return null;
	
	try	{
		l_MethodName = "get" + capitalize(p_Property);
		l_Getter = this.getClass().getMethod(l_MethodName, l_ParameterTypes);
		}
	catch (NoSuchMethodException p_Exception)
		{		
		l_Getter = null;
		}
		
	if (l_Getter == null)
		{
		try	{
			l_MethodName = "is" + capitalize(p_Property);
			l_Getter = this.getClass().getMethod(l_MethodName, l_ParameterTypes);
			}
		catch (NoSuchMethodException p_Exception)
			{		
			if (! quiet) {
				this.log(Level.INFO, "Getter for property " + p_Property + " does not exists!");	
			}
			l_Getter = null;
			}
		}
	
	if (l_Getter != null)
		 return l_Getter.invoke (this, l_Parameters);
	else return null;
	}

//---------------------------------------------------------------------------
/**
 * Calls the setter method for the specified property. The method relies on
 * reflextion to call the setter method for the specified property and
 * argument type.
 * @param p_Property specifies the property to set the value of.
 * @param p_Type specifies the type of the value we'd like to set.
 * @param p_Value specifies the value of type p_Type we'd like to set specified
 * property to.
 */
//---------------------------------------------------------------------------

@Transient
public void setProperty (String p_Property, Class<?> p_Type, Object p_Value) throws Exception 
	{
	Class<?>	l_Class;
	Class<?>[]  l_ParameterTypes = {p_Type};
	Object[] 	l_Parameters     = {p_Value};
	String	 	l_MethodName;	
	Method	 	l_Setter		  = null;
	
	if (p_Property == null) return;

	l_MethodName = "set" + capitalize(p_Property);

//	try	{
		l_Class = this.getClass();
		
		do	{
			try	{
				l_Setter = l_Class.getDeclaredMethod (l_MethodName, l_ParameterTypes);
				}
			catch (NoSuchMethodException p_Exception)
				{
				l_Class = l_Class.getSuperclass();
				}
			}
		while ((l_Setter == null) && (l_Class != null));	

		if (l_Setter != null) 
			{
			l_Setter.invoke (this, l_Parameters);
			}
		else this.log(Level.DEBUG, "Setter for property " + p_Property + " does not exists!");	
//		}
//	catch (Exception p_Exception)
//		{
//		this.log(Level.ERROR, "Error while setting property " + p_Property + "!",p_Exception);	
//		}
	}
	
//---------------------------------------------------------------------------
/**
 * Sets the specified property to the specified value. Property type is
 * automatically derived and if value can be assigned, the property will
 * be set. If specified property does not exist, name of property and
 * its value will be stored in the beans non assignable store.
* @param p_Property specifies the property to be set.
 * @param p_Value specifies the value to be given to the specified property.
  * @see #setNonAssignable
 */
//---------------------------------------------------------------------------

public void setProperty (String p_Property, Object p_Value)
	{
	Class<?>	l_Type;	
	
	if (p_Value == null) return;
				
	try	{
		l_Type = this.getPropertyType (p_Property);
		if (l_Type != null)
			{
			if (l_Type.isAssignableFrom (p_Value.getClass()))
				this.setProperty (p_Property, l_Type, p_Value);
			else if (p_Value instanceof String)			
				this.setProperty(p_Property, l_Type, this.cast(l_Type, (String)p_Value));
			else if (l_Type.isAssignableFrom (AbstractList.class))
				this.setProperty (p_Property, l_Type, new ArrayList((Collection<?>) p_Value));			
			}
		else this.setNonAssignable (p_Property, p_Value);	
		}
	
	catch(Exception p_Exception)
		{
		this.log(Level.ERROR, "Error while setting " + p_Property + " of " + this.getClass().getName(), p_Exception);
		}
	}

//---------------------------------------------------------------------------
/**
 * Returns the type (or class) for the specified property.
 * @param p_Property specifies the property to get the type of.
 * @return type (or class) for the specified property.
 */
//---------------------------------------------------------------------------

@Transient
public Class<?> getPropertyType (String p_Property)
	{
	BeanInfo				l_BeanInfo;
	PropertyDescriptor[]	l_Descriptors;
	PropertyDescriptor		l_Descriptor;
	Class<?>				l_PropertyType = null;
	int						l_Index = 0;
	boolean					l_Found = false;
	
	try	{
		l_BeanInfo = Introspector.getBeanInfo (this.getClass());
		l_Descriptors = l_BeanInfo.getPropertyDescriptors();
	
		while ((l_Index < l_Descriptors.length) && !l_Found)
			{
			l_Descriptor = l_Descriptors [l_Index];	
			if (l_Descriptor.getName().equals(p_Property))
				{
				l_PropertyType = l_Descriptor.getPropertyType();
				l_Found = true;
				}
			else l_Index++;	
			}
		}
	catch (IntrospectionException p_Exception)
		{	
		}
	
	return 	l_PropertyType;
	}

//---------------------------------------------------------------------------
/**
 * Returns a list of all properties defined for this bean
 */
//---------------------------------------------------------------------------

@Transient
public Collection <String> getProperties ()
	{
	BeanInfo				l_BeanInfo;
	PropertyDescriptor[]	l_Descriptors;
	PropertyDescriptor		l_Descriptor;
	int						l_Index;
	
	Collection <String>		l_Properties;		
	
	l_Properties = new HashSet <String> ();
	
	try	{
		l_BeanInfo = Introspector.getBeanInfo( this.getClass() );
		l_Descriptors = l_BeanInfo.getPropertyDescriptors();
	
		for (l_Index = 0; l_Index < l_Descriptors.length; l_Index++)
			{
			l_Descriptor = l_Descriptors [l_Index];
			l_Properties.add(l_Descriptor.getName());
			}
		}
	catch (IntrospectionException p_Exception)
		{	
		}
	
	return l_Properties;
	}

//---------------------------------------------------------------------------
/**
 * Creates a new instance of the specified class.
 * @param p_ClassName specifies the class name of the GECAMedEntityBean to
 * instantiate.
 * @return a new instance of the specified class.
 */
//---------------------------------------------------------------------------

@Transient
public static GECAMedEntityBean newInstanceOf (String p_ClassName) throws Exception 
	{
	if (p_ClassName == null) return null;
		
	Class<?>    	l_Class = Class.forName(p_ClassName);
	Class<?>[]  	l_Types = null;	
	Object[] 		l_Parameters = null;
	Constructor<?>	l_Constructor;
	
	l_Class.getConstructors();
	l_Constructor = l_Class.getConstructor(l_Types);	
	return (GECAMedEntityBean) l_Constructor.newInstance(l_Parameters);
	}

//***************************************************************************
//* Class Body                                                              *
//***************************************************************************

//---------------------------------------------------------------------------
/**
 * Overrides java.lang.Object equals method in order to make sure that
 * equality means same object ID.
 * @return <b>true</b> if the two objects have the same ID, <b>false</b> otherwise
 */
//---------------------------------------------------------------------------	 

public boolean equals (Object p_OtherGECAMedEntityBean)	
	{
	GECAMedEntityBean l_OtherGECAMedEntityBean;
		
	if (p_OtherGECAMedEntityBean == null) return false;
	
	if (! (p_OtherGECAMedEntityBean instanceof GECAMedEntityBean)) return false;

	l_OtherGECAMedEntityBean = (GECAMedEntityBean) p_OtherGECAMedEntityBean;
	if ((this.getId() != null) && (l_OtherGECAMedEntityBean.getId() != null))
		 return l_OtherGECAMedEntityBean.getId().equals(this.getId());
	else return super.equals(p_OtherGECAMedEntityBean);
	}

//---------------------------------------------------------------------------
/**
 * Overrides java.lang.Object hashCode method. Algorithm taken from 
 * Effective Java Book, Chapter 3. Overriding hashCode method is a MUST
 * when you override equals method.
 */
//---------------------------------------------------------------------------

public int hashCode ()
	{
	int	l_Code = 17;
	
	if (m_Id != null)	
		{
		l_Code = 37*l_Code + m_Id.intValue();
		}		
	else	
		{
		l_Code = super.hashCode();
		}
		
	return l_Code;
	}


/* (non-Javadoc)
 * @see java.lang.Comparable#compareTo(java.lang.Object)
 */
public int compareTo(GECAMedEntityBean o) {
	/* ====================================================== */
	/* ------------------------------------------------------- */
	if (o == null)
		return -1;
	else
		return GECAMedUtils.compareTo(this.getId(), o.getId());
//	if (this.getId() == null && o.getId() == null)
//		return 0;
//	if (this.getId() != null && o.getId() == null)
//		return 1;
//	if (this.getId() == null && o.getId() != null)
//		return -1;
//	/* ------------------------------------------------------- */
//	if (this.getId() > o.getId())
//		return 1;
//	if (this.getId() < o.getId())
//		return -1;
//	/* ------------------------------------------------------- */
//	return 0;
	/* ====================================================== */
}


//---------------------------------------------------------------------------
/**
 * Get the object ID.
 * this is the primary key of the mapped db table
 * @return The object ID.
 */
//---------------------------------------------------------------------------
	
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "id")

public Integer getId() 
	{
	return this.m_Id;
	}

//---------------------------------------------------------------------------
/**
 * Sets the object ID
 * this is the primary key of the mapped db table
 * @param p_ID
 */
//---------------------------------------------------------------------------

public void setId (Integer p_Id) 
	{
	this.m_Id = p_Id;
	}

//---------------------------------------------------------------------------
/**
 * Returns the non assignable property specified by the provided key. Non
 * assignable properties are temporary properties of GECAMedEntityBeans which
 * are not persisted.
 * @param p_Key specifies the key of the non assignable property.
 * @return the value of the non assignable property specified by the provided
 * key if it exists, <code>null</code> otherwise.
 */
//---------------------------------------------------------------------------

@Transient
public Object getNonAssignable (String p_Key)
	{
	if ((m_NonAssignables != null) && (m_NonAssignables.containsKey(p_Key)))
		 return m_NonAssignables.get(p_Key);
	else return null;	
	}

//---------------------------------------------------------------------------
/**
 * Sets the non assignable property specified by the provided key to the
 * specified value.
 * @param p_Key specifies the key of the non assignable property to set
 * the value of.
 * @param p_Value specifies the value to be set for the non assignable
 * property.
 */
//---------------------------------------------------------------------------

@Transient
public void setNonAssignable (String p_Key, Object p_Value)
	{
	if (p_Key == null) 
		{
		this.log(Level.WARN, "NULL specified as key for non assignable! Doing Nothing!");
		return;
		}
		
	if (m_NonAssignables == null) m_NonAssignables = new Hashtable <String,Object> ();
		
	m_NonAssignables.put(p_Key, p_Value);
	}

//---------------------------------------------------------------------------
/**
 * Removes the specified non assignable property from this beans store, if
 * such a property existed.
 * @param p_Key specifies the key of the non assignable property to be
 * removed.
 */
//---------------------------------------------------------------------------

@Transient
public void removeNonAssignable (String p_Key)
	{
	if ((m_NonAssignables != null) && (m_NonAssignables.containsKey(p_Key)))
		{
		m_NonAssignables.remove(p_Key);
		}
	}

//---------------------------------------------------------------------------
/**
 * Returns the keys of all non assignable properties set so far.
 * @return an enumeration holding the keys of all defined non assignable
 * properties.
 */
//---------------------------------------------------------------------------

@Transient
public Enumeration <String> getNonAssignableKeys ()
	{
	if (m_NonAssignables != null) 
		return m_NonAssignables.keys ();
	else return null;
	}

//---------------------------------------------------------------------------

@Transient
public Boolean containsNonAssignable (String p_Key)
	{
	if (m_NonAssignables != null) 
		return m_NonAssignables.containsKey(p_Key);
	else return null;
	}

//---------------------------------------------------------------------------
/**
 * Use this method to check whether the entity is already persisted 
 * or not.
 * 
 * @return <b>true</b> if persistend
 */
//---------------------------------------------------------------------------

@Transient
public Boolean isPersistent() 
	{
	if (this.getId() != null)
		 return Boolean.TRUE;
	else return Boolean.FALSE;	
	}

//***************************************************************************
//* End of Class															*
//***************************************************************************
}

