/*
 * Created on Oct 2, 2004
 *
 */
package org.springframework.context.support;

import java.net.MalformedURLException;
import java.net.URL;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;

/**
 * Standalone XML application context, taking the context definition
 * files from the class path of the classloader of the class defined in 
 * the constructors. If the clazz parameter is null, the behavior of this 
 * should be the same as the ClassPathXmlApplicationContext
 *
 * @author Ole Petter Aasen
 * @see #getResource
 * @see #getResourceByPath
 */
public class AlternateClassPathXmlApplicationContext extends
		ClassPathXmlApplicationContext {

	private Class clazz;
	
	/**
	 * Create a new AlternateClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocation file path
	 */
	public AlternateClassPathXmlApplicationContext(String configLocation, 
			Class  clazz) throws BeansException {
		this(new String[] {configLocation}, clazz);
	}

	/**
	 * Create a new AlternateClassPathXmlApplicationContext, loading the definitions
	 * from the given XML files and automatically refreshing the context.
	 * @param configLocations array of file paths
	 */
	public AlternateClassPathXmlApplicationContext(String[] configLocations, 
			Class clazz) throws BeansException {
		this(configLocations, clazz, true);
	}

	/**
	 * Create a new AlternateClassPathXmlApplicationContext, loading the definitions
	 * from the given XML files.
	 * @param configLocations array of file paths
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @see #refresh
	 */
	public AlternateClassPathXmlApplicationContext(String[] configLocations, 
			Class clazz, boolean refresh) throws BeansException {
		super(configLocations, false);
		this.clazz = clazz;
		if (refresh){
			refresh();
		}
	}

	/**
	 * Create a new AlternateClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files and automatically
	 * refreshing the context.
	 * @param configLocations array of file paths
	 * @param parent the parent context
	 */
	public AlternateClassPathXmlApplicationContext(String[] configLocations, 
			Class clazz, ApplicationContext parent)
			throws BeansException {
		this(configLocations, clazz, true, parent);
	}

	/**
	 * Create a new AlternateClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of file paths
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @see #refresh
	 */
	public AlternateClassPathXmlApplicationContext(String[] configLocations, 
			Class clazz, boolean refresh, ApplicationContext parent)
			throws BeansException {
		super(configLocations,false, parent);
		this.clazz = clazz;
		if (refresh) {
			refresh();
		}
	}

	/**
	 * Initialize the bean definition reader used for loading the bean definitions 
	 * of this context. This implementation only sets the beanDefinitionReaders
	 * classloader.
	 * @param beanDefinitionReader the bean definition reader used by this context
	 * @see org.springframework.context.support.AbstractXmlApplicationContext
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
		if (clazz != null){
			beanDefinitionReader.setBeanClassLoader(clazz.getClassLoader());
		}
	}
	/**
	 * Overides the default implementation from DefaultResourceLoader to force
	 * the ClassPathResource to load the resource using a defined classloader if
	 * the class attribute of the class is set.
	 * @param location location of the resource.
	 * @return Resource handle
	 * @see org.springframework.core.io.DefaultResourceLoader
	 */
	public Resource getResource(String location) {
		if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			if (clazz != null)
				return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), clazz);
			else
				return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
		}
		else {
			try {
				// try URL
				URL url = new URL(location);
				return new UrlResource(url);
			}
			catch (MalformedURLException ex) {
				// no URL -> resolve resource path
				return getResourceByPath(location);
			}
		}
	}

	/**
	 * Overrides the default impementation in the DefaultResourceLoader
	 * Return a Resource handle for the resource at the given path loaded
	 * by the classloader of the class clazz if this is set.
	 * 
	 * @param path path to the resource
	 * @return Resource handle
	 * @see ClassPathResource
	 * @see org.springframework.core.io.DefaultResourceLoader
	 */
	protected Resource getResourceByPath(String path) {
		if (clazz != null) return new ClassPathResource(path, clazz);
		else return new ClassPathResource(path);
	}
	
}

