package tests.net.sf.jabref.imports;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import junit.framework.TestCase;
import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.FindUnlinkedFilesDialog;
import net.sf.jabref.JabRefPreferences;
import net.sf.jabref.FindUnlinkedFilesDialog.CheckableTreeNode;
import net.sf.jabref.external.ExternalFileType;
import net.sf.jabref.gui.FileListEntry;
import net.sf.jabref.gui.FileListTableModel;
import net.sf.jabref.imports.BibtexParser;
import net.sf.jabref.imports.EntryFromPDFCreator;
import net.sf.jabref.imports.ParserResult;
import net.sf.jabref.imports.UnlinkedFilesCrawler;
/**
*
* @author Nosh&Dan
* @version 09.11.2008 | 21:06:17
*
*/
public class DatabaseFileLookupTest extends TestCase {
private BibtexDatabase database;
private Collection<BibtexEntry> entries;
private BibtexEntry entry1;
private BibtexEntry entry2;
private File pdfDirectory;
private File fileInDatabase;
private File fileNotInDatabase;
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
ParserResult result = BibtexParser.parse(new FileReader("src/tests/net/sf/jabref/util/unlinkedFilesTestBib.bib"));
database = result.getDatabase();
entries = database.getEntries();
entry1 = database.getEntryByKey("entry1");
entry2 = database.getEntryByKey("entry2");
pdfDirectory = new File("src/tests/net/sf/jabref/imports/unlinkedFilesTestFolder");
fileInDatabase = new File(pdfDirectory.getPath() + File.separator + "pdfInDatabase.pdf");
fileNotInDatabase = new File(pdfDirectory.getPath() + File.separator + "pdfNotInDatabase.pdf");
}
/**
* Tests the prerequisites of this test-class itself.
*/
public void testTestDatabase() throws Exception {
assertEquals(2, database.getEntryCount());
assertEquals(2, entries.size());
assertNotNull(entry1);
assertNotNull(entry2);
assertTrue(pdfDirectory.exists());
assertTrue(pdfDirectory.isDirectory());
assertTrue(fileInDatabase.exists());
assertTrue(fileInDatabase.isFile());
assertTrue(fileNotInDatabase.exists());
assertTrue(fileNotInDatabase.isFile());
}
public void testInsertTestData() throws Exception {
entry1 = new BibtexEntry();
JabRefPreferences jabRefPreferences = JabRefPreferences.getInstance();
ExternalFileType fileType = jabRefPreferences.getExternalFileTypeByExt("PDF");
FileListEntry fileListEntry = new FileListEntry("", fileInDatabase.getAbsolutePath(), fileType);
FileListTableModel model = new FileListTableModel();
model.addEntry(0, fileListEntry);
entry1.setField("file", model.getStringRepresentation());
database.insertEntry(entry1);
// #################### SETUP END ##################### //
UnlinkedFilesCrawler crawler = new UnlinkedFilesCrawler(database);
CheckableTreeNode treeNode = crawler.searchDirectory(pdfDirectory, new EntryFromPDFCreator());
assertNotNull(treeNode);
/**
* Select all nodes manually.
*/
Enumeration enumeration = treeNode.breadthFirstEnumeration();
while(enumeration.hasMoreElements()) {
CheckableTreeNode nextElement = (CheckableTreeNode) enumeration.nextElement();
nextElement.setSelected(true);
}
List<File> resultList = getFileListFromNode(treeNode);
assertFalse(resultList.isEmpty());
assertTrue(resultList.contains(fileNotInDatabase));
assertFalse(resultList.contains(fileInDatabase));
}
/**
* Connector-Method for the private method
* {@link FindUnlinkedFilesDialog#getFileListFromNode()} of the dialog
* {@link FindUnlinkedFilesDialog}. <br>
* <br>
* This method uses <b>reflection</b> to get access to that method.
*
* @see FindUnlinkedFilesDialog#getFileListFromNode()
*/
private List<File> getFileListFromNode(CheckableTreeNode node) throws Exception {
return invokeMethod("getFileListFromNode", FindUnlinkedFilesDialog.class, node);
}
/**
* Invokes a method in the supplied class with the given arguments, and
* returnes the methods result in the desired type. <br>
* <br>
* The only requirement ist, that the type, on which the method is to be
* called, has the default constructor. <br>
* <br>
* This method will create an instance of the provided class
* <code>targetClass</code>, which is generally described by the generic
* parameter <code>T</code> (for <i>Type</i>). The instance is created using
* the <b>default constructor</b>. If the default constructor is not
* declared, an Exception will be throwen. However, there is no requirement
* on the visibility of the default constructor. <br>
* Using this instance, the method specified by the string parameter
* <code>methodName</code> will be invoked. Again, there is no requirement
* on the visibility of the method. <br>
* The method will be invoked, using the supplied parameter-list
* <code>params</code>. <br>
* <br>
* The result will be returned as an object of the generic type
* <code>R</code> (for <i>Result</i>), and as this type parameter
* <code>R</code> is not further specified, the result my be any type and
* does not need to be casted.
*
* @param <R>
* The result type of the method. Does not need to be declared.
* @param <T>
* The type, on which the method will be invoked.
* @param methodName
* Method name to be invoked.
* @param targetClass
* Class instance of the type, on which the method is to be
* invoked.
* @param params
* Parameters for the invokation of the method.
* @return The result of the method, that is invoked.
*/
@SuppressWarnings("unchecked")
public static <R, T> R invokeMethod(String methodName, Class<T> targetClass, Object... params) throws Exception {
T instance = getInstanceFromType(targetClass);
if (instance == null) {
throw new InstantiationException("The type '" + targetClass + "' could not be instantiated.");
}
Class<?>[] paramTypes = new Class<?>[params.length];
for (int i = 0; i < params.length; i++)
paramTypes[i] = params[i].getClass();
Method method = targetClass.getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return (R) method.invoke(instance, params);
}
private static <T> T getInstanceFromType(Class<T> targetClass) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
T instance = null;
try {
Constructor<? extends T> constructor;
constructor = targetClass.getDeclaredConstructor(new Class<?>[] {});
constructor.setAccessible(true);
instance = constructor.newInstance(new Object[] {});
} catch (Exception e) {
instance = getInstanceFromNonDefaultConstructor(targetClass);
}
return instance;
}
@SuppressWarnings("unchecked")
private static <T> Constructor<? extends T>[] orderByParamCount(Constructor<? extends T>[] constructors) {
List<Constructor<? extends T>> list = Arrays.asList(constructors);
Collections.sort(list, new Comparator<Constructor<? extends T>>() {
public int compare(Constructor<? extends T> c1, Constructor<? extends T> c2) {
return new Integer(c1.getParameterTypes().length).compareTo(c2.getParameterTypes().length);
}
});
return new ArrayList<Constructor<? extends T>>(list).toArray(new Constructor[list.size()]);
}
private static <T> T getInstanceFromNonDefaultConstructor(Class<T> targetClass) {
Constructor<?>[] constructors = targetClass.getDeclaredConstructors();
constructors = orderByParamCount(constructors);
for (int i = 0; i < constructors.length; i++) {
Constructor<?> constructor = constructors[i];
constructor.setAccessible(true);
Class<?>[] parameterTypes = constructor.getParameterTypes();
try {
/**
* Trying to invoke constructor with <code>null</code> values.
*/
@SuppressWarnings("unchecked")
T instance = (T) constructor.newInstance(new Object[parameterTypes.length]);
return instance;
} catch (Exception e) {
}
/**
* Creating proper instances for the parameter types.
*/
Object[] arguments = createArguments(parameterTypes, targetClass);
if (arguments == null) {
continue;
}
try {
@SuppressWarnings("unchecked")
T instance = (T) constructor.newInstance(arguments);
return instance;
} catch (Exception e) {
continue;
}
}
return null;
}
/**
* Creates an argument-array for the type-array <code>parameterTypes</code>
* by trying to instanciate every single parameter type. <br>
* <br>
* If one of the instanciation fails, the <code>null</code> value will be written
* into the argument-array.
*
* @param parameterTypes An Array of types, which shall be created.
* @return An array of arguments.
*/
private static <T> Object[] createArguments(Class<?>[] parameterTypes, Class<T> targetClass) {
Object[] parameterValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> typeClass = parameterTypes[i];
if (targetClass.equals(typeClass)) {
return null;
}
try {
parameterValues[i] = getInstanceFromType(typeClass);
}
catch (Exception e) {
parameterValues[i] = null;
}
}
return parameterValues;
}
}