/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
 */

package fr.gouv.culture.sdx.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.Naming;
import java.util.Hashtable;
import java.util.Locale;

/*MAJ_COCOON_2.1.10
import org.apache.avalon.excalibur.io.FileUtil;*/
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.lucene.search.BooleanQuery;

import fr.gouv.culture.sdx.application.Application;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.framework.Framework;
import fr.gouv.culture.sdx.framework.FrameworkImpl;
import fr.gouv.culture.sdx.search.lucene.query.Query;
import fr.gouv.culture.sdx.search.lucene.query.RegexTerms;
import fr.gouv.culture.sdx.search.lucene.query.RemoteIndex;
import fr.gouv.culture.sdx.search.lucene.query.Terms;
import fr.gouv.culture.sdx.utils.constants.ContextKeys;
import fr.gouv.culture.sdx.utils.constants.Node;
import fr.gouv.culture.sdx.utils.jvm.URLCoderWrapper;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;

/**Useful programming utilities for SDX.
 *
 * Contains some useful static methods.
 */
public class
Utilities {


	public static File getSystemTempDir() {
		String javaIoTmpDir = System.getProperty("java.io.tmpdir");
		if (Utilities.checkString(javaIoTmpDir))
			return new File(javaIoTmpDir);
		else
			return null;
	}

	/**Verifies if a given directory exists, if not the directory is created.
	 *
	 * @param dirPath           The path for the directory to verify.
	 * @param logger            Logger to use for error handling
	 * @throws SDXException
	 */
	public static File checkDirectory(String dirPath, Logger logger)
	throws SDXException
	{

		//verifying the parameter
		if (!Utilities.checkString(dirPath)) throw new SDXException(logger, SDXExceptionCode.ERROR_DIRECTORY_PATH_NULL, null, null);
		//building a file to represent the directory
		File dir = new File(dirPath);
		//verifying the the file creation was ok
		if (dir == null) throw new SDXException(logger, SDXExceptionCode.ERROR_DIRECTORY_NULL, null, null);

		//performing directory checks
		if (!dir.exists()) {
			if (!dir.mkdirs()) {
				String[] args = new String[2];
				args[0] = dirPath;
				args[1] = dir.getAbsolutePath();
				throw new SDXException(logger, SDXExceptionCode.ERROR_CREATE_DIRS, args, null);
			} else
				LoggingUtils.logDebug(logger, "The Directory and any necessary parent directories \"" + dir.getAbsolutePath() + "\" were successfully created.");

		} else
			LoggingUtils.logDebug(logger, "The Directory \"" + dir.toString() + "\" exists.");

		if (!dir.isDirectory()) {
			String[] args = new String[2];
			args[0] = dir.getAbsolutePath();
			throw new SDXException(logger, SDXExceptionCode.ERROR_NOT_DIRECTORY, args, null);
		}

		if (!dir.canRead()) {
			String[] args = new String[2];
			args[0] = dir.getAbsolutePath();
			throw new SDXException(logger, SDXExceptionCode.ERROR_DIRECTORY_NOT_READABLE, args, null);
		}

		if (!dir.canWrite()) {
			String[] args = new String[2];
			args[0] = dir.getAbsolutePath();
			SDXException sdxE = new SDXException(null, SDXExceptionCode.ERROR_DIRECTORY_NOT_WRITABLE, args, null);
			LoggingUtils.logWarn(logger, sdxE.getMessage(), null);
		}

		return dir;

	}

	/**Checks a document to ensure the object is not null and that it's id is not null or an empty string
	 *
	 * @param logger    The super.getLog() for error handling
	 * @param doc       The document object to verify
	 * @throws SDXException Thrown if the object is null, or its id is <code>null</code> or an empty String
	 */
	public static void checkDocument(Logger logger, fr.gouv.culture.sdx.document.Document doc) throws SDXException {
		//ensuring we have a valid object
		if (doc == null)
			throw new SDXException(logger, SDXExceptionCode.ERROR_DOC_NULL, null, null);
		//ensuring we have a valid unique id
		if (!Utilities.checkString(doc.getId())) {
			String[] args = new String[1];
			if (doc.getURL() != null) args[0] = doc.getURL().toString();
			throw new SDXException(logger, SDXExceptionCode.ERROR_INVALID_DOC_ID, args, null);
		}

	}

	/**Verifies that an OutputStream is not null
	 *
	 * @param logger    The super.getLog() for error handling
	 * @param os        The OutputStream to verify
	 * @throws SDXException
	 */
	public static void checkOutputStream(Logger logger, OutputStream os) throws SDXException {
		if (os == null)
			throw new SDXException(logger, SDXExceptionCode.ERROR_OUTPUT_STREAM_NULL, null, null);
	}

	/**Verifies that an XMLConsumer is not null
	 *
	 * @param logger    The super.getLog() for error handling
	 * @param consumer  The XMLConsumer to verify
	 * @throws SDXException
	 */
	public static void checkXmlConsumer(Logger logger, XMLConsumer consumer) throws SDXException {
		if (consumer == null)
			throw new SDXException(logger, SDXExceptionCode.ERROR_XML_CONSUMER_NULL, null, null);
	}

	/**This method will attempt to verify if a file or a directory exists
	 *
	 * @param logger          Logger  The super.getLog() if needed
	 * @param confLocation    String  The location string from a configuration object, this way we can determine our current directory location, if needed
	 * @param context         Context The application properties object, containing the path needed to find the file
	 * @param path            String  The path of the document, absolute or relative to see below:
	 * @param isConfDirectory boolean Indicates whether the file trying to be resolved could be a non-existent FSRepository base directory,
	 *                                if so we would like to create it
	 * <p>This method will attempt to do this in the following order:
	 * <ol>
	 * <li>build a file object using the absolute file path, "file:/..."</li>
	 * <li>a file object using the base path of the web application directory and the relative path provided, must begin with a "/"</li>
	 * <li>a file object using the base path of the current directory (sdx configuration directory or the application configuration directory)
	 *      and the relative path provided, could be something like: "../dir1/dir2/file.extension" or "dir1/file.extension", etc.</li>
	 * </ol>
	 * @return  A File object for the file if we are able to build a correct path to the existing file
	 */
	public static File resolveFile(Logger logger, String confLocation,
							org.apache.avalon.framework.context.Context context,
							String path, boolean isConfDirectory)
			throws SDXException
	{

		String currentDir = confLocation;

		if (!Utilities.checkString(path))
			throw new SDXException(null, SDXExceptionCode.ERROR_RESOLVE_FILE_INVALID_FILE_PATH, null, null);
		File file = null;
		// trying the absolute path option first
		// FIXME: should we support file over a http URL in the future?-rbp
		try {
			URL _url = new URL(path);
			if ( _url.getProtocol().equals("file"))
				file = FileUtils.toFile( _url );
			else
				file = new File( _url.toExternalForm() );
		} catch (MalformedURLException e) {
			if( path.matches("^[a-z]+:.+") )
			{
				// the path contains a protocol (file:, http:, etc.) throws an exeception here
				String[] args = new String[1];
				args[0] = path;
				throw new SDXException(logger, SDXExceptionCode.ERROR_BUILD_URL, args, e);
			}
		} catch (IllegalArgumentException ee) {
			if( path.matches("^file:.+") )
			{
				// the path contains a protocol (file:, http:, etc.) throws an exeception here
				String[] args = new String[1];
				args[0] = path;
				throw new SDXException(logger, SDXExceptionCode.ERROR_BUILD_URL, args, ee);
			}
		}
		if (file != null && file.exists())
			return file; // file exists, return it
		else if (file != null && isConfDirectory)
			return Utilities.checkDirectory(file.getAbsolutePath(), logger); // file is valid but not exists, create it and return it
		else {
			// at this point we need this object to build paths, and we need to ensure it is valid
			if (context != null) {
				/*determining the current directory based upon a string check
				this is a hack, but i see no other way to determine our current
				directory as our configuration architecture needs to remain generic
				to be useful in other places*/
				try {
					//DEBUG: System.out.println("currentDir before : " + currentDir);
					if ( confLocation.startsWith("null:") ) {
						 /*confLocation null : dynamic xconf
						 1) path starts with "/" : path is relative to SDX configuration directory
						 2) path doesn't start with "/" : path is relative to application configuration directory*/
						if ( path.startsWith("/")
								|| path.startsWith(File.separator) )
						{
							currentDir = Utilities.getStringFromContext(ContextKeys.SDX.Framework.CONFIGURATION_PATH, context);
						}
						else {
							currentDir = Utilities.getStringFromContext(ContextKeys.SDX.Application.CONFIGURATION_DIRECTORY_PATH, context);
						}
					}
					else if ( confLocation.indexOf(FrameworkImpl.CONFIGURATION_FILE_NAME) > -1
							 || confLocation.indexOf(new File(Utilities.getStringFromContext(ContextKeys.SDX.Framework.CONFIGURATION_PATH, context)).toURI().toURL().toExternalForm()) > -1 )
					{
						//trying to build a relative path from the sdx configuration directory
						currentDir = Utilities.getStringFromContext(ContextKeys.SDX.Framework.CONFIGURATION_PATH, context);
					}
					else if ( confLocation.indexOf(Application.APP_CONFIG_FILENAME) > -1
							 || confLocation.indexOf(new File(Utilities.getStringFromContext(ContextKeys.SDX.Application.CONFIGURATION_DIRECTORY_PATH, context)).toURI().toURL().toExternalForm()) > -1 )
					{
						//trying to build a relative path from the application's configuration directory
						currentDir = Utilities.getStringFromContext(ContextKeys.SDX.Application.CONFIGURATION_DIRECTORY_PATH, context);
					}
					else {
						String tmp = currentDir;
						int endIdx = tmp.lastIndexOf("/");
						if(endIdx < 0) endIdx = tmp.lastIndexOf(File.separator);
						/* FIXME (MP): Doit-on en informer le developpeur ?
						 if (tmp.startsWith("null:")) {
							// confLocation is null : it's not possible to have relative path with a pure dynamique xconf [MP]
							String[] args = new String[2];
							args[0] = path;
							args[1] = confLocation;
							throw new SDXException(null, SDXExceptionCode.ERROR_RESOLVE_FILE_NULL_LOCATION, args, null);
						}
						else */if (endIdx < 0) endIdx = tmp.length();	// currentDir does not contain slash
						currentDir = (FileUtils.toFile(new URL(tmp.substring(0, endIdx)))).getCanonicalPath();
					}
					//DEBUG: System.out.println("currentDir after : " + currentDir);
				} catch (IOException e) {
					// if not we can't resolve the file, sorry
					String[] args = new String[1];
					args[0] = path;
					throw new SDXException(null, SDXExceptionCode.ERROR_RESOLVE_FILE, args, e);
				}
				// trying to build the path from the sdx application directory under "webapps"
				if ( path.startsWith("/")
						|| path.startsWith(File.separator) )
				{
					/* building the path from the sdx application (cocoon app)
					 * directory and the relative path minus the intial forward slash */
					file = new File(Utilities.getStringFromContext(ContextKeys.SDX.Framework.ROOT_PATH, context), path.substring(1));
				}
				else {
					/*path is relative to currentDir (ie, sdx application dir under $TOMCAT/webapps)
					MAJ Cocoon 2.1.10
					file = FileUtil.resolveFile(new File(currentDir), path);*/
					try {
						file = new File( FilenameUtils.getFullPath( currentDir ), path );
						// file = FileUtils.toFile( new URL( new File(FilenameUtils.getFullPath( currentDir + path) ) );
					} catch (NullPointerException npe) {
						String[] args = new String[1];
						args[0] = path;
						throw new SDXException(null, SDXExceptionCode.ERROR_RESOLVE_FILE, args, npe);
					}
				}
			}
		}
		if (file != null && file.exists()) {
			// file is valid and exists, return it
			return file;
		}
		else if (file != null && isConfDirectory) {
			// file is valid but not exists, create it and return it
			return Utilities.checkDirectory(file.getAbsolutePath(), logger);
		}
		else {
			 /*file is not valid or not (exists or is conf directory)
			 Trying to get a relative path out of it
			 TODO: this part is working but later you can get a ContextException
			 (probably due to the fact the one sent here is null). We should fix it.*/
			if( !( path.startsWith("file:/") | path.startsWith("http:/") ) )
			{
				File fileUpload = new File(currentDir);
				String tmp = fileUpload.getParent();
				tmp = Utilities.replaceAllSubString(tmp,"\\\\", "/");
				if(tmp.lastIndexOf('/') != tmp.length()) tmp += '/';
				file = new File(tmp + path);
			}
			if ( file != null && file.exists() ) {
				return file;
			}
			else {
				//if not we can't resolve the file, sorry
				String[] args = new String[1];
				args[0] = path;
				throw new SDXException(logger, SDXExceptionCode.ERROR_RESOLVE_FILE, args, null);
			}
		}

	}

	/**Returns true if a string is not null or empty
	 *
	 * @param s The string to verify
	 */
	public static boolean checkString(String s) {
		if (s != null && !s.trim().equals(""))
			return true;
		else
			return false;
	}


	/**Returns a locale from a configuration object
	 *
	 * @param conf  The configuration object for the element which contains the "xml:lang"
	 *              attribute with an optional "variant" attribute.
	 * @param defaultLocale A default locale to use if the building of the local fails
	 *                     if a <code>null</code> defaultLocale is passed, the system default is used.
	 */
	public static Locale buildLocale(Configuration conf, Locale defaultLocale) {
		//getting 'xml:lang' attr, default is null if it doesn't exist
		String xmlLang = conf.getAttribute(Node.Name.XML_LANG, null);
		//getting 'variant' attr, default is null if it doesn't exist
		String variant = conf.getAttribute(Node.Name.VARIANT, null);
		return Utilities.buildLocale(xmlLang, variant, defaultLocale);
	}

	/**Returns a locale from a String
	 *
	 * @param xmlLang  A valid xml:lang attribute value.
	 * @param variant  A variant from the Java specs,can be <code>null</code>
	 * @param defaultLocale A default locale to use if the building of the local fails
	 *                     if a <code>null</code> defaultLocale is passed, the system default is used.
	 */
	public static Locale buildLocale(String xmlLang, String variant, Locale defaultLocale) {
		Locale locale = null;
		//the country portion of the xml:lang attr
		String country = "";
		//the language portion of the xml:lang attr
		String lang = "";

		//if a default was not provided we use the system default
		if (defaultLocale == null)
			defaultLocale = Locale.getDefault();

		if (Utilities.checkString(xmlLang)) {
			//hypen, "-", is the separator between the primary tag and subtag in RFC 1766
			int hypenIndex = xmlLang.indexOf("-");
			//if there is a hypen we split the until the hypen for the lang and after the hypen for the country
			if (hypenIndex > 0) {
				lang = xmlLang.substring(0, hypenIndex);
				country = xmlLang.substring(hypenIndex + 1);
			} else//if no hypen, we have been given a country only
				lang = xmlLang;
		}

		if (Utilities.checkString(lang)) {
			if (Utilities.checkString(country)) {
				if (Utilities.checkString(variant)) {
					locale = new Locale(lang, country, variant);
				} else
					locale = new Locale(lang, country);
			} else
				locale = new Locale(lang, "");
		} else
			locale = defaultLocale;

		return locale;
	}

	/** Build an id for an attached document from :
	 *      the parent document id (@refId)
	 *      a name unique relatively to the parent document (@relId)
	 *  Used to store and attach files to a parent document (example: images)
	 *
	 * @param   baseId  the id of parent document
	 * @param   relId  an id relative to the parent document
	 * @return  an id surely unique in scope of a document base
	 */
	public static String attId(String baseId, String relId) {
		return baseId + "_" + relId;
	}

	/** Build an URL for an attached document from :
	 *      the parent document URL (@refURL)
	 *      a path relative to the parent document (@path)
	 *  Used for relative URL like <img href="images/myimage.jpg"/> in an HTML document
	 *
	 * @param   refURL  a well-formed URL
	 * @param   path  a path string it could be relative or absolute
	 * @return  a URL for a
	 * @throws MalformedURLException
	 */
	public static URL attUrl(URL refURL, String path) throws MalformedURLException {
		//building document attUrl from parent document attUrl and relative attUrl
		String attUrl = path;
		try {
			//trying to build a url from an absolute path
			return new URL(path);
		} catch (MalformedURLException e) {
			//trying to build a url from a relative path
			if (refURL != null && Utilities.checkString(refURL.toExternalForm())) {
				/*
                String basePath = refURL.toExternalForm().substring(0, refURL.toString().lastIndexOf("/") + 1);
                //now we have the present directory path of the parent file
                //we make sure we have a good base attUrl (ie it ends with a forward slash)
                if (!basePath.endsWith("/")) basePath = basePath + "/";
                attUrl = basePath + path;
				 */
				try {
					URL baseURL = new URL( org.apache.commons.io.FilenameUtils.getFullPath(refURL.toString()));
					URL resultURL = new URL ( baseURL, attUrl );
//					MAJ Cocoon 2.1.10
//					File f = FileUtil.toFile(refURL);
//					URL resultURL = null;
//					if (f != null){
//					resultURL =
//					FileUtil.resolveFile(FileUtil.toFile(refURL).getParentFile(),
//					attUrl).toURL();
//					}
//					else if (f == null){
//					URL baseURL = new URL
//					(refURL.toString().substring(0,refURL.toString().lastIndexOf("/")+1));
//					resultURL = new URL(baseURL, attUrl);
//					}
					return resultURL;
				} catch (MalformedURLException e1) {
					try {
						/*if all is fails we will just add the paths together, but they must be in the proper format
                        for java's URL, sorry can't do much else here*/
						return new URL(refURL, attUrl);
					} catch (MalformedURLException e2) {
						throw new MalformedURLException(e.getMessage() + "\n" + e1.getMessage() + "\n" + e2.getMessage());
					}
				}
			} else
				throw e;
		}

	}


	/**Return's a string value from a hashtable by making the appropriate cast
	 *
	 * @param key   The key for the string value
	 * @param context The ContextKeys
	 * @return String
	 */
	public static String getStringFromContext(String key, org.apache.avalon.framework.context.Context context) {
		String value = null;
		if (Utilities.checkString(key) && context != null) {
			try {
				value = (String) context.get(key);
			} catch (ContextException e) {
				value = null;
			}
		}
		return value;
	}

	/**Return's a string value from a hashtable by making the appropriate cast
	 *
	 * @param key   The key for the string value
	 * @param context The context
	 * @return Object
	 */
	public static Object getObjectFromContext(String key, org.apache.avalon.framework.context.Context context) {
		Object value = null;
		if (Utilities.checkString(key) && context != null) {
			try {
				value = context.get(key);
			} catch (ContextException e) {
				value = null;
			}
		}
		return value;
	}

	/**Builds a rmi url string for RemoteIndex lookups
	 *
	 * @param rmiHost   The host name or ip address of the machine
	 * @param rmiPort   The port number for the rmi registry
	 * @param appId     The id of the application to which the RemoteIndex belongs
	 * @param dbId      The id of the document base to which the RemoteIndex belongs
	 * @return String
	 */
	public static String buildRmiName(String rmiHost, int rmiPort, String appId, String dbId) {
		String rmiName = null;
		if (Utilities.checkString(rmiHost) && Utilities.checkString(appId) && Utilities.checkString(dbId))
			rmiName = "//" + rmiHost + ":" + Integer.toString(rmiPort) + "/" + appId + "_" + dbId;
		return rmiName;
	}

	public static RemoteIndex getRemoteIndex(Logger logger, String remoteIndexName) throws SDXException {
		RemoteIndex rIndex = null;
		try {
			rIndex = (RemoteIndex) Naming.lookup(remoteIndexName);

		} catch (Exception e) {
			String[] args = new String[1];
			args[0] = remoteIndexName;
			throw new SDXException(logger, SDXExceptionCode.ERROR_GET_REMOTE_INDEX, args, e);
		}
		return rIndex;
	}

	/**Joins an array of strings
	 *
	 * @param strings   The array of strings
	 * @param delimiter The delimiter, if none desired use <code>null</code>
	 * @return  The joined string, or <code>null</code> if the array was null or empty
	 */
	public static String joinStrings(String[] strings, String delimiter) {
		if (strings == null || strings.length == 0) return null;

		if (delimiter == null) delimiter = "";
		String newString = null;
		for (int i = 0; i < strings.length; i++) {

			if (strings[i] == null) strings[i] = "";
			//if it is the first string, we dont want to add the delimiter
			if (i == 0)
				newString = strings[i];
			else
				newString = newString + delimiter + strings[i];
		}
		return newString;
	}

	public static Application getApplication(ServiceManager manager, org.apache.avalon.framework.context.Context context) throws SDXException {
		Application app = null;
		String appId = Utilities.getStringFromContext(ContextKeys.SDX.Application.ID, context);
		FrameworkImpl frame = null;
		if (Utilities.checkString(appId)) {
			try {
				frame = (FrameworkImpl) manager.lookup(Framework.ROLE);
				if (frame != null)
					app = frame.getApplicationById(appId);
			} catch (ServiceException e) {
				throw new SDXException(null, SDXExceptionCode.ERROR_GENERIC, null, e);
			} finally {
				manager.release(frame);
			}

		}
		return app;
	}

	public static String encodeURL(String url, String encoding) {
		try {
			return URLCoderWrapper.encode(url, encoding);
		} catch (Exception e) {
			try {
				return URLCoderWrapper.encode(url, Encodable.DEFAULT_ENCODING);
			} catch (Exception e1) {
				return url;
			}
		}
	}

	public static String decodeURL(String url, String encoding) {
		try {
			return URLCoderWrapper.decode(url, encoding);
		} catch (Exception e) {
			try {
				return URLCoderWrapper.decode(url, Encodable.DEFAULT_ENCODING);
			} catch (Exception e1) {
				return url;
			}
		}
	}

	public static String prefixNodeNameSDX(String elemName) {
		return SAXUtils.prefixNodeName(Framework.SDXNamespacePrefix, elemName);
	}

	public static File createTempDirectory(Logger logger, String prefix, String suffix, File parentDir) throws SDXException {
		try {
			Utilities.checkDirectory(parentDir.getAbsolutePath(), logger);
			File tmpFile = File.createTempFile(prefix, suffix, parentDir);
			//TODOException: unable to delete file
			if (!tmpFile.delete()) throw new SDXException(logger, SDXExceptionCode.ERROR_GENERIC, null, null);
			while (tmpFile.isDirectory())//if this directory exists we recurse
				tmpFile = Utilities.createTempDirectory(logger, prefix, suffix, parentDir);
			return Utilities.checkDirectory(tmpFile.getAbsolutePath(), logger);
		} catch (IOException e) {
			//TODOException: a better one here
			throw new SDXException(logger, SDXExceptionCode.ERROR_GENERIC, null, e);
		}

	}

	public static SdxObject setUpSdxObject(SdxObject sdxObj, Logger logger, org.apache.avalon.framework.context.Context context, ServiceManager manager) throws ConfigurationException {
		try {
			sdxObj.enableLogging(logger);
			sdxObj.contextualize(context);
			sdxObj.service(manager);
		} catch (ContextException e) {
			throw new ConfigurationException(e.getMessage(), e);
		} catch (ServiceException e) {
			throw new ConfigurationException(e.getMessage(), e);
		}
		return sdxObj;
	}

	public static void isObjectUnique(Hashtable objs, String key, Object obj) throws SDXException {
		String l_key = key;
		if (!Utilities.checkString(l_key) && obj != null && obj instanceof SdxObject) l_key = ((SdxObject) obj).getId();
		if (objs!=null && objs.containsKey(l_key)) {
			String[] l_args = new String[2];
			l_args[0] = "";
			if (obj != null) l_args[0] = obj.toString();
			l_args[1] = l_key;
			throw new SDXException(null, SDXExceptionCode.ERROR_OBJECT_WITH_ID_EXISTS, l_args, null);
		}
	}

	public static void isObjectUnique(Context objs, String key, Object obj) throws SDXException {
		String l_key = key;
		if (!Utilities.checkString(l_key) && obj != null && obj instanceof SdxObject) l_key = ((SdxObject) obj).getId();
		try {
			Object l_obj = objs.get(l_key);
			if (l_obj != null) {//the object with the l_key already exists
				String[] l_args = new String[2];
				l_args[0] = l_obj.toString();
				if (obj != null) l_args[0] = obj.toString();
				l_args[1] = l_key;
				throw new SDXException(null, SDXExceptionCode.ERROR_OBJECT_WITH_ID_EXISTS, l_args, null);
			}
		} catch (ContextException e) {
			//do nothing here as the object doesn't already exist in the context object
		}
	}


	public static Object getObjectForClassName(Logger logger, String fullClassName, String packageNamePrefix, String shortName, String classNameSuffix) throws ConfigurationException {
		//reading the document base type attribute, if not we use the default, currently "lucene"
		//building the fully qualified class name based on the type
		String l_className = fullClassName;
		//getting the class from the class name
		Class l_objClass = null;
		Object l_obj = null;
		try {
			try {
				l_objClass = Class.forName(l_className);
			} catch (ClassNotFoundException e) {
				//trying to build a class name using prefix, shortName provided like fr.gouv.culture.sdx.repository. + MYSQLDatabase
				l_className = packageNamePrefix + shortName.substring(0, 1).toUpperCase() + shortName.substring(1, shortName.length());
				try {
					l_objClass = Class.forName(l_className);
				} catch (ClassNotFoundException e1) {
					//trying to build a class name using prefix, shortName, and suffix provided like fr.gouv.culture.sdx.repository. + MYSQL + Database
					l_className = l_className + classNameSuffix;
					try {
						l_objClass = Class.forName(l_className);
					} catch (ClassNotFoundException e2) {
						//trying to build a class name using prefix, shortName, and suffix provided like fr.gouv.culture.sdx.repository. + mysql.toUpperCase() + Database
						l_className = packageNamePrefix + shortName.toUpperCase() + classNameSuffix;
						try {
							l_objClass = Class.forName(l_className);
						} catch (ClassNotFoundException e3) {
							//all class resolution failed so we will throw the original exception
							throw e;
						}
					}
				}
			}

			//building a new instance of the object
			l_obj = l_objClass.newInstance();

		} catch (ClassNotFoundException e) {
			throw new ConfigurationException(e.getMessage(), e);
		} catch (InstantiationException e) {
			throw new ConfigurationException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw new ConfigurationException(e.getMessage(), e);
		}

		if (l_obj == null) {
			String[] args = new String[1];
			args[0] = shortName;
			SDXException l_sdxE = new SDXException(null, SDXExceptionCode.ERROR_NEW_OBJECT_INSTANCE_NULL, args, null);
			throw new ConfigurationException(l_sdxE.getMessage(), l_sdxE);
		}

		return l_obj;
	}

	public static String getElementName(String classNameSuffix) {
		return classNameSuffix.substring(0, 1).toLowerCase() + classNameSuffix.substring(1, classNameSuffix.length());

	}

	public static org.apache.avalon.framework.context.Context createNewReadOnlyContext(org.apache.avalon.framework.context.Context context) {
		DefaultContext l_context = new DefaultContext(context);
		l_context.makeReadOnly();
		return l_context;
	}

	public static BooleanQuery newBooleanQuery(){
		BooleanQuery l_bq = new BooleanQuery();
		BooleanQuery.setMaxClauseCount(Query.LUCENE_BOOLEAN_QUERY_MAX_CLAUSES);
		return l_bq;
	}


	public static boolean copyFile(File srcFile, File dstDir) throws IOException
	{
		if(srcFile == null || dstDir == null) return false;
		if(!(srcFile.isFile() && dstDir.isDirectory())) return false;

		final int BUFFER_SIZE = 1024;
		byte[] bArray = new byte[BUFFER_SIZE];
		long bufPointer = 0;
		int getSize = BUFFER_SIZE;

		// clean and create destination file
		File targetFile = new File(dstDir.getAbsolutePath() + File.separator + srcFile.getName());
		targetFile.delete();
		targetFile.createNewFile();

		// data flow to copy from
		FileInputStream streamToCopy = new FileInputStream(srcFile);
		// data flow to copy to
		FileOutputStream streamTarget = new FileOutputStream(targetFile);

		// copying file content
		while(bufPointer < srcFile.length())
		{
			//Computing right size to read / write
			if(srcFile.length() - bufPointer < BUFFER_SIZE) getSize = (int) (srcFile.length() - bufPointer);
			else getSize = BUFFER_SIZE;

			//read data
			streamToCopy.read(bArray, 0, getSize);
			//write them
			streamTarget.write(bArray, 0, getSize);

			//increment offset
			bufPointer += BUFFER_SIZE;
		}
		streamTarget.close();
		streamToCopy.close();

		return true;
	}

	/**
	 * Copy an index to an other in a safe and efficient mode
	 * try to do a rename to do a "cut and paste", or if failed try a copy and delete
	 */
	public static void safeCopy(File src, File dst)
	{
		if(src == null || dst == null || !src.exists()) return;

		// new method using apache commons io package
		try {
			FileUtils.deleteDirectory(dst);
			FileUtils.copyDirectory(src, dst);
		} catch (IOException e){
//			TODO: handle the exception
		}


//		old method using org.apache.avalon.excalibur.io.FileUtil deprecated since Cocoon 2.1.10
//		try the rename method
//		try {
//		FileUtil.deleteDirectory(dst.getAbsolutePath());

//		} catch (IOException e) {
//		//TODO: handle the exception
//		}
//		src.renameTo(dst);

		// if renamed failed, then do a full copy
		if(!dst.exists())
		{
			// copy the files
			File[] content = src.listFiles();
			int i = 0;
			boolean copyCheck = true;
			while(content != null && i < content.length && copyCheck)
			{
				if(content[i].isFile())
					try {
						copyCheck = copyFile(content[i], dst);
					} catch (IOException e) {
						//TODO: handle the exception
					}
					i++;
			}
			// and now delete the old ones if there is no errors
			try {
				if(copyCheck) FileUtils.deleteDirectory(src);
			} catch (IOException e) {
				//TODO: handle the exception
			}
		}
	}

	/**
	 * Is the name given in parameter is a correct index file name?
	 */
	public static boolean isNameMatchIndexFiles(String name)
	{
		//Small hack to be compatible with Java 1.3
		long value=0;
		try{
			value = Long.parseLong(name);
		}catch(NumberFormatException e){
			return false;
		}
		if(value <0 || value >9999)
			return false;
		return true;
	}

	/**
	 * Replace all the substring given in parameter by another substring
	 */
	public static String replaceAllSubString(String source, String what, String with)
	{
		//replace the method String.replaceAll in Java 1.4 to be compatible Java 1.3
		StringBuffer result = new StringBuffer(source);
		int i = source.indexOf(what);
		int length_what = what.length();
		int length_with = with.length();
		while(i != -1)
		{
			result = result.replace(i, i+length_what,with);
			i=result.toString().indexOf(what, i+length_with);
		}
		return result.toString();
	}
	
	/** Converts a string into a regular expression.
	 * 
	 * <p>Converts a string into a regular expression suitable to search
	 * a string regardless of diacritics.
	 * <br/>Exemple: for "église", this method returns the regular expression
	 * "[eéèêëgliseéèêë]".
	 * </p>
	 * 
	 * Care, this method only support ISO-8859-1 encoding!
	 * 
	 * @param string	The string to convert in regular expression
	 * @param encoding	The encoding used to convert the string
	 * @return	Regular expression as a String
	 */
	public static String buildSuggestRegexValue(String string, String encoding)
	{

		String ret = null;
		try
		{

			if ( !encoding.equals("ISO-8859-1") )
			{
				return null;
			}
			else if ( encoding.equals("ISO-8859-1") )
			{

				int m_length = string.length();
				StringBuffer chars = new StringBuffer( m_length );
				for ( int i = 0; i < m_length; i++ )
				{
					char c = string.charAt(i);
					switch (c) {
					/* NOTE (MP) : on echappe pas les caracteres reserves en
					 * expression reguliere. on estime que ce qui vient est une
					 * expression reguliere correcte. */
					// case '\u0028': // "("
					// case '\u0029': // ")"
					// case '\u002A': // "*"
					// case '\u002B': // "+"
					// case '\u002E': // "."
					// case '\u005B': // "["
					// case '\\': // "\"
					// case '\u005D': // "]"
					// case '\u005E': // "^"
					// case '\u007B': // "{"
					// case '\u007C': // "|"
					// case '\u007D': // "}"
					//   chars.append("\\"+c); // caracteres reserves en expression reguliere
					//   break;
					case 'A':
					case '\u00C0':
					case '\u00C1':
					case '\u00C2':
					case '\u00C3':
					case '\u00C4':
					case '\u00C5':
					case 'a':
					case '\u00E0':
					case '\u00E1':
					case '\u00E2':
					case '\u00E3':
					case '\u00E4':
					case '\u00E5':
						chars.append("[a\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5]");
						break;
					case '\u00C6': // AE ligature
					case '\u00E6': // ae ligature
						chars.append("((AE)|\u00E6)");
						break;
					case 'C':
					case '\u00C7':
					case 'c':
					case '\u00E7':
						chars.append("[c\u00E7]");
						break;
					case 'E':
					case '\u00C8':
					case '\u00C9':
					case '\u00CA':
					case '\u00CB':
					case 'e':
					case '\u00E8':
					case '\u00E9':
					case '\u00EA':
					case '\u00EB':
						chars.append("[e\u00E8\u00E9\u00EA\u00EB]");
						break;
					case 'I':
					case '\u00CC':
					case '\u00CD':
					case '\u00CE':
					case '\u00CF':
					case 'i':
					case '\u00EC':
					case '\u00ED':
					case '\u00EE':
					case '\u00EF':
						chars.append("[i\u00EC\u00ED\u00EE\u00EF]");
						break;
					case 'D':
					case '\u00D0':
					case 'd':
					case '\u00F0':
						chars.append("[d\u00F0]");
						break;
					case 'N':
					case '\u00D1':
					case 'n':
					case '\u00F1':
						chars.append("[n\u00F1]");
						break;
					case 'O':
					case '\u00D2':
					case '\u00D3':
					case '\u00D4':
					case '\u00D5':
					case '\u00D6':
					case '\u00D8':
					case 'o':
					case '\u00F2':
					case '\u00F3':
					case '\u00F4':
					case '\u00F5':
					case '\u00F6':
					case '\u00F8':
						chars.append("[o\u00F2\u00F3\u00F4\u00F5\u00F6\u00F8]");
						break;
					case '\u0152':
					case '\u0153':
						chars.append("((oe)|\u0153)");
						break;
					case '\u00DE':
					case '\u00FE':
						chars.append("((th)|\u00FE)");
						break;
					case 'U':
					case '\u00D9':
					case '\u00DA':
					case '\u00DB':
					case '\u00DC':
					case 'u':
					case '\u00F9':
					case '\u00FA':
					case '\u00FB':
					case '\u00FC':
						chars.append("[u\u00F9\u00FA\u00FB\u00FC]");
						break;
					case 'Y':
					case '\u00DD':
					case '\u0178':
					case 'y':
					case '\u00FD':
					case '\u00FF':
						chars.append("[y\u00FD\u00FF]");
						break;
					case '\u00DF':
						chars.append("((ss)|"+c+")");
						break;
					default:
						chars.append( c );
						break;
					}
				}

				ret = chars.toString();
			}

		} catch(Exception e) {
			return null;
		}
		Terms _terms = new RegexTerms();
		try {
			((RegexTerms) _terms).setField("unchamp");
		} catch (SDXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ret;

	}
	
	/**
	 * Converts a string into a regular expression, using the encoding &quot;ISO-8859-1&quot;.
	 * 
	 * @param The {@link String} to convert.
	 * @return The {@link String} representation of the regular expression.
	 * @see #buildSuggestRegexValue(String, String)
	 */
	public static String buildSuggestRegexValue(String str) {
		return buildSuggestRegexValue(str, "ISO-8859-1");
	}

}
