/*******************************************************************************
 * 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.utils.entitymapper;

import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.core.ejb.session.beans.EntityMapperBean;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.EntityMapperInterface;
import lu.tudor.santec.gecamed.core.utils.Logger;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.querybuilder.WhereClause;
import lu.tudor.santec.gecamed.importexport.utils.XPathAPI;

import org.apache.log4j.Level;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class EntityMapper
	{
	protected Document		m_XMLDocument;
	protected Node			m_CurrentNode;
	
	protected boolean		m_CheckXPath;
	
	private Stack <Node>	m_PreviousNodes;
	
	private Logger 		m_Logger = null;
	
	private EntityMapperInterface	m_EntityMapperInterface;
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

	protected static final String c_InsRepeating	= "repeating";
	protected static final String c_InsIndex		= "index";
	protected static final String c_InsMapping		= "mapping";
	protected static final String c_InsFormat		= "format";
	protected static final String c_InsKey			= "key";
	
	protected static final int	c_None			= -1;
	protected static final int	c_Repeating		= 0;
	protected static final int	c_Index			= 1;
	protected static final int	c_Mapping		= 2;
	protected static final int	c_Format		= 3;
	protected static final int	c_Key			= 4;
	
    private static final Hashtable <String,Integer> m_Instructions = new Hashtable <String,Integer> ();
    
    static 	{
    		m_Instructions.put( EntityMapper.c_InsRepeating , 	EntityMapper.c_Repeating);
    		m_Instructions.put( EntityMapper.c_InsIndex,	    EntityMapper.c_Index);
    		m_Instructions.put( EntityMapper.c_InsMapping,	    EntityMapper.c_Mapping);
    		m_Instructions.put( EntityMapper.c_InsFormat,	    EntityMapper.c_Format);
    		m_Instructions.put( EntityMapper.c_InsKey,	        EntityMapper.c_Key);
     		}
	
	protected static final String c_TypDate = "date";
	
	private static Pattern 
    
    c_InstructionPattern = Pattern.compile ("^\\s*?@(\\w*?)\\s*?$",Pattern.CASE_INSENSITIVE);

	protected static Pattern 
    
    c_ComposedPattern = Pattern.compile ("\\{\\s?(\\w*)\\b(.*?)\\}",Pattern.CASE_INSENSITIVE);
	
	protected static Pattern 
    
    c_CollectionPattern = Pattern.compile ("\\[\\s?([a-z0-9\\.]*)\\b(.*?)\\]",Pattern.CASE_INSENSITIVE);

	protected static Pattern 
    
    c_SinglePattern = Pattern.compile ("<\\s?([a-z0-9\\.]*)\\b(.*?)>",Pattern.CASE_INSENSITIVE);
	
	protected static Pattern 
    
    c_ReferencePattern = Pattern.compile (">\\s?([a-z0-9\\.]*)\\b(.*?)<",Pattern.CASE_INSENSITIVE);

	protected static Pattern
	
	c_ExpressionPattern = Pattern.compile ("(.*?)\\s*?=\\s*?(.*)",Pattern.CASE_INSENSITIVE);
	
	protected static Pattern
	
	c_XPathPattern = Pattern.compile ("^(.*?)(\\[?@(.*?)\\]?)?$",Pattern.CASE_INSENSITIVE);
	
	protected static Pattern
	
	c_PredicatePattern = Pattern.compile ("(.*?)\\s*?=\\s*?\"(.*?)\"",Pattern.CASE_INSENSITIVE);
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

public EntityMapper (Document p_XMLDocument, boolean p_CheckXPath)
	{
	m_XMLDocument = p_XMLDocument;
	m_CheckXPath = p_CheckXPath;
	m_CurrentNode = null;
	m_PreviousNodes = new Stack <Node> ();
	m_Logger = new Logger (this.getClass());
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The private getEntityMapperInterface returns an instance of the EntityMapperBean
 * session bean. On the first call, the EntityMapperBean will actualy be looked up
 * via JNDI. Once it has been found, the reference to the bean will be stored
 * in a private data member. Doing so avoids JNDI lookups on later calls.
 * @return an instance of the EntityMapperBean session bean.
 */
//---------------------------------------------------------------------------

protected EntityMapperInterface getEntityMapperInterface ()
	{
	if (m_EntityMapperInterface != null) return m_EntityMapperInterface;

	try {
		m_EntityMapperInterface = (EntityMapperInterface) ManagerFactory.getRemote (EntityMapperBean.class);
		} 
	catch (Exception p_Exception) 
		{
		m_Logger.log (Level.FATAL, "Failed to lookup EntityMapperBean!",p_Exception);
		}

	return m_EntityMapperInterface;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class 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.
 */
//---------------------------------------------------------------------------

protected void log (Level p_Level, String p_Message)
	{
	m_Logger.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.
 */
//---------------------------------------------------------------------------

protected void log (Level p_Level, String p_Message, Exception p_Exception)
	{
	m_Logger.log (p_Level, p_Message, p_Exception);
	}

//---------------------------------------------------------------------------
/**
 * 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;
	}

//---------------------------------------------------------------------------
/**
 * Loads the specified mapping file and returns its content.
 * @param p_MappingFile specifies the file name of the mapping file to load
 * @return the mapping data that was read from the specified mapping file
 * or <code>null</code> if an error occured!
 */
//---------------------------------------------------------------------------

protected Properties loadMappingFile (String p_MappingFile)
	{
	Properties l_Properties;	
		
	l_Properties = new Properties ();
    	
    try {
    	l_Properties.load (this.getClass().getResourceAsStream(p_MappingFile));
		}
    catch(FileNotFoundException p_Exception)
		{
		l_Properties = null;
		m_Logger.log(Level.ERROR, "Couldn't find mapping file " + p_MappingFile, p_Exception);
		}
	catch(Exception p_Exception)
		{
		l_Properties = null;
		m_Logger.log(Level.ERROR, "Error while loading mapping file " + p_MappingFile, p_Exception);
		}
	return l_Properties;
	}

//---------------------------------------------------------------------------
/**
 * If the specified key embodies a known instruction, the getInstruction
 * method will return the corresponding instruction code.
 * @param p_Key specifies the key we'd like to get the instruction code of.
 * @return the instruction code for the specified key if key embodies a
 * known instruction, <code>c_None</code> otherwise.
 */
//---------------------------------------------------------------------------

protected int	getInstruction (String p_Key)
	{
	Matcher	l_InstructionMatcher;
	int		l_Instruction = c_None;
	
	if (p_Key == null) return l_Instruction;
	
	l_InstructionMatcher = c_InstructionPattern.matcher(p_Key);
	if (   l_InstructionMatcher.matches()
		&& m_Instructions.containsKey(l_InstructionMatcher.group(1)))
		{
		l_Instruction = m_Instructions.get(l_InstructionMatcher.group(1));
		}
	
	return l_Instruction;	
	}

//---------------------------------------------------------------------------
/**
 * Sets the current base path to the specified node. Before doing so, the
 * method safeguards the previous value of current node.
 * @param p_NewNode specifies the new node to become current node.
 * @return new current node
 */
//---------------------------------------------------------------------------

protected Node setCurrentNode (Node p_NewNode)
	{
	if (m_CurrentNode != null) m_PreviousNodes.push (m_CurrentNode);
	m_CurrentNode = p_NewNode;	
	return m_CurrentNode;	
	}

//---------------------------------------------------------------------------
/**
 * Retrieves the previously saved value of the current node and restores it.
 * @return previous value of (now again) current node
 */
//---------------------------------------------------------------------------

protected Node resetCurrentNode ()
	{
	if (!m_PreviousNodes.isEmpty())
		 m_CurrentNode = m_PreviousNodes.pop();
	else m_CurrentNode = null;	
		
	return m_CurrentNode;
	}

//---------------------------------------------------------------------------
/**
 * Returns the value of the current node. If current is not set yet, the method
 * will return the document as current node.
 * @return current node if set, document root otherwise.
 */
//---------------------------------------------------------------------------

protected Node getCurrentNode ()
	{
	return (m_CurrentNode != null)?m_CurrentNode:m_XMLDocument;	
	}

//---------------------------------------------------------------------------
/**
 * creates a new DOM node identified by the specified XPath. An optional 
 * reference node may be specified. If none is provided, the method will 
 * use the document root as reference node.
 * @param p_Node specifies an optional reference node. Specifying <code>null</code>
 * tells the method to use the document root node as reference node.
 * @param p_XPath specifies the path to the new DOM node to be created. Path
 * is to be expressed relative to reference node.
 * @return a newly created DOM node specfied by p_XPath if it did not
 * exist already, otherwise the already existing will be returned.
 */
//---------------------------------------------------------------------------

protected Node createNodeByXPath (Node p_Node, String p_XPath)
	{
	String[]		l_Levels;
	int				l_Level		  = 0;	
	Node			l_Node	      = null;
	Node			l_CurrentNode = null;
	
	// Split specified path into its constituting sub-nodes
	
	l_Levels = p_XPath.split("/");
	if (l_Levels.length == 0) return null;
	
	// In case no reference node was specified, use document root node as
	// reference.
	
	l_CurrentNode = (p_Node != null)?p_Node:m_XMLDocument;
		
	// Walk down specified path. As long as node exists at current level,
	// do nothing, otherwise create a new node.
	
	do	{		
		try	{
			l_Node = XPathAPI.selectSingleNode(l_CurrentNode, l_Levels[l_Level], m_CheckXPath);
			if ((l_Node != null) && (l_Level < l_Levels.length - 1))	
				{
				l_CurrentNode = l_Node;
				}
			else
				{
				if (!l_Levels[l_Level].equals("."))
					{
					l_Node = m_XMLDocument.createElement(l_Levels[l_Level]);
					l_CurrentNode.appendChild(l_Node);	
					l_CurrentNode = l_Node;	
					}
				else l_Node = l_CurrentNode;
				}
			}
		catch (Exception p_Exception)
			{
			this.log (Level.ERROR, "Failed to create node for XPath :" + p_XPath, p_Exception);
			}		
		l_Level++;
		}	 
	while (l_Level < l_Levels.length);

	return l_Node;
	}

//---------------------------------------------------------------------------
/**
 * The getArguments method splits the specified arguments string
 * and returns the encountered arguments left and right hand side in a 
 * hashtable, such as that the left hand side of the argument is the key
 * and the right hand side the value. It should be noted that the method
 * DOES NOT evaluate encountered values.
 * @param p_Arguments specifies the argument string to split.
 * @return A Hashtable holding the arguments. 
 */
//---------------------------------------------------------------------------

protected Hashtable <String,String> getArguments (String p_Arguments)
	{
	Hashtable <String,String> l_Arguments;
	String[]	   			  l_Expressions;
	String					  l_Expression;
	String					  l_Value;
	int						  l_Index;
	Matcher					  l_ExpressionMatcher;
	
	l_Arguments = new Hashtable <String,String> ();
	l_Expressions = p_Arguments.split(",");
	for (l_Index = 0; l_Index < l_Expressions.length; l_Index++)
		{
		l_Expression = l_Expressions[l_Index].trim();
		l_Value      = null;
		
		l_ExpressionMatcher = c_ExpressionPattern.matcher(l_Expression);
		if (l_ExpressionMatcher.matches())
			{
			l_Value = l_ExpressionMatcher.group(2);
			}
		
		if (l_Value != null) l_Arguments.put(l_ExpressionMatcher.group(1).toLowerCase(),l_Value);
		}	
	return l_Arguments;
	}

//---------------------------------------------------------------------------
/**
 * Checks whether the specified node has the specified attribute and if yes,
 * returns its value. 
 * @param p_Node specifies the node to get attribute from.
 * @param p_Attribute specifies the attribute to retrieve eventualy.
 * @return the value of the specified attribute if it exists, empty string
 * otherwise.
 */
//---------------------------------------------------------------------------

protected String getAttribute (Node p_Node, String p_Attribute)
	{
	Node			l_Node 		 	= null;
	NamedNodeMap	l_Attributes 	= null;
	String  		l_Value  		= "";

	if ((p_Node == null) || (p_Attribute == null)) return "";
	
	if (p_Node.hasAttributes())
		{
		l_Attributes = p_Node.getAttributes();
		if (l_Attributes != null) 
			{
			l_Node = l_Attributes.getNamedItem (p_Attribute);
			if (l_Node != null) l_Value = l_Node.getNodeValue();
			}
		}
	return l_Value;
	}

//---------------------------------------------------------------------------
/**
 * Sets the value of the specified attribute for the specified node.
 * @param p_Node specifies the node to set attribute of.
 * @param p_Attribute specifies the attribute to set.
 * @param p_Value specifies the value to set the attribute to.
 * @return The newly created attribute noed.
 */
//---------------------------------------------------------------------------

protected Node setAttribute (Node p_Node, String p_Attribute, String p_Value)
	{
	Node			l_Node 		 	= null;
	NamedNodeMap	l_Attributes 	= null;

	if ((p_Node == null) || (p_Attribute == null)) return null;
			
	l_Attributes = p_Node.getAttributes();	
	if (l_Attributes != null) 
		{
		l_Node = m_XMLDocument.createAttribute (p_Attribute);
		l_Node.setNodeValue(p_Value);
		l_Attributes.setNamedItem(l_Node);	
		}
		
	return l_Node;
	}

//---------------------------------------------------------------------------
/**
 * The getValueByXPath method returns the value of the DOM node identified
 * by the specified XPath. The specified path must be expressed relative to
 * the specified reference node p_Node. An optional attribute allows to 
 * specify the attribute to get the value of instead of the node's value.
 * @param p_Node specifies the reference node that the specified xpath is expressed
 * against.
 * @param p_XPath specifies the path to the target DOM node of interest.
 * @param p_Attribute specifies the (optional) name of one of the target node's 
 * attribute to get the value of.
 * @return the value of the specified target node if p_Attribute is set
 * to <code>null</code>, otherwise the value of the specified attribute
 * of the target node.
 */
//---------------------------------------------------------------------------

protected String getValueByXPath (Node p_Node, String p_XPath, String p_Attribute)
	{
	String			l_Name				= null;
	String  		l_Value  			= null;
	NodeList		l_NodeList 			= null;
	Node			l_Node 				= null;
	Matcher		 	l_ExpressionMatcher;
	boolean			l_Found				= false;
	
	if (p_Node == null) return l_Value;
	
	p_Node.normalize();
	l_Value = p_Node.getPrefix();
		
	try	{
		l_NodeList	= XPathAPI.selectNodeIterator(p_Node, p_XPath, m_CheckXPath);
		if (l_NodeList != null && l_NodeList.getLength() > 0)
			{
			l_Node = l_NodeList.item(0);
			if (l_Node != null)
				{
				if (p_Attribute == null) 
					{
					l_Value = l_Node.getTextContent();
					}
				else 
					{
					l_ExpressionMatcher = c_PredicatePattern.matcher(p_Attribute);
					if (l_ExpressionMatcher.matches())
						{
						l_Name  = l_ExpressionMatcher.group(1);
						l_Value = l_ExpressionMatcher.group(2);
						
						while (!l_Found && l_Node != null)
							{
							l_Found = l_Value.equals(this.getAttribute (l_Node, l_Name));
							if (!l_Found) l_Node = l_Node.getNextSibling();		
							}
						
						if (l_Found) 
							 l_Value = l_Node.getTextContent();
						else l_Value = "";
						}
					else l_Value = this.getAttribute(l_Node, p_Attribute);
					}
				}
			}
		}
	catch (Exception p_Exception)
		{
		l_Value = p_XPath + ((p_Attribute!=null)?"@" + p_Attribute:"");
		this.log (Level.ERROR, "Error while retrieving XPath :" + l_Value, p_Exception);
		}
	
	return l_Value;
	}

//---------------------------------------------------------------------------
/**
 * The setValueByXPath method sets the value of the DOM identified by the
 * specified path. The specified path must be expressed relative to
 * the specified reference node p_Node. An optional attribute allows to 
 * specify the attribute to set the value of instead of the node's value.
 * @param p_Node specifies the reference node that the specified xpath is expressed
 * against.
 * @param p_XPath specifies the path to the target DOM node of interest.
 * @param p_Attribute specifies the (optional) name of one of the target node's 
 * attribute to set the value of.
 * @param p_Value specifies the value to be set.
 */
//---------------------------------------------------------------------------

protected void setValueByXPath (Node p_Node, String p_XPath, String p_Attribute, String p_Value)
	{
	String			l_Name				= null;
	String  		l_Value  			= null;
	String			l_Path;
	Node			l_Node;
	Node			l_CurrentNode;
	Matcher		 	l_ExpressionMatcher;
	
	if ((p_Node == null) || (p_Value == null) || (p_Value.length() == 0)) return;
	
	p_Node.normalize();
		
	try	{
		l_CurrentNode = this.createNodeByXPath (p_Node, p_XPath);
				
		if (p_Attribute == null)
			{
			l_CurrentNode.setTextContent (p_Value);
			if (l_CurrentNode != null) l_Node = l_CurrentNode;
			}
		else 
			{
			l_ExpressionMatcher = c_PredicatePattern.matcher(p_Attribute);
			if (l_ExpressionMatcher.matches())
				{
				l_Name  = l_ExpressionMatcher.group(1);
				l_Value = l_ExpressionMatcher.group(2);
						
				l_Node = this.setAttribute (l_CurrentNode, l_Name, l_Value);		
				if (l_Node != null) l_CurrentNode.setTextContent (p_Value);
				}
			else this.setAttribute (l_CurrentNode, p_Attribute, p_Value);	
			}
		}
	catch (Exception p_Exception)
		{
		l_Path = p_XPath + ((p_Attribute!=null)?"@" + p_Attribute:"");
		p_Value = (p_Value != null)?p_Value:"null";
		
		this.log (Level.ERROR, "Error while setting value " + p_Value + " to XPath :" + l_Path, p_Exception);
		}		
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified property of the specified GECAMedEntityBean to the
 * specified value. In case the specified bean does not have the specified
 * property, the property and its value will be stored in the beans
 * non assignable store.
 * @param p_Bean specifies the GECAMedEntityBean to set the property of.
 * @param p_Property specifies the property to be set.
 * @param p_Value specifies the value to be given to the specified property.
 * @return the modified GECAMedEntityBean.
 * @see GECAMedEntityBean
 */
//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------
/**
 * Returns the value of the specified property for the specified GECAMedEntityBean.
 * @param p_Bean specifies the bean to get the value of the property of.
 * @param p_Property specifies the property to get the value of.
 * @return the value of the specified property from the specified GECAMedEntityBean,
 * <code>null</code> if an error occured.
 */
//---------------------------------------------------------------------------

protected Object getProperty (GECAMedEntityBean p_Bean, String p_Property)
	{
	Class	l_Type;
	Object	l_Value = null;
	
	try	{
		l_Type = p_Bean.getPropertyType (p_Property);
		if (l_Type != null)
			 l_Value = p_Bean.getProperty (p_Property);
		else l_Value = p_Bean.getNonAssignable(p_Property);
		}
	
	catch(Exception p_Exception)
		{
		this.log(Level.ERROR, "Error while getting " + p_Property + " from " + p_Bean.getClass().getName(), p_Exception);
		}
	
	return l_Value;		
	}

//---------------------------------------------------------------------------
/**
 * Looks in the database for a persisted GECAMedEntityBean of the specified class 
 * using search criteria specified in the where clause.
 * @param p_ClassName specifies the class of the GECAMedEntityBean to get.
 * @param p_Clause specifies search criteria to get desired GECAMedEntityBean.
 * @return A GECAMedEntityBean of the desired class matching the search
 * criteria specified by where clause if such a bean exists, <code>null</code>
 * otherwise. 
 */
//---------------------------------------------------------------------------

protected GECAMedEntityBean getReference (String p_ClassName, WhereClause p_Clause)
	{
	EntityMapperInterface	l_EntityMapperInterface;
	GECAMedEntityBean		l_Reference = null;
	
	l_EntityMapperInterface = this.getEntityMapperInterface();
	if (l_EntityMapperInterface == null) return null;
	
	try	{
		l_Reference = l_EntityMapperInterface.getReference(p_ClassName, p_Clause);
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Error while fetching Reference of type " + p_ClassName + " with where clause " + p_Clause.toString(), p_Exception);
		}
	
	return l_Reference;
	}

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

public void setXMLDocument (Document p_XMLDocument)
	{
	m_XMLDocument = p_XMLDocument;	
	}

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

public Document getXMLDocument ()
	{
	return m_XMLDocument;	
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class                                                            *
//***************************************************************************
//---------------------------------------------------------------------------

	}
