- Some Google in Your Apps
- Getting Started
- A .NET Google Fight
- Viewing Cached Pages in Java
- Displaying a Cached Page
- Just That Easy
Viewing Cached Pages in Java
For the Java example, let's delve a little deeper into the Google API with an application that presents the user with search results in a list, providing the ability to restrict the search between a set of dates. In addition, the user will be able to double-click any of the results and view a cached version of the page. This example uses another simple GUI (see Figure 3), this time written in Swing.
Figure 3 Our simple search GUI.
Figure 4 shows an example of a simple search.
Figure 4 A search and some results.
Now let's take a look at the code to run the search:
protected void doSearch() { String searchString = getSearchBox().getText(); int startJulDate = -1; int endJulDate = -1; DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); Date startDate = null; Date endDate = null; Calendar startCal = null; Calendar endCal = null; int month = -1; int day = -1; int year = -1; //Returns true if the checkbox is ticked if(getUseDates()) { //Attempt to parse the provided dates try { startDate = dateFormat.parse(getFromDateBox().getText()); } catch(Exception e) { error("The start date entered (" + getFromDateBox().getText() + ") is not a valid date"); } try { endDate = dateFormat.parse(getToDateBox().getText()); } catch(Exception e) { error("The end date entered (" + getToDateBox().getText() + ") is not a valid date"); } //If we were able to parse the dates, append them onto the search if((startDate != null) && (endDate != null)) { //Google requires that all dates be in the Julian // form, i.e. days since 4713 B.C. startCal = new GregorianCalendar(); startCal.setTime(startDate); endCal = new GregorianCalendar(); endCal.setTime(endDate); startJulDate = getJulian(startCal.get(Calendar.YEAR), startCal.get(Calendar.MONTH)+1, startCal.get(Calendar.DAY_OF_MONTH)); endJulDate = getJulian(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH)+1, endCal.get(Calendar.DAY_OF_MONTH)); //Use the "daterange:" keyword to searchString += " daterange:" + startJulDate + "-" + endJulDate; } else { return; } } try { //Run the search and save the results getSearch().setQueryString(searchString); setLastSearchString(searchString); setLastResult(getSearch().doSearch()); //Update the list getResultsList().setListData(getLastResult().getResultElements()); setSearchIndex(10); } catch(Exception e) { error("Exception from Google: " + e); } }
There's a lot going on here, including some logic to parse the dates and convert them to Julian format. A Julian counts the number of days since January 1, 4713 B.C. Converting a Gregorian date to Julian is somewhat complicated, but the formula is readily available online. Here's a method that does it:
public int getJulian(int year, int month, int date) { int a, b, c, e, f; a = year / 100; b = a / 4; c = 2 - a + b; e = (int)(365.25 * (year + 4716)); f = (int)(30.6001 * (month + 1)); return (int)(c + date + e + f - 1524.5); }
Once you have the numeric representation of the date, you can just concatenate daterange: startDate-endDate onto the end of your search string.
Let's go back to the doSearch() method. The last try/catch block is where much of the work happens:
try { //Run the search and save the results getSearch().setQueryString(searchString); setLastSearchString(searchString); setLastResult(getSearch().doSearch()); //Update the list getResultsList().setListData(getLastResult().getResultElements()); setSearchIndex(10); } catch(Exception e) { error("Exception from Google: " + e); }
The getSearch() method returns the global GoogleSearch instance, which encapsulates the Google API calls. The query string in this case is the text that was entered in the text box, plus the optional date qualification. We set the string in the GoogleSearch instance; we also store it in a global variable, as we may need it later. The doSearch() method performs the search, returning a GoogleSearchResult object that contains an array of GoogleSearchResultElements we can drop directly into our JList. By default, the JList displays an ugly textual representation of the GoogleSearchResultElement, but creating a custom ListCellRenderer will display the URL instead.
The final task is to store the index of the last GoogleSearchResultElement that's displayed, in this case 10. We'll need to start here to retrieve the next set of search results if the user clicks the >>> button. As mentioned earlier, you can only retrieve 10 search results at a time; retrieving the next 10 requires another search. This is the method that the >>> button calls when clicked:
protected void shiftRight() { try { //Search with the last string getSearch().setQueryString(getLastSearchString()); //Indicate to start at the last result getSearch().setStartResult(getSearchIndex()); setLastResult(getSearch().doSearch()); getResultsList().setListData(getLastResult().getResultElements()); setSearchIndex(getLastResult().getEndIndex()); } catch(Exception e) { error("Exception from Google: " + e); } }
We're using the same search string here as before, but now we're specifying setStartResult() to be the value of the last index of the last search, so our new set of results will begin with the next result. We update the JList again with the new results, and set our new index. The shiftLeft() method looks very similar, subtracting 10 from the index before running the search.
If you're familiar with Swing, you know that its single-threaded nature can make your GUI stop responding when something is happening in the background. Since the Google searches generally take a second or two to complete, you'd do well to use something like XXX!!! to handle multithreading, to prevent your application from hanging while the search is in process. Additionally, a simple cache of GoogleSearchResultElements would make navigating through the list much faster.