package com.compomics.util.experiment.io.identifications;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ServiceLoader;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
/**
* This factory will provide the appropriate identification file reader for each
* type of file. Null when the format is not supported.
*
* @author Marc Vaudel
*/
public class IdfileReaderFactory {
/**
* Class specific log4j logger for Enzyme instances.
*/
static Logger logger = Logger.getLogger(IdfileReaderFactory.class);
/**
* The factory instance.
*/
private static IdfileReaderFactory singleton = null;
/**
* The list of registered IdfileReaders.
*/
private static HashMap<String, Class> idFileReaders = new HashMap<String, Class>();
/**
* Static initializer block that checks for registered IdfileReaders through
* the Java service loader
*/
static {
ServiceLoader<IdfileReader> ifdrServiceLoader = ServiceLoader.load(IdfileReader.class);
Iterator<IdfileReader> idfrIterator = ifdrServiceLoader.iterator();
while (idfrIterator.hasNext()) {
IdfileReader idfileReader = idfrIterator.next();
logger.info("Found IdfileReader '" + idfileReader.getClass().getCanonicalName() + "' in Java service loader.");
IdfileReaderFactory.registerIdFileReader(idfileReader.getClass(), idfileReader.getExtension());
}
}
/**
* The factory constructor.
*/
private IdfileReaderFactory() {
}
/**
* A static method to retrieve the instance of the factory.
*
* @return the factory instance
*/
public static IdfileReaderFactory getInstance() {
if (singleton == null) {
singleton = new IdfileReaderFactory();
}
return singleton;
}
/**
* This method registers a new IdfileReader Class, and the file extension it
* can read from. Note that the collection of IdfileReaders is keyed by this
* extension, and similar to the java.util.HashMap syntax, a Class is
* therefore returned if the extension provided already had an associated
* Class.
*
* @param aReader Class of the IdfileReader to register.
* @param aExtension String with the extension of the file that this
* IdfileReader implementation can read.
* @return Class with the Class that was already previously registered for
* this extension, or 'null' if the extension was not yet registered at all.
*/
public static Class registerIdFileReader(Class aReader, String aExtension) {
Class result = null;
// See if we have the right type of class!
if (IdfileReader.class.isAssignableFrom(aReader)) {
// Now verify the presence of a correct constructor!
try {
aReader.getConstructor(File.class);
result = idFileReaders.put(aExtension, aReader);
logger.info("Registered IdfileReader implementation '" + aReader.getCanonicalName() + "' for extension '" + aExtension + "'.");
if (result != null) {
logger.warn("Overwrite occurred for extension '" + aExtension + "'; replaced old IdfileReader '" + result.getCanonicalName() + "' with new IdfileReader '" + aReader.getCanonicalName() + "'!");
}
} catch (NoSuchMethodException nsme) {
logger.warn("Unable to find required constructor with single java.io.File parameter in IdfileReader implementation '" + aReader.getCanonicalName() + "'! IdfileReader is ignored!");
nsme.printStackTrace();
}
} else {
logger.warn("Was expecting an implementation of '" + IdfileReader.class.getCanonicalName() + "', but got class '" + aReader.getCanonicalName() + "' instead! Ignoring IdfileReader!");
}
return result;
}
/**
* This method returns the proper identification file reader depending on
* the format of the provided file. It is very important to close the file
* reader after creation.
*
* @param aFile the file to parse
* @return an adapted file reader
*
* @throws SAXException if a SAXException occurs
* @throws FileNotFoundException if a FileNotFoundException occurs
* @throws IOException if an IOException occurs
* @throws OutOfMemoryError thrown if the parser runs out of memory
*/
public IdfileReader getFileReader(File aFile) throws SAXException, FileNotFoundException, IOException, OutOfMemoryError {
// @TODO: create parsers using waiting handlers and indexed files.
// The return value, defaulting to null.
IdfileReader result = null;
// Get file name of the idfile to process.
String name = aFile.getName().toLowerCase();
// Iterator registered IdfileReaders, see who likes this file. First come, first served.
// @TODO: May want to make this more sophisticated, possibly like the DBLoaders in DBToolkit,
// that get the actual file to read some lines prior to making up their mind; thus constitutes
// an actual format check rather than an extension check.
Iterator<String> extensions = idFileReaders.keySet().iterator();
while (extensions.hasNext()) {
String key = extensions.next();
String extension = key.toLowerCase();
if (name.endsWith(extension)) {
Class idfileReaderClass = idFileReaders.get(key);
try {
result = (IdfileReader) idfileReaderClass.getConstructor(File.class).newInstance(aFile);
break;
} catch (NoSuchMethodException nsme) {
logger.error("Unable to find required constructor with single java.io.File parameter in IdfileReader implementation '"
+ idfileReaderClass.getCanonicalName() + "', matching query extension '" + extension + "'!", nsme);
nsme.printStackTrace();
} catch (IllegalAccessException iae) {
logger.error("Required public constructor with single java.io.File parameter in IdfileReader implementation '"
+ idfileReaderClass.getCanonicalName() + "', matching query extension '" + extension + "' has incorrect access modifier!", iae);
iae.printStackTrace();
} catch (InvocationTargetException ite) {
if (ite.getCause() instanceof OutOfMemoryError) {
ite.printStackTrace();
throw (OutOfMemoryError) ite.getCause();
} else {
logger.error("Required constructor with single java.io.File parameter in IdfileReader implementation '"
+ idfileReaderClass.getCanonicalName() + "', matching query extension '" + extension + "' threw an exception!", ite);
ite.printStackTrace();
}
} catch (InstantiationException ie) {
logger.error("Required constructor with single java.io.File parameter in IdfileReader implementation '"
+ idfileReaderClass.getCanonicalName() + "', matching query extension '" + extension + "' inaccessible; probably abstract class?!", ie);
ie.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
}