/******************************************************************
  NOTE: Source files best viewed with a tabstop of 4.
 
 Copyright 2000-2006 by BEA Systems Inc.
 
 THE SOURCE CODE HEREIN (THE "CODE") IS SUBJECT TO THE TERMS AND
 CONDITIONS OF THE LICENSE AGREEMENT ACCOMPANYING THE BEA SYSTEMS INC.
 KODO SOFTWARE (THE "LICENSE AGREEMENT"). WITHOUT SUPERSEDING OR
 LIMITING THE APPLICATION OF THE LICENSE AGREEMENT, THE CODE IS
 SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND, AND YOU ACKNOWLEDGE
 THAT (EXCEPT AS OTHERWISE PROHIBITED BY LAW) BEA SYSTEMS INC. SHALL
 HAVE NO LIABILITY WHATSOEVER IN RELATION TO ANY USE OF THE CODE.
 
 LICENSEE MAY NOT REDISTRIBUTE, TRANSFER, SUBLICENSE OR SELL THE CODE OR
 EXPLOIT THE CODE IN ANY OTHER MANNER THAN AS EXPRESSLY ALLOWED IN THE
 LICENSE AGREEMENT. THIS LICENSE DOES NOT ENTITLE LICENSEE TO RECEIVE
 FROM BEA SYSTEMS INC. DOCUMENTATION, TECHNICAL SUPPORT, TELEPHONE
 ASSISTANCE, OR ENHANCEMENTS OR UPDATES TO THE CODE.
 ******************************************************************/
 

package tutorial.persistence;


import java.lang.reflect.*;
import java.util.*;
import javax.persistence.*;


/**
 *	Provides some maintenance methods for adding, removing, and analyzing
 *	the animals in the data store.
 */
public class AnimalMaintenance
{
	/**
	 *	Return a list of animals that match the specified query filter.
	 *
	 *	@param	filter 		the JPQL filter to apply to the query
	 *	@param 	em			the EntityManager to obtain the query from
	 */
	public static List getAnimals (String filter, EntityManager em)
	{
		System.out.println
			("*** getAnimals method will be implemented in Chapter III ***");
		return Collections.EMPTY_LIST;
	}


	/**
	 *	Performs the actual JPA work of putting <code>object</code>
	 *	into the data store.
	 *
	 *	@param	object 	the object to persist in the data store
	 */
	public static void persistObject (EntityManager em, Object object)
	{
		System.out.println
			("*** persistObject method will be implemented in Chapter III ***");
	}


	/**
	 *	Performs the actual JPA work of removing <code>objects</code>
	 *	from the data store.
	 *
	 *	@param	objects 	the objects to persist in the data store
	 *	@param 	em			the EntityManager to delete with
	 */
	public static void deleteObjects (Collection objects, EntityManager em)
	{
		System.out.println
			("*** deleteObject method will be implemented in Chapter III ***");
	}


	///////////////////////////////////////////////////////////////
	// Methods below here are provided by BEA, and do not 
	// need to be altered. Feel free to examine the code below, 
	// but for the purposes of this tutorial, there is no need to
	// read and understand everything that they do.				
	///////////////////////////////////////////////////////////////


	/**
	 *	Uses reflection to add the animal specified by <code>cls</code>,
	 *	<code>name</code>, and <code>arg</code>. Assumes that there is a 
	 *	two-arg constructor whose first argument is a string name, and whose
	 *	second argument is some sort of primitive or string.
	 *
	 *	@param	em		the EntityManager to use
	 *	@param	cls		the type of animal to add; one of Dog, Rabbit, or Snake
	 *	@param	name 	the name of the animal to add
	 *	@param	arg		the numeric arg to be passed to the constructor.
	 *					this will be translated into the appropriate type
	 *					for the class specified, as determined by reflection
	 *					on the constructors
	 */
	public static void add (EntityManager em, Class cls,
		String name, String arg)
		throws IllegalArgumentException
	{
		try
		{
    		Constructor[] constructors = cls.getDeclaredConstructors ();
    		for (int i = 0; i < constructors.length; i++)
    		{
				// Find an appropriate constructor.
    			Class[] paramTypes = constructors[i].getParameterTypes ();
    			if (paramTypes.length == 2 && paramTypes[0] == String.class)
    			{
    				Object[] params = new Object[] { 
    					name, getPrimitiveWrapper (arg, paramTypes[1]),
    				};
    
    				Object newObject = constructors[i].newInstance (params);
					persistObject (em, newObject);
    			}
    		}
		}
		catch (Exception e)
		{
			System.out.println ("Exception creating " + cls + ": " + e);
		}
	}


	/**
	 *	Attempt to convert <code>arg</code> to a primite object wrapper
	 *	of class <code>cls</code>.
	 */
	private static Object getPrimitiveWrapper (String arg, Class cls)
	{
		if (cls.equals (Boolean.class) || cls.equals (Boolean.TYPE))
			return Boolean.valueOf (arg);
		if (cls.equals (Byte.class) || cls.equals (Byte.TYPE))
			return Byte.valueOf (arg);
		if (cls.equals (Character.class) || cls.equals (Character.TYPE))
			return new Character (arg.charAt (0));
		if (cls.equals (Short.class) || cls.equals (Short.TYPE))
			return Short.valueOf (arg);
		if (cls.equals (Integer.class) || cls.equals (Integer.TYPE))
			return Integer.valueOf (arg);
		if (cls.equals (Long.class) || cls.equals (Long.TYPE))
			return Long.valueOf (arg);
		if (cls.equals (Float.class) || cls.equals (Float.TYPE))
			return Float.valueOf (arg);
		if (cls.equals (Double.class) || cls.equals (Double.TYPE))
			return Double.valueOf (arg);
		return null;
	}


	/**
	 *	Removes all animals that match <code>query</code>.
	 *
	 *	@param 	em		the EntityManager to use
	 *	@param 	query	the filter to use when finding animals to remove
	 */
	public static void remove (EntityManager em, String query)
	{
		// don't allow removing all animals
		if (query.trim ().length () == 0)
		{
			System.out.println ("Error: cannot remove all animals. Please "
				+ "provide a query.");
			return;
		}

		List animals = getAnimals (query, em);
		if (animals.isEmpty ())
		{
			System.out.println ("No animal matching '" + query
				+ "' found in entity manager.");
		}
		else
			deleteObjects (animals, em);
	}


	/**
	 *	Prints out a description of all the animals that match
	 *	<code>query</code>.
	 *
	 *	@param 	em			the EntityManager to use
	 *	@param 	query		the filter to use when finding animals to list
	 *	@param	detailed	if <code>true</code>, display details about
	 *						each animal identified by <code>query</code>;
	 *						else, print out terse info only
	 */
	public static void list (EntityManager em, String query, boolean detailed)
	{
		List<Animal> animals = getAnimals (query, em);

		if (animals.size () == 0)
		{
			System.out.println ("No animal matching '" + query
				+ "' found in entity manager.");
		}
		else
		{
			for (Animal animal : animals)
				System.out.println (animal.toString (detailed));
		}
	}


   	public static void main (String[] args)
		throws Exception
	{
		EntityManagerFactory emf = Persistence.
			createEntityManagerFactory (null);
		EntityManager em = emf.createEntityManager ();

		try
		{
    		if (args.length == 2 && args[0].equals ("list"))
    		{
    			String query;
				if (args[1].startsWith ("select ")
					|| args[1].startsWith ("SELECT "))
					query = args[1];
				else
					query = "select a from " + args[1] + " a";
    			list (em, query, false);
    			return;
    		}
    		if (args.length == 2 && args[0].equals ("details"))
    		{
    			list (em, args[1], true);
    			return;
    		}
    		if (args.length == 2 && args[0].equals ("remove"))
    		{
    			remove (em, args[1]);
    			return;
    		}
    		if (args.length == 4 && args[0].equals ("add"))
    		{
    			add (em, getAnimalSubclass (args[1]), args[2], args[3]);
    			return;
    		}
		}
		catch (ClassNotFoundException cnfe)
		{
			System.out.println ("Error: tutorial.persistence." + args[1] 
				+ " does not exist. Ensure that this class is compiled,"
				+ " and retry.");
		}
		finally
		{
			if (em.getTransaction ().isActive ())
				em.getTransaction ().rollback ();
			em.close ();
			emf.close ();
		}
		
		// If we get here, something went wrong.
		System.out.println ("Usage:");

		String cname = AnimalMaintenance.class.getName ();
		System.out.println ("	java " + cname + " list <'class'|'query'>");
		System.out.println ("	java " + cname + " details 'query'");
		System.out.println ("	java " + cname + " remove 'query'");
		System.out.println ("	java " + cname + " add Dog"
			+ " 'name' 'price'");
		System.out.println ("	java " + cname + " add Snake"
			+ " 'name' 'length'");
		System.out.println ("	java " + cname + " add Rabbit"
			+ " 'name' 'isFemale'");
		System.out.println ();
		System.out.println ("	'query' is a JPQL string identifying "
			+ "the animals to act upon");
		System.out.println ("	'class' is the Animal subclass to query on.");
	}


	/**
	 * Looks up the class specified by <code>clsName</code>. This class must 
	 * be one of Dog, Rabbit, Snake, and Animal.
	 */
	private static Class getAnimalSubclass (String clsName)
		throws ClassNotFoundException
	{
		if (!clsName.startsWith ("tutorial.persistence."))
			clsName = "tutorial.persistence." + clsName;

		if (!clsName.equals ("tutorial.persistence.Dog") 
			&& !clsName.equals ("tutorial.persistence.Rabbit") 
			&& !clsName.equals ("tutorial.persistence.Snake") 
			&& !clsName.equals ("tutorial.persistence.Animal"))
		{
			throw new IllegalArgumentException 
				("Error: " + clsName + " is not a legal type of animal. "
				 + "Animal types are limited to \"Dog\", \"Rabbit\", "
				 + "\"Animal\", and \"Snake\".");
		}

		return Class.forName (clsName);
	}
}
