/*
* Created on Jun 13, 2004
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package net.sf.jabref.imports;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.GUIGlobals;
import net.sf.jabref.Globals;
import net.sf.jabref.KeyCollisionException;
import net.sf.jabref.SidePaneComponent;
import net.sf.jabref.SidePaneManager;
import net.sf.jabref.Util;
import net.sf.jabref.undo.NamedCompound;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author mspiegel
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class CiteSeerFetcher extends SidePaneComponent {
final static String CITESEER_HOST = "citeseer.ist.psu.edu";
final static String PREFIX_URL = "http://" + CITESEER_HOST + "/";
final static String PREFIX_IDENTIFIER = "oai:CiteSeerPSU:";
final static String OAI_HOST = "http://cs1.ist.psu.edu/";
final static String OAI_URL = OAI_HOST + "cgi-bin/oai.cgi?";
final static String OAI_ACTION = "verb=GetRecord";
final static String OAI_METADATAPREFIX ="metadataPrefix=oai_citeseer";
protected SAXParserFactory parserFactory;
protected SAXParser saxParser;
boolean citationFetcherActive;
boolean importFetcherActive;
JProgressBar progressBar, progressBar2;
JLabel citeSeerProgress;
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints con = new GridBagConstraints();
SidePaneManager sidePaneManager;
public CiteSeerFetcher(SidePaneManager p0) {
super(p0, GUIGlobals.getIconUrl("citeseer"), Globals.lang("CiteSeer Transfer"));
sidePaneManager = p0;
progressBar = new JProgressBar();
progressBar2 = new JProgressBar();
citeSeerProgress = new JLabel();
progressBar.setValue(0);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setStringPainted(true);
progressBar2.setValue(0);
progressBar2.setMinimum(0);
progressBar2.setMaximum(100);
progressBar2.setStringPainted(true);
JPanel main = new JPanel();
main.setLayout(gbl);
//SidePaneHeader header = new SidePaneHeader
// ("CiteSeer Transfer", GUIGlobals.wwwCiteSeerIcon, this);
con.gridwidth = GridBagConstraints.REMAINDER;
con.fill = GridBagConstraints.BOTH;
con.weightx = 1;
con.insets = new Insets(0, 0, 2, 0);
//gbl.setConstraints(header, con);
//add(header);
con.insets = new Insets(0, 0, 0, 0);
con.fill = GridBagConstraints.HORIZONTAL;
gbl.setConstraints(progressBar, con);
main.add(progressBar);
gbl.setConstraints(progressBar2, con);
main.add(progressBar2);
gbl.setConstraints(citeSeerProgress, con);
main.add(citeSeerProgress);
main.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
add(main, BorderLayout.CENTER);
try {
citationFetcherActive = false;
importFetcherActive = false;
parserFactory = SAXParserFactory.newInstance();
saxParser = parserFactory.newSAXParser();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
/***********************************/
/* Begin Inner Class Declarations */
/* The inner classes are used to modify components, when not in the
* event-dispatching thread. These are used to follow the "single-threaded
* rule", as defined here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
*
*
* I'm pretty sure the Dialog box invokers should remain as inner classes,
* but I can't decide whether or not to break the one-thread rule for the
* progress bar classes. Because the search contains a locking-mechanism,
* activateFetcher() and deactivateFetcher(), there should only be at-most-one
* thread accessing the progress bar at any time.
*/
class ShowEmptyFetchSetDialog implements Runnable {
public void run() {
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("The CiteSeer fetch operation returned zero results"
+ "."),
"CiteSeer",
JOptionPane.INFORMATION_MESSAGE);
deactivateCitationFetcher();
}
}
public ShowEmptyFetchSetDialog getEmptyFetchSetDialog() {
return(new ShowEmptyFetchSetDialog());
}
class ShowNoConnectionDialog implements Runnable {
protected String targetURL = "";
ShowNoConnectionDialog(String URL) {
targetURL = URL;
}
public void run() {
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("Could not connect to host") + " " + targetURL + ". " +
Globals.lang("Please check your network connection to this machine" + "."),
Globals.lang("CiteSeer Error"),
JOptionPane.ERROR_MESSAGE);
}
}
class ShowBadIdentifiersDialog implements Runnable {
Hashtable<Integer, BibtexEntry> rejectedEntries;
ShowBadIdentifiersDialog(Hashtable<Integer, BibtexEntry> entries) {
rejectedEntries = entries;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
if (rejectedEntries.size() == 1) {
} else if (rejectedEntries.size() > 1) {
int i;
String rowNumbers = "";
String oneRowOfNumbers = "";
TreeSet<Integer> rowSet = new TreeSet<Integer>(rejectedEntries.keySet());
int rowSize = rowSet.size();
for(i=0; (i < rowSize - 1) && (i < 100); i++) {
Integer next = rowSet.first();
if (oneRowOfNumbers.equals(""))
oneRowOfNumbers = next.toString();
else {
oneRowOfNumbers = oneRowOfNumbers + ", ";
if (oneRowOfNumbers.length() > 50) {
oneRowOfNumbers = oneRowOfNumbers + "\n";
rowNumbers = rowNumbers + oneRowOfNumbers;
oneRowOfNumbers = "";
}
oneRowOfNumbers = oneRowOfNumbers + next.toString();
}
rowSet.remove(next);
}
rowNumbers = rowNumbers + oneRowOfNumbers;
if (i == 100) {
rowNumbers = rowNumbers + "..";
} else {
rowNumbers = rowNumbers + " "+Globals.lang("and")+" " + rowSet.first().toString();
}
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("Couldn't parse the 'citeseerurl' field of the following entries") + ':' + '\n' +
rowNumbers + ".\n" +
Globals.lang("Please refer to the JabRef help manual on using the CiteSeer tools" + '.'),
Globals.lang("Warning"),
JOptionPane.WARNING_MESSAGE);
}
}
}
class ShowBadIdentifierDialog implements Runnable {
protected String badURL = "";
protected int rowNumber;
ShowBadIdentifierDialog(String URL, int row) {
badURL = URL;
rowNumber = row;
}
public void run() {
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("Couldn't find an entry associated with this URL") + ": \"" + badURL + '\"' +
Globals.lang(" on entry number ") + (rowNumber + 1) + ". " +
Globals.lang("Please refer to the JabRef help manual on using the CiteSeer tools."),
Globals.lang("CiteSeer Error"),
JOptionPane.ERROR_MESSAGE);
}
}
class ShowBadURLDialog implements Runnable {
protected String badURL = "";
protected int rowNumber;
ShowBadURLDialog(String URL, int row) {
badURL = URL;
rowNumber = row;
}
public void run() {
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("Unable to parse the following URL") + ": \"" + badURL + '\"' +
Globals.lang(" on entry number ") + (rowNumber + 1) + ". " +
Globals.lang("Please refer to the JabRef help manual on using the CiteSeer tools."),
Globals.lang("CiteSeer Error"),
JOptionPane.ERROR_MESSAGE);
}
}
class ShowMissingURLDialog implements Runnable {
protected int rowNumber;
ShowMissingURLDialog(int row) {
rowNumber = row;
}
public void run() {
JOptionPane.showMessageDialog(panel.frame(),
Globals.lang("The URL field appears to be empty on entry number ") + (rowNumber + 1) + ". " +
Globals.lang("Please refer to the JabRef help manual on using the CiteSeer tools."),
Globals.lang("CiteSeer Error"),
JOptionPane.ERROR_MESSAGE);
}
}
class UpdateProgressBarMaximum implements Runnable {
protected int maximum;
UpdateProgressBarMaximum(int newValue) {
maximum = newValue;
}
public void run() {
progressBar.setMaximum(maximum);
}
}
class UpdateProgressBarTwoMaximum implements Runnable {
protected int maximum;
UpdateProgressBarTwoMaximum(int newValue) {
maximum = newValue;
}
public void run() {
progressBar2.setMaximum(maximum);
}
}
class InitializeProgressBar implements Runnable {
public void run() {
progressBar.setValue(0);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setString(null);
}
}
class InitializeProgressBarTwo implements Runnable {
public void run() {
progressBar2.setValue(0);
progressBar2.setMinimum(0);
progressBar2.setMaximum(100);
progressBar2.setString(null);
}
}
class UpdateProgressBarValue implements Runnable {
protected int counter;
UpdateProgressBarValue(int newValue) {
counter = newValue;
}
public void run() {
progressBar.setValue(counter);
}
}
class UpdateProgressBarTwoValue implements Runnable {
protected int counter;
UpdateProgressBarTwoValue(int newValue) {
counter = newValue;
}
public void run() {
progressBar2.setValue(counter);
}
}
class UpdateProgressStatus implements Runnable {
protected String status;
UpdateProgressStatus(String newStatus) {
status = newStatus;
}
public void run() {
citeSeerProgress.setText(status);
}
}
/* End Inner Class Declarations */
/***********************************/
synchronized public boolean activateCitationFetcher() {
if (citationFetcherActive == true) {
return(false);
} else {
citationFetcherActive = true;
return(true);
}
}
synchronized public void deactivateCitationFetcher() {
citationFetcherActive = false;
}
synchronized public boolean activateImportFetcher() {
if (importFetcherActive == true) {
return(false);
} else {
importFetcherActive = true;
return(true);
}
}
synchronized public void deactivateImportFetcher() {
importFetcherActive = false;
}
public void beginImportCiteSeerProgress() {
progressBar.setIndeterminate(true);
progressBar.setString("");
progressBar2.setVisible(false);
citeSeerProgress.setText("");
sidePaneManager.show("CiteSeerProgress");
}
public void endImportCiteSeerProgress() {
progressBar.setIndeterminate(false);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(100);
}
/**
* @param newDatabase
* @param targetDatabase
*/
public int populate(BibtexDatabase newDatabase, BibtexDatabase targetDatabase) {
int errorCode = 0;
Iterator<String> targetIterator = targetDatabase.getKeySet().iterator();
boolean abortOperation = false;
String currentKey;
BibtexEntry currentEntry;
Map<String, Boolean> citationHashTable = new HashMap<String, Boolean>();
Hashtable<Integer, BibtexEntry> rejectedEntries = new Hashtable<Integer, BibtexEntry>();
InitializeProgressBar initializeProgressBar = new InitializeProgressBar();
InitializeProgressBarTwo initializeProgressBarTwo = new InitializeProgressBarTwo();
UpdateProgressBarMaximum updateMaximum = new UpdateProgressBarMaximum(targetDatabase.getEntryCount());
progressBar2.setVisible(true);
SwingUtilities.invokeLater(initializeProgressBar);
SwingUtilities.invokeLater(initializeProgressBarTwo);
SwingUtilities.invokeLater(updateMaximum);
int identifierCounter = 0;
UpdateProgressStatus progressStatus = new UpdateProgressStatus(Globals.lang("Fetching Identifiers"));
SwingUtilities.invokeLater(progressStatus);
while (targetIterator.hasNext() && !abortOperation) {
currentKey = targetIterator.next();
currentEntry = targetDatabase.getEntryById(currentKey);
abortOperation = generateIdentifierList(currentEntry, citationHashTable, rejectedEntries);
UpdateProgressBarValue updateValue = new UpdateProgressBarValue(++identifierCounter);
SwingUtilities.invokeLater(updateValue);
}
if (rejectedEntries.size() > 0) {
errorCode = -1;
ShowBadIdentifiersDialog badIdentifiersDialog = new ShowBadIdentifiersDialog(rejectedEntries);
SwingUtilities.invokeLater(badIdentifiersDialog);
}
if (citationHashTable.size() > 0) {
UpdateProgressBarTwoMaximum update2Maximum = new UpdateProgressBarTwoMaximum(citationHashTable.size());
SwingUtilities.invokeLater(update2Maximum);
}
progressStatus = new UpdateProgressStatus(Globals.lang("Fetching Citations"));
SwingUtilities.invokeLater(progressStatus);
generateCitationList(citationHashTable, newDatabase);
progressStatus = new UpdateProgressStatus(Globals.lang("Done"));
SwingUtilities.invokeLater(progressStatus);
if (abortOperation)
errorCode = -2;
return(errorCode);
}
private Map<String, Boolean> generateCitationList(Map<String, Boolean> citationHashTable, BibtexDatabase database)
{
try {
NamedCompound dummyNamedCompound = new NamedCompound(Globals.lang("Import Data from CiteSeer Database"));
BooleanAssign dummyBoolean = new BooleanAssign(false);
if ((citationHashTable != null) && (citationHashTable.size() > 0)) {
int citationCounter=0;
for (String key : citationHashTable.keySet()){
String id = Util.createNeutralId();
BibtexEntry newEntry = new BibtexEntry(id);
StringBuffer citeseerURLString = new StringBuffer();
citeseerURLString.append(OAI_URL);
citeseerURLString.append(OAI_ACTION);
citeseerURLString.append("&" + OAI_METADATAPREFIX);
citeseerURLString.append("&" + "identifier=").append(key);
URL citeseerUrl = new URL( citeseerURLString.toString());
HttpURLConnection citeseerConnection = (HttpURLConnection)citeseerUrl.openConnection();
saxParser.parse(citeseerConnection.getInputStream(), new CiteSeerUndoHandler(dummyNamedCompound, newEntry, panel, dummyBoolean));
database.insertEntry(newEntry);
citationCounter++;
UpdateProgressBarTwoValue updateValue = new UpdateProgressBarTwoValue(citationCounter);
SwingUtilities.invokeLater(updateValue);
}
}
} catch (SAXException e) {
System.out.println("SAXException: " + e.getLocalizedMessage());
e.printStackTrace();
} catch (IOException e) {
ShowNoConnectionDialog dialog = new ShowNoConnectionDialog(OAI_HOST);
SwingUtilities.invokeLater(dialog);
} catch (KeyCollisionException e) {
System.out.println("KeyCollisionException: " + e.getLocalizedMessage());
e.printStackTrace();
}
return citationHashTable;
}
public static String generateCanonicalNumber(BibtexEntry be) {
return(generateCanonicalNumber(be.getField("citeseerurl")));
}
public static String generateCanonicalNumber(String link) {
String IDnumber = null;
if (link != null) {
Pattern pattern = Pattern.compile("[0-9]+");
Matcher matcher = pattern.matcher(link);
if (matcher.find()) {
IDnumber = matcher.group();
}
}
return IDnumber;
}
public String generateCanonicalIdentifier(BibtexEntry be) {
String canonID = null;
String IDnumber = generateCanonicalNumber(be);
if (IDnumber != null) {
canonID = PREFIX_IDENTIFIER + IDnumber;
}
return(canonID);
}
public static String generateCanonicalURL(String link) {
String canonURL = null;
String IDnumber = generateCanonicalNumber(link);
if (IDnumber != null) {
canonURL = PREFIX_URL + IDnumber + ".html";
}
return(canonURL);
}
public static String generateCanonicalURL(BibtexEntry be) {
return(generateCanonicalURL(be.getField("citeseerurl")));
}
private boolean generateIdentifierList(BibtexEntry currentEntry, Map<String, Boolean> citationHashTable, Hashtable<Integer, BibtexEntry> rejectedEntries)
{
boolean abortOperation = false;
String identifier = generateCanonicalIdentifier(currentEntry);
try {
if (identifier != null) {
StringBuffer citeseerURLString = new StringBuffer();
citeseerURLString.append(OAI_URL);
citeseerURLString.append(OAI_ACTION);
citeseerURLString.append("&" + OAI_METADATAPREFIX);
citeseerURLString.append("&" + "identifier=").append(identifier);
URL citeseerUrl = new URL( citeseerURLString.toString());
HttpURLConnection citeseerConnection = (HttpURLConnection)citeseerUrl.openConnection();
saxParser.parse(citeseerConnection.getInputStream(), new CiteSeerCitationHandler(citationHashTable));
} else {
int row = panel.mainTable.findEntry(currentEntry);
rejectedEntries.put(new Integer(row+1),currentEntry);
}
} catch (SAXException e) {
System.out.println("SAXException: " + e.getLocalizedMessage());
e.printStackTrace();
} catch (IOException e) {
System.out.println("IOException: " + e.getLocalizedMessage());
ShowNoConnectionDialog dialog = new ShowNoConnectionDialog(OAI_HOST);
abortOperation = true;
SwingUtilities.invokeLater(dialog);
}
return(abortOperation);
}
public boolean importCiteSeerEntries(int[] clickedOn, NamedCompound citeseerNamedCompound) {
boolean newValues = false;
boolean abortOperation = false;
Vector<Integer> clickedVector = new Vector<Integer>();
Hashtable<Integer, BibtexEntry> rejectedEntries = new Hashtable<Integer, BibtexEntry>();
for(int i=0; i < clickedOn.length; i++)
clickedVector.add(new Integer(clickedOn[i]));
Iterator<Integer> clickedIterator = clickedVector.iterator();
BooleanAssign overwriteAll = new BooleanAssign(false);
BooleanAssign overwriteNone = new BooleanAssign(false);
while (clickedIterator.hasNext() && !abortOperation) {
int currentIndex = clickedIterator.next().intValue();
BooleanAssign newValue = new BooleanAssign(false);
BibtexEntry be = panel.mainTable.getEntryAt(currentIndex);
abortOperation = importCiteSeerEntry(be, citeseerNamedCompound, overwriteAll, overwriteNone, newValue, rejectedEntries);
if (newValue.getValue())
newValues = true;
}
if (rejectedEntries.size() > 0) {
ShowBadIdentifiersDialog badIdentifiersDialog = new ShowBadIdentifiersDialog(rejectedEntries);
SwingUtilities.invokeLater(badIdentifiersDialog);
}
return newValues;
}
/**
* @param be
* @param overwriteNone
* @param overwriteAll
* @param rejectedEntries
*
*/
public boolean importCiteSeerEntry(BibtexEntry be, NamedCompound citeseerNC, BooleanAssign overwriteAll,
BooleanAssign overwriteNone, BooleanAssign newValue, Hashtable<Integer, BibtexEntry> rejectedEntries) {
boolean abortOperation = false;
String identifier = generateCanonicalIdentifier(be);
try {
if (identifier != null) {
StringBuffer citeseerURLString = new StringBuffer();
citeseerURLString.append(OAI_URL);
citeseerURLString.append(OAI_ACTION);
citeseerURLString.append("&" + OAI_METADATAPREFIX);
citeseerURLString.append("&" + "identifier=").append(identifier);
URL citeseerUrl = new URL( citeseerURLString.toString());
HttpURLConnection citeseerConnection = (HttpURLConnection)citeseerUrl.openConnection();
InputStream inputStream = citeseerConnection.getInputStream();
DefaultHandler handlerBase = new CiteSeerUndoHandler(citeseerNC, be, panel, newValue, overwriteAll, overwriteNone);
saxParser.parse(inputStream, handlerBase);
} else {
int row = panel.mainTable.findEntry(be);
rejectedEntries.put(new Integer(row+1), be);
}
} catch (IOException e) {
ShowNoConnectionDialog dialog = new ShowNoConnectionDialog(OAI_HOST);
SwingUtilities.invokeLater(dialog);
abortOperation = true;
} catch (SAXException e) {
System.out.println("SAXException: " + e.getLocalizedMessage());
e.printStackTrace();
abortOperation = true;
}
return abortOperation;
}
}