/*
 *	Service.java
 *
 *	This file is part of Tritonus: http://www.tritonus.org/
 */

/*
 *  Copyright (c) 2000 by Matthias Pfisterer
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
|<---            this code is formatted to fit into 80 columns             --->|
*/

package org.tritonus.core;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.URL;

import org.tritonus.share.TDebug;
import org.tritonus.share.ArraySet;



public class Service
{
	private static final String	BASE_NAME = "META-INF/services/";


	/**	Determines if the order of service providers is reversed.
		If this is true, the Iterator returned by providers(Class)
		iterates through the service provider classes backwards.
		This means that service providers that are in the user class
		path are first, then service providers in the extension class
		path, then those in the boot class path.
		This behaviour has the advantage that 'built-in' providers
		(those in the boot class path) can be 'shadowed' by
		providers in the extension and user class path.
	 */
	private static final boolean	REVERSE_ORDER = true;



	public static Iterator providers(Class cls)
	{
		if (TDebug.TraceService) { TDebug.out("Service.providers(): begin"); }
		String	strFullName = BASE_NAME + cls.getName();
		if (TDebug.TraceService) { TDebug.out("Service.providers(): full name: " + strFullName); }
		List	instancesList = createInstancesList(strFullName);
		Iterator	iterator = instancesList.iterator();
		if (TDebug.TraceService) { TDebug.out("Service.providers(): end"); }
		return iterator;
	}



	private static List<Object> createInstancesList(String strFullName)
	{
		if (TDebug.TraceService) { TDebug.out("Service.createInstancesList(): begin"); }
		List<Object>	providers = new ArrayList<Object>();
		Iterator	classNames = createClassNames(strFullName);
		if (classNames != null)
		{
			while (classNames.hasNext())
			{
				String	strClassName = (String) classNames.next();
				if (TDebug.TraceService) { TDebug.out("Service.createInstancesList(): Class name: " + strClassName); }
				try
				{
					ClassLoader	systemClassLoader = ClassLoader.getSystemClassLoader();
					Class	cls = Class.forName(strClassName, true, systemClassLoader);
					if (TDebug.TraceService) { TDebug.out("Service.createInstancesList(): now creating instance of " + cls); }
					Object	instance = cls.newInstance();
					if (REVERSE_ORDER)
					{
						providers.add(0, instance);
					}
					else
					{
						providers.add(instance);
					}
				}
				catch (ClassNotFoundException e)
				{
					if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
				}
				catch (InstantiationException e)
				{
					if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
				}
				catch (IllegalAccessException e)
				{
					if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
				}
				catch (Throwable e)
				{
					if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
				}
			}
		}
		if (TDebug.TraceService) { TDebug.out("Service.createInstancesList(): end"); }
		return providers;
	}



	private static Iterator<String> createClassNames(String strFullName)
	{
		if (TDebug.TraceService) TDebug.out("Service.createClassNames(): begin");
		Set<String>	providers = new ArraySet<String>();
		Enumeration	configs = null;
		try
		{
			configs = ClassLoader.getSystemResources(strFullName);
		}
		catch (IOException e)
		{
			if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
		}
		if (configs != null)
		{
			while (configs.hasMoreElements())
			{
				URL	configFileUrl = (URL) configs.nextElement();
				if (TDebug.TraceService) { TDebug.out("Service.createClassNames(): config: " + configFileUrl); }
				InputStream	input = null;
				try
				{
					input = configFileUrl.openStream();
				}
				catch (IOException e)
				{
					if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
				}
				if (input != null)
				{
					BufferedReader	reader = new BufferedReader(new InputStreamReader(input));
					try
					{
						String	strLine = reader.readLine();
						while (strLine != null)
						{
							strLine = strLine.trim();
							int	nPos = strLine.indexOf('#');
							if (nPos >= 0)
							{
								strLine = strLine.substring(0, nPos);
							}
							if (strLine.length() > 0)
							{
								providers.add(strLine);
								if (TDebug.TraceService) { TDebug.out("Service.createClassNames(): adding class name: " + strLine); }
							}
							strLine = reader.readLine();
						}
					}
					catch (IOException e)
					{
						if (TDebug.TraceService || TDebug.TraceAllExceptions) { TDebug.out(e); }
					}
				}
			}
		}
		Iterator<String> iterator = providers.iterator();
		if (TDebug.TraceService) TDebug.out("Service.createClassNames(): end");
		return iterator;
	}
}



/*** Service.java ***/
