package com.hammurapi.extract;

import java.util.Iterator;
import java.util.ServiceLoader;

import com.hammurapi.extract.scripting.ScriptingExtractorFactory;


/**
 * Extractor factory service. Creates scripted extractors and predicates.
 * The service is discoverable using the Java Service Loader framework.
 * @author Pavel Vlasov.
 *
 */
public interface ExtractorFactory {
	
	/**
	 * Binding for context object.
	 */
	String CONTEXT_BINDING = "eCtx";
	
	/**
	 * Binding for arguments arrray if parameter names are not provided.
	 */
	String ARGS_BINDING = "args";

	/**
	 * This instance uses the service loading framework to discover
	 * extractor factory providers. It iterates over the providers and
	 * invokes provider's createExtractor() methods until it gets not null
	 * result which is returned from the method.
	 * If there are no providers supporting given language, then null is returned.
	 */
	ExtractorFactory INSTANCE = new ExtractorFactory() {
		
		private ScriptingExtractorFactory sef = new ScriptingExtractorFactory();

		public <T, V, C> Extractor<T, V, C> createExtractor(String language, String code, String[] parameterNames, Class<T>[] parameterTypes, Class<V> valueType, Class<C> contextType, ClassLoader classLoader) {
			ServiceLoader<ExtractorFactory> sl = classLoader==null ? ServiceLoader.load(ExtractorFactory.class) : ServiceLoader.load(ExtractorFactory.class, classLoader);
			Iterator<ExtractorFactory> efi = sl.iterator();
			while (efi.hasNext()) {
				Extractor<T, V, C> ret = efi.next().createExtractor(language, code, parameterNames, parameterTypes, valueType, contextType, classLoader);
				if (ret!=null) {
					return ret;
				}
			}
			return sef.createExtractor(language, code, parameterNames, parameterTypes, valueType, contextType, classLoader);
		}
		
		public <T, C> Predicate<T, C> createPredicate(String language, String code, String[] parameterNames, Class<T>[] parameterTypes, Class<C> contextType, ClassLoader classLoader) {
			ServiceLoader<ExtractorFactory> sl = classLoader==null ? ServiceLoader.load(ExtractorFactory.class) : ServiceLoader.load(ExtractorFactory.class, classLoader);
			Iterator<ExtractorFactory> efi = sl.iterator();
			while (efi.hasNext()) {
				Predicate<T, C> ret = efi.next().createPredicate(language, code, parameterNames, parameterTypes, contextType, classLoader);
				if (ret!=null) {
					return ret;
				}
			}
			return sef.createPredicate(language, code, parameterNames, parameterTypes, contextType, classLoader);
		}
		
	};
	
	/**
	 * Creates extractor.
	 * @param <T> Source objects type.
	 * @param <V> Value type.
	 * @param language Extractor language.
	 * @param code Extractor code.
	 * @param parameterTypes Extractor parameter types.
	 * @param valueType Extractor value type. If this type is Boolean then the factory
	 * shall return Predicate.
	 * @param classLoader Class loader to use for script evaluation.
	 * @return Extractor or null if this factory doesn't support given language.
	 */
	<T, V, C> Extractor<T, V, C> createExtractor(String language, String code, String[] parameterNames, Class<T>[] parameterTypes, Class<V> valueType, Class<C> contextType, ClassLoader classLoader);

	/**
	 * Creates extractor.
	 * @param <T> Source objects type.
	 * @param language Extractor language.
	 * @param code Extractor code.
	 * @param parameterTypes Extractor parameter types.
	 * @param valueType Extractor value type. If this type is Boolean then the factory
	 * shall return Predicate.
	 * @param classLoader Class loader to use for script evaluation.
	 * @return Extractor or null if this factory doesn't support given language.
	 */
	<T, C> Predicate<T, C> createPredicate(String language, String code, String[] parameterNames, Class<T>[] parameterTypes, Class<C> contextType, ClassLoader classLoader);
}
