f6c5bb17c70a48fcaffccb14e4880cb89ee4f424
[debian/jabref.git] / src / java / net / sf / jabref / imports / GoogleScholarFetcher.java
1 /*  Copyright (C) 2003-2011 JabRef contributors.
2     This program is free software; you can redistribute it and/or modify
3     it under the terms of the GNU General Public License as published by
4     the Free Software Foundation; either version 2 of the License, or
5     (at your option) any later version.
6
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10     GNU General Public License for more details.
11
12     You should have received a copy of the GNU General Public License along
13     with this program; if not, write to the Free Software Foundation, Inc.,
14     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15 */
16 package net.sf.jabref.imports;
17
18 import net.sf.jabref.*;
19 import net.sf.jabref.gui.FetcherPreviewDialog;
20 import net.sf.jabref.net.URLDownload;
21 import net.sf.jabref.util.NameListNormalizer;
22
23 import javax.swing.*;
24 import java.io.*;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.net.URLEncoder;
28 import java.util.*;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32
33 public class GoogleScholarFetcher implements PreviewEntryFetcher {
34
35     private boolean hasRunConfig = false;
36     private boolean clearKeys = true; // Should we clear the keys so new ones can be generated?
37     protected static int MAX_ENTRIES_TO_LOAD = 50;
38     final static String QUERY_MARKER = "___QUERY___";
39     final static String URL_START = "http://scholar.google.com";
40     final static String URL_SETTING = "http://scholar.google.com/scholar_settings";
41     final static String URL_SETPREFS = "http://scholar.google.com/scholar_setprefs";
42     final static String SEARCH_URL = URL_START+"/scholar?q="+QUERY_MARKER
43             +"&hl=en&btnG=Search";
44
45     final static Pattern BIBTEX_LINK_PATTERN = Pattern.compile("<a href=\"([^\"]*)\">[A-Za-z ]*BibTeX");
46     final static Pattern TITLE_START_PATTERN = Pattern.compile("<div class=\"gs_ri\">");
47     final static Pattern LINK_PATTERN = Pattern.compile("<h3 class=\"gs_rt\"><a href=\"([^\"]*)\">");
48     final static Pattern TITLE_END_PATTERN = Pattern.compile("<div class=\"gs_fl\">");
49
50     protected HashMap<String,String> entryLinks = new HashMap<String, String>();
51     //final static Pattern NEXT_PAGE_PATTERN = Pattern.compile(
52     //        "<a href=\"([^\"]*)\"><span class=\"SPRITE_nav_next\"> </span><br><span style=\".*\">Next</span></a>");
53
54     protected boolean stopFetching = false;
55
56
57     public int getWarningLimit() {
58         return 10;
59     }
60
61     public int getPreferredPreviewHeight() {
62         return 100;
63     }
64
65     public boolean processQuery(String query, ImportInspector inspector, OutputPrinter status) {
66         return false;
67     }
68
69     public boolean processQueryGetPreview(String query, FetcherPreviewDialog preview, OutputPrinter status) {
70         entryLinks.clear();
71         stopFetching = false;
72         try {
73             if (!hasRunConfig) {
74                 runConfig();
75                 hasRunConfig = true;
76             }
77             Map<String, JLabel> citations = getCitations(query);
78             for (String link : citations.keySet()) {
79                 preview.addEntry(link, citations.get(link));
80             }
81
82             return true;
83         } catch (IOException e) {
84             e.printStackTrace();
85             status.showMessage(Globals.lang("Error fetching from Google Scholar"));
86             return false;
87         }
88     }
89
90     public void getEntries(Map<String, Boolean> selection, ImportInspector inspector) {
91         int toDownload = 0, downloaded = 0;
92         for (String link : selection.keySet()) {
93             boolean isSelected = selection.get(link);
94             if (isSelected) toDownload++;
95         }
96         if (toDownload == 0) return;
97
98         for (String link : selection.keySet()) {
99             if (stopFetching)
100                 break;
101             inspector.setProgress(downloaded, toDownload);
102             boolean isSelected = selection.get(link);
103             if (isSelected) {
104                 downloaded++;
105                 try {
106                     BibtexEntry entry = downloadEntry(link);
107                     inspector.addEntry(entry);
108                 } catch (IOException e) {
109                     e.printStackTrace();
110                 }
111             }
112         }
113
114     }
115
116
117     public String getTitle() {
118         return "Google Scholar";
119     }
120
121     public String getKeyName() {
122         return "Google Scholar";
123     }
124
125     public URL getIcon() {
126         return GUIGlobals.getIconUrl("www");
127     }
128
129     public String getHelpPage() {
130         return "GoogleScholarHelp.html";
131     }
132
133     public JPanel getOptionsPanel() {
134         return null;
135     }
136
137     public void stopFetching() {
138         stopFetching = true;
139     }
140
141
142     private void save(String filename, String content) throws IOException {
143         BufferedWriter out = new BufferedWriter(new FileWriter(filename));
144         out.write(content);
145         out.close();
146     }
147
148     protected void runConfig() throws IOException {
149         String urlQuery;
150         try {
151             URL url;
152             URLDownload ud;
153             url = new URL("http://scholar.google.com");
154             ud = new URLDownload(url);
155             ud.download();
156             url = new URL(URL_SETTING);
157             ud = new URLDownload(url);
158             ud.download();
159             //save("setting.html", ud.getStringContent());
160             String settingsPage = ud.getStringContent();
161             // Get the form items and their values from the page:
162             HashMap<String,String> formItems = getFormElements(settingsPage);
163             // Override the important ones:
164             formItems.put("scis", "yes");
165             formItems.put("scisf", "4");
166             formItems.put("num", String.valueOf(MAX_ENTRIES_TO_LOAD));
167             StringBuilder ub = new StringBuilder(URL_SETPREFS+"?");
168             for (Iterator<String> i = formItems.keySet().iterator(); i.hasNext();) {
169                 String name = i.next();
170                 ub.append(name).append("=").append(formItems.get(name));
171                 if (i.hasNext())
172                     ub.append("&");
173             }
174             ub.append("&submit=");
175             // Download the URL to set preferences:
176             URL url_setprefs = new URL(ub.toString());
177             ud = new URLDownload(url_setprefs);
178             ud.download();
179
180         } catch (UnsupportedEncodingException ex) {
181             ex.printStackTrace();
182         }
183     }
184
185     /**
186      *
187      * @param query
188      *            The search term to query Google Scholar for.
189      * @return a list of IDs
190      * @throws java.io.IOException
191      */
192     protected Map<String, JLabel> getCitations(String query) throws IOException {
193         String urlQuery;
194         LinkedHashMap<String, JLabel> res = new LinkedHashMap<String, JLabel>();
195         try {
196             urlQuery = SEARCH_URL.replace(QUERY_MARKER, URLEncoder.encode(query, "UTF-8"));
197             int count = 1;
198             String nextPage = null;
199             while (((nextPage = getCitationsFromUrl(urlQuery, res)) != null)
200                     && (count < 2)) {
201                 urlQuery = nextPage;
202                 count++;
203                 if (stopFetching)
204                     break;
205             }
206             return res;
207         } catch (UnsupportedEncodingException e) {
208             throw new RuntimeException(e);
209         }
210     }
211
212     protected String getCitationsFromUrl(String urlQuery, Map<String, JLabel> ids) throws IOException {
213         URL url = new URL(urlQuery);
214         URLDownload ud = new URLDownload(url);
215         ud.download();
216         String cont = ud.getStringContent();
217         //save("query.html", cont);
218         Matcher m = BIBTEX_LINK_PATTERN.matcher(cont);
219         int lastRegionStart = 0;
220         while (m.find()) {
221             String link = m.group(1).replaceAll("&amp;", "&");
222             String pText = null;
223             //System.out.println("regionStart: "+m.start());
224             String part = cont.substring(lastRegionStart, m.start());
225             Matcher titleS = TITLE_START_PATTERN.matcher(part);
226             Matcher titleE = TITLE_END_PATTERN.matcher(part);
227             boolean fS = titleS.find();
228             boolean fE = titleE.find();
229             //System.out.println("fs = "+fS+", fE = "+fE);
230             //System.out.println(titleS.end()+" : "+titleE.start());
231             if (fS && fE) {
232                 if (titleS.end() < titleE.start()) {
233                     pText = part.substring(titleS.end(), titleE.start());
234                 }
235                 else pText = part;
236             }
237             else
238                 pText = link;
239
240             pText = pText.replaceAll("\\[PDF\\]", "");
241             JLabel preview = new JLabel("<html>"+pText+"</html>");
242             ids.put(link, preview);
243
244             // See if we can extract the link Google Scholar puts on the entry's title.
245             // That will be set as "url" for the entry if downloaded:
246             Matcher linkMatcher = LINK_PATTERN.matcher(pText);
247             if (linkMatcher.find())
248                 entryLinks.put(link, linkMatcher.group(1));
249
250             lastRegionStart = m.end();
251         }
252
253         /*m = NEXT_PAGE_PATTERN.matcher(cont);
254         if (m.find()) {
255             System.out.println("NEXT: "+URL_START+m.group(1).replaceAll("&amp;", "&"));
256             return URL_START+m.group(1).replaceAll("&amp;", "&");
257         }
258         else*/
259         return null;
260     }
261
262     protected BibtexEntry downloadEntry(String link) throws IOException {
263         try {
264             URL url = new URL(URL_START+link);
265             URLDownload ud = new URLDownload(url);
266             ud.download();
267             String s = ud.getStringContent();
268             BibtexParser bp = new BibtexParser(new StringReader(s));
269             ParserResult pr = bp.parse();
270             if ((pr != null) && (pr.getDatabase() != null)) {
271                 Collection<BibtexEntry> entries = pr.getDatabase().getEntries();
272                 if (entries.size() == 1) {
273                     BibtexEntry entry = entries.iterator().next();
274                     if (clearKeys)
275                         entry.setField(BibtexFields.KEY_FIELD, null);
276                     // If the entry's url field is not set, and we have stored an url for this
277                     // entry, set it:
278                     if (entry.getField("url") == null) {
279                         String storedUrl = entryLinks.get(link);
280                         if (storedUrl != null)
281                             entry.setField("url", storedUrl);
282                     }
283                     return entry;
284                 }
285                 else if (entries.size() == 0) {
286                     System.out.println("No entry found! ("+link+")");
287                     return null;
288                 }
289                 else {
290                     System.out.println(entries.size()+" entries found! ("+link+")");
291                     return null;
292                 }
293             }
294             else {
295                 System.out.println("Parser failed! ("+link+")");
296                 return null;
297
298             }
299         } catch (MalformedURLException ex) {
300             ex.printStackTrace();
301             return null;
302         }
303     }
304
305
306     static Pattern inputPattern = Pattern.compile("<input type=([^ ]+) name=([^ ]+) value=([^> ]+)");
307     public static HashMap<String,String> getFormElements(String page) {
308         Matcher m = inputPattern.matcher(page);
309         HashMap<String,String> items = new HashMap<String, String>();
310         while (m.find()) {
311             String name = m.group(2);
312             if ((name.length() > 2) && (name.charAt(0) == '"')
313                     && (name.charAt(name.length()-1) == '"'))
314                 name = name.substring(1, name.length()-1);
315             String value = m.group(3);
316             if ((value.length() > 2) && (value.charAt(0) == '"')
317                     && (value.charAt(value.length()-1) == '"'))
318                 value = value.substring(1, value.length()-1);
319             items.put(name, value);
320         }
321         return items;
322     }
323 }