package de.cegedim.iss.ojb.broker;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache ObjectRelationalBridge" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.metadata.PersistentField;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;

/**
 * This is a pathed version of {@link org.apache.ojb.broker.metadata.PersistentFieldPropertyImpl}:
 * <ul>
 * <li>allows to set <code>null</code> value on properties</li>
 * <li>finds properties with names like "qType" (qType->getQType()->Introspector derives QType)</li>
 * <li>imroved logging</li>
 * </ul>
 * 
 * @author merker
 */
public class PersistentFieldPropertyImpl implements PersistentField
{

	private java.lang.Class declaringClass;
	private String propertyName;
	private transient java.beans.PropertyDescriptor propertyDescriptor;

	public PersistentFieldPropertyImpl(Class aClass, String aPropertyName)
	{
		setDeclaringClass(aClass);
		setPropertyName(aPropertyName);
		setPropertyDescriptor(findPropertyDescriptor(aClass, aPropertyName));
	}

	/**
	 * Sets aValue for anObject
	 */
	public void set(Object anObject, Object aValue) throws PersistenceBrokerException
	{
		Method m = getPropertyDescriptor().getWriteMethod();
		Object[] args = { aValue };
		if (m != null)
		{
			try
			{
				m.invoke(anObject, args);
			}
			catch (Throwable t)
			{
				String msg = t.getClass().getName();
				logProblem(anObject, aValue, msg);
				throw new PersistenceBrokerException(t);
			}
		}
		else
		{
			String msg = "getWriteMethod returned null";
			logProblem(anObject, aValue, msg);
			throw new PersistenceBrokerException(msg);
		}
	}

	/**
	 * Let's give the user some hints as to what could be wrong.
	 */
	protected void logProblem(Object anObject, Object aValue, String msg)
	{
		Logger logger = LoggerFactory.getDefaultLogger();
		logger.error("Error in operation [set] of object [PersistentFieldPropertyImpl], " + msg);
		logger.error("Declaring class [" + getDeclaringClass().getName() + "]");
		logger.error("Property Name [" + getPropertyName() + "]");
		logger.error("Property Type [" + getPropertyDescriptor().getPropertyType().getName() + "]");

		if (anObject != null)
		{
			logger.error("anObject was class [" + anObject.getClass().getName() + "]");
		}
		else
		{
			logger.error("anObject was null");
		}
		if (aValue != null)
		{
			logger.error("aValue was class [" + aValue.getClass().getName() + "]");
		}
		else
		{
			logger.error("aValue was null");
		}
	}

    /**
     * Get the Value from anObject
     */
    public Object get(Object anObject)
        throws PersistenceBrokerException
    {
        Method m = getPropertyDescriptor().getReadMethod();
        Object[] args = null;
        try
        {
            if (m != null)
            {
                Object result = m.invoke(anObject, args);
                return result;
            }
            else
            {
                /**
                 * Let's give the user some hints as to what could be wrong.
                 */
                LoggerFactory.getDefaultLogger().error("Error in operation [get] of object [PersistentFieldPropertyImpl], getReadMethod returned null");
                LoggerFactory.getDefaultLogger().error("Declaring class ["+getDeclaringClass().getName()+"]");
                LoggerFactory.getDefaultLogger().error("Property Name ["+getPropertyName()+"]");
                LoggerFactory.getDefaultLogger().error("Property Type ["+getPropertyDescriptor().getPropertyType().getName()+"]");
                if (anObject != null)
                    LoggerFactory.getDefaultLogger().error("anObject was class ["+anObject.getClass().getName()+"]");
                else
                    LoggerFactory.getDefaultLogger().error("anObject was null");
                // dunno if i should return null here or not.
	            throw new MetadataException("Error getting field:"+getPropertyName()+" in object:"+getDeclaringClass().getName());
            }
        }
        catch (Exception ex)
        {
            /**
             * Let's give the user some hints as to what could be wrong.
             */
            LoggerFactory.getDefaultLogger().error("Error in operation [get] of object [PersistentFieldPropertyImpl]", ex);
            LoggerFactory.getDefaultLogger().error("Declaring class ["+getDeclaringClass().getName()+"]");
            LoggerFactory.getDefaultLogger().error("Property Name ["+getPropertyName()+"]");
            LoggerFactory.getDefaultLogger().error("Property Type ["+getPropertyDescriptor().getPropertyType().getName()+"]");
            if (anObject != null)
                LoggerFactory.getDefaultLogger().error("anObject was class ["+anObject.getClass().getName()+"]");
            else
                LoggerFactory.getDefaultLogger().error("anObject was null");
            LoggerFactory.getDefaultLogger().error("getting " + declaringClass.getName() + "." + propertyName, ex);

            throw new MetadataException("Error getting field:"+getPropertyName()+" in object:"+getDeclaringClass().getName(), ex);
        }
    }

	public Class getType()
	{
		return getPropertyDescriptor().getPropertyType();
	}

	public String getName()
	{
		return getPropertyDescriptor().getName();
	}

	/**
	 * Get the PropertyDescriptor for aClass and aPropertyName
	 */
	protected PropertyDescriptor findPropertyDescriptor(Class aClass, String aPropertyName)
	{
		BeanInfo info;
		PropertyDescriptor[] pd;
		PropertyDescriptor descriptor = null;

		try
		{
			info = Introspector.getBeanInfo(aClass);
			pd = info.getPropertyDescriptors();
			for (int i = 0; i < pd.length; i++)
			{
				if (pd[i].getName().equals(aPropertyName))
				{
					descriptor = pd[i];
					break;
				}
			}
            // Mer: search again ignoring the case
            // this is a workaround for properties like qType->getQType()->Introspector derives QType
            if (descriptor == null) {
	            for (int i = 0; i < pd.length; i++)
	            {
	                if (pd[i].getName().compareToIgnoreCase(aPropertyName) == 0)
	                {
	                    descriptor = pd[i];
	                    break;
	                }
	            }
            }
			if (descriptor == null)
			{
				/*
				 * Daren Drummond: 	Throw here so we are consistent
				 * 					with PersistentFieldDefaultImpl.
				 */
				throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName());
			}
			return descriptor;
		}
		catch (IntrospectionException ex)
		{
			/*
			 * Daren Drummond: 	Throw here so we are consistent
			 * 					with PersistentFieldDefaultImpl.
			 */
			throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName(), ex);
		}

	}

	/**
	 * Insert the method's description here.
	 * Creation date: (29.11.2001 09:49:21)
	 * @return java.lang.Class
	 */
	public java.lang.Class getDeclaringClass()
	{
		return declaringClass;
	}

	/**
	 * Insert the method's description here.
	 * Creation date: (29.11.2001 09:19:17)
	 * @return java.beans.PropertyDescriptor
	 */
	protected java.beans.PropertyDescriptor getPropertyDescriptor()
	{
		if (propertyDescriptor == null)
		{
			setPropertyDescriptor(findPropertyDescriptor(getDeclaringClass(), getPropertyName()));
		}
		return propertyDescriptor;
	}

	/**
	 * Insert the method's description here.
	 * Creation date: (29.11.2001 09:49:21)
	 * @param newDeclaringClass java.lang.Class
	 */
	public void setDeclaringClass(java.lang.Class newDeclaringClass)
	{
		declaringClass = newDeclaringClass;
	}

	/**
	 * Insert the method's description here.
	 * Creation date: (29.11.2001 09:19:17)
	 * @param newPropertyDescriptor java.beans.PropertyDescriptor
	 */
	protected void setPropertyDescriptor(java.beans.PropertyDescriptor newPropertyDescriptor)
	{
		propertyDescriptor = newPropertyDescriptor;
	}

	/**
	 * Gets the propertyName.
	 * @return Returns a String
	 */
	public String getPropertyName()
	{
		return propertyName;
	}

	/**
	 * Sets the propertyName.
	 * @param propertyName The propertyName to set
	 */
	public void setPropertyName(String propertyName)
	{
		this.propertyName = propertyName;
	}

	public String toString()
	{
		return getDeclaringClass().getName() + "." + getPropertyName();
	}

}

