package net.sf.jabref.imports; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.Collection; import java.util.StringTokenizer; import javax.swing.JOptionPane; import javax.swing.JPanel; import net.sf.jabref.BibtexEntry; import net.sf.jabref.GUIGlobals; import net.sf.jabref.Globals; import net.sf.jabref.OutputPrinter; /** * This class fetches up to 200 citations from JStor by a given search query. It * communicates with jstor via HTTP and Cookies. The fetcher automates the * following steps: * <ol> * <li>Do a basic search on www.jstor.org</li> * <li>Save the first 200 hits</li> * <li>Download the saved citations as bibtex</li> * <li>Parse it with the BibtexParser</li> * <li>Import the BibtexEntrys via the ImportInspectionDialog</li> * </ol> * * @author Juliane Doege, Tobias Langner */ public class JSTORFetcher implements EntryFetcher { /** * cookies can't save more than 200 citations */ protected static int MAX_CITATIONS = 200; /** * Cookie key for Jstor ticket (authentication) */ protected static final String COOKIE_TICKET = "Jstor_Ticket"; /** * location where the ticket is obtained * */ protected static final String URL_TICKET = "http://www.jstor.org/search"; /** * Cookie key for citations to be fetched * */ protected static final String COOKIE_CITATIONS = "Jstor_citations0"; /** * location where to obtain the citations cookie * */ protected static final String URL_BIBTEX = "http://www.jstor.org/browse/citations.txt?exportFormat=bibtex&exportAction=Display&frame=noframe&dpi=3&config=jstor&viewCitations=1&View=View"; public String getHelpPage() { return "JSTOR.html"; } public URL getIcon() { return GUIGlobals.getIconUrl("www"); } public String getKeyName() { return "Search JSTOR"; } public JPanel getOptionsPanel() { // No Options panel return null; } public String getTitle() { return Globals.menuTitle("Search JSTOR"); } public void stopFetching() { // cannot be interrupted } public boolean processQuery(String query, ImportInspector dialog, OutputPrinter status) { try { // First open a ticket with JStor String ticket = openTicket(); // Then execute the query String citations = getCitations(ticket, query); // Last retrieve the Bibtex-entries of the citations found Collection<BibtexEntry> entries = getBibtexEntries(ticket, citations); if (entries.size() == 0){ status.showMessage(Globals.lang("No entries found for the search string '%0'", query), Globals.lang("Search JSTOR"), JOptionPane.INFORMATION_MESSAGE); return false; } for (BibtexEntry entry : entries){ dialog.addEntry(entry); } return true; } catch (IOException e) { status.showMessage(Globals.lang("Error while fetching from JSTOR") + ": " + e.getMessage()); } return false; } /** * Given a ticket an a list of citations, retrieve BibtexEntries from JStor * * @param ticket * A valid ticket as returned by openTicket() * @param citations * A list of citations as returned by getCitations() * @return A collection of BibtexEntries parsed from the bibtex returned by * JStor. * @throws IOException * Most probably related to a problem connecting to JStor. */ protected Collection<BibtexEntry> getBibtexEntries(String ticket, String citations) throws IOException { try { URL url = new URL(URL_BIBTEX); URLConnection conn = url.openConnection(); conn.setRequestProperty("Cookie", ticket + "; " + citations); conn.connect(); BibtexParser parser = new BibtexParser(new BufferedReader(new InputStreamReader(conn .getInputStream()))); return parser.parse().getDatabase().getEntries(); } catch (MalformedURLException e) { // Propagate... throw new RuntimeException(e); } } /** * * @return a Jstor ticket ID * @throws IOException */ protected String openTicket() throws IOException { URL url = new URL(URL_TICKET); URLConnection conn = url.openConnection(); return getCookie(COOKIE_TICKET, conn); } /** * requires a valid JStor Ticket ID * * @param query * The search term to query JStor for. * @param ticket * JStor ticket * @return cookie value of the key JSTORFetcher.COOKIE_CITATIONS. null if * search is empty or ticket is invalid * @throws IOException */ protected String getCitations(String ticket, String query) throws IOException { String urlQuery; try { urlQuery = "http://www.jstor.org/search/BasicResults?hp=" + MAX_CITATIONS + "&si=1&gw=jtx&jtxsi=1&jcpsi=1&artsi=1&Query=" + URLEncoder.encode(query, "UTF-8") + "&wc=on&citationAction=saveAll"; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } URL url = new URL(urlQuery); URLConnection conn = url.openConnection(); conn.setRequestProperty("Cookie", ticket); return getCookie(COOKIE_CITATIONS, conn); } /** * evaluates the 'Set-Cookie'-Header of a HTTP response * * @param name * key of a cookie value * @param conn * URLConnection * @return cookie value referenced by the key. null if key not found * @throws IOException */ public static String getCookie(String name, URLConnection conn) throws IOException { for (int i = 0;; i++) { String headerName = conn.getHeaderFieldKey(i); String headerValue = conn.getHeaderField(i); if (headerName == null && headerValue == null) { // No more headers break; } if (headerName != null && headerName.equals("Set-Cookie")) { if (headerValue.startsWith(name)) { // several key-value-pairs are separated by ';' StringTokenizer st = new StringTokenizer(headerValue, "; "); while (st.hasMoreElements()) { String token = st.nextToken(); if (token.startsWith(name)) { return token; } } } } } return null; } }