« - »

Feeding your own data picker (part 3)

8 March 2008

We built the RFA data picker servlet by extending our abstract data picker base code, setting some properties in the constructor and overriding the method that builds the SQL for the query. But what if your data source is not accessed via SQL? In the country code selector used on the demo page, our data came from a plain text file from the ISO web site. We already built a data source for this data using PHP. But what about Java?

We can still salvage must of the base code from our abstract class, even if we do not have an SQL-based source of data. We do this by overriding the fetchOptions() method instead of the getQueryStatement() method. Actually, because it is an abstract method, you still have to override the getQueryStatement() method as well, but it will never be called, so you can just return null. By overriding the fetchOptions() method, we can replace the standard JDBC/SQL code with code appropriate for our data source, yet still take advantage of all of the common caching and formatting code that is in the base class. The complete Java module to support the County data picker is listed here:

package org.restafarian.example.servlets;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.restafarian.core.servlets.SelectListServletBase;

/**
 * <p>Country data picker servlet.</p>
 */
public class CountrySelectorServlet extends SelectListServletBase {
  private static final long serialVersionUID = 1;
  private static final String SOURCE_URL =
       "http://www.iso.org/iso/iso3166_en_code_lists.txt";
  private static final long ONE_WEEK = 604800000L;
  private Log log = LogFactory.getLog(getClass());
  private List allOptions = null;
  private Date expirationDate = null;

  /**
   * <p>Constructs a new <code>CountrySelectorServlet</code>.</p>
   */
  public CountrySelectorServlet(){
    super();
    setContextKey("country.selector.results.cache");
    setDataFields(defineDataFields());
  }

  /**
   * <p>Builds the list of fields to include.</p>
   *
   * @return the static list of fields to include
   */
  private Map defineDataFields() {
    Map fields = new HashMap();

    fields.put("id", "id");
    fields.put("name", "name");

    return fields;
  }

  /**
   * <p>This method obtains the requested options from the data
   * source.</p>
   *
   * @param startsWith the "starts with" query parameter
   * @param contains the "contains" query parameter
   * @param orderBy the sort order
   * @return the options
   */
  protected List fetchOptions(String startsWith, String contains,
         String orderBy) {
    List options = new ArrayList();

    if (allOptions == null || new Date().after(expirationDate)) {
      allOptions = loadOptions();
    }
    if (allOptions != null) {
      if (startsWith == null) {
        startsWith = "";
      }
      if (contains == null) {
        contains = "";
      }
      String key = "id";
      if ("name".equalsIgnoreCase(orderBy)) {
        key = "name";
      }
      Map tempMap = new TreeMap();
      Iterator i = allOptions.iterator();
      while (i.hasNext()) {
        Map thisOption = (Map) i.next();
        String thisKey = (String) thisOption.get(key);
        if (startsWith.length() > 0) {
          if (thisKey.startsWith(startsWith)) {
            tempMap.put(thisKey, thisOption);
          }
        } else if (contains.length() > 0) {
          if (thisKey.indexOf(contains) != -1) {
            tempMap.put(thisKey, thisOption);
          }
        } else {
          tempMap.put(thisKey, thisOption);
        }
      }
      i = tempMap.keySet().iterator();
      while (i.hasNext()) {
        options.add(tempMap.get(i.next()));
      }
    }

    return options;
  }

  /**
   * <p>This method gets the list of countries from the ISO source.</p>
   *
   * @return the options
   */
  protected List loadOptions() {
    List options = new ArrayList();

    try {
      URL url = new URL(SOURCE_URL);
      BufferedReader input = new
           BufferedReader(new InputStreamReader(url.openStream()));
      String line = "";
      while ((line = input.readLine()) != null) {
        if (line.length() > 100 || line.length() < 4) {
          System.out.println("discarding line: " + line);
        } else {
          options.add(getOption(line));
        }
      }
    } catch (Exception e) {
      log.error("Exception encountered while attempting to load
           country codes from URL " + SOURCE_URL, e);
    }
    allOptions = options;
    expirationDate = new Date(new Date().getTime() + ONE_WEEK);

    return options;
  }

  /**
   * <p>Loads a single option.</p>
   */
  private Map getOption(String line) {
    Map option = new HashMap();

    String[] parts = line.split(";");
    option.put("name", beautifyName(parts[0]));
    option.put("id", parts[1]);

    return option;
  }

  /**
   * <p>Converts the name from all caps to upper and lower case.</p>
   */
  private String beautifyName(String name) {
    StringBuffer buffer = new StringBuffer();

    String[] parts = name.toLowerCase().split(" ");
    String separator = "";
    for (int i=0;i<parts.length;i++) {
      String thisWord = parts[i];
      if (!"and".equals(thisWord) && !"the".equals(thisWord) &&
             !"of".equals(thisWord)) {
        if (thisWord.startsWith("(")) {
          thisWord = "(" + capitalize(thisWord.substring(1));
        } else {
          thisWord = capitalize(thisWord);
        }
        if (thisWord.indexOf("D'i") > -1) {
          thisWord = thisWord.replaceAll("D'i", "d'I");
        }
        if (thisWord.indexOf("U.s.") > -1) {
          thisWord = thisWord.replaceAll("U.s.", "U.S.");
        }
      }
      buffer.append(separator);
      buffer.append(thisWord);
      separator = " ";
    }

    return buffer.toString();
  }

  /**
   * <p>This method creates the SQL statement.</p>
   *
   * @param startsWith the "starts with" query parameter
   * @param contains the "contains" query parameter
   * @param orderBy the sort order
   * @return the SQL statement
   */
  protected String getQueryStatement(String startsWith,
         String contains, String orderBy) {
    return null;
  }

  /**
   * <p>Converts the first character of the string to a caplital
   * letter.</p>
   *
   * @param string the string to capitalize
   * @return the capitalized string
   * @since Eaasy Street 2.2
   */
  private static String capitalize(String string) {
    char firstChar = Character.toUpperCase(string.charAt(0));
    return (new Character(firstChar).toString() + string.substring(1));
  }
}

Building data picker servlets for non-SQL data sources is very similar to building those that are back up by data accessed via SQL. You just have to insert your implementation-specific code a little higher up in process. Other than that, the process is virtually the same.


http://blog.restafarian.org/2008/03/feeding-your-own-data-picker-part-3/

Comments are closed.

Sorry, the comment form is closed at this time.