org.icefaces.sample.location.CityDictionary.java Source code

Java tutorial

Introduction

Here is the source code for org.icefaces.sample.location.CityDictionary.java

Source

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */
package org.icefaces.sample.location;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import java.beans.XMLDecoder;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * <p>Application-scope bean used to store static lookup information for
 * AutoComplete (selectInputText) example. Statically referenced by
 * SelectInputTextBean as the cityDictionary is rather large.</p>
 * <p>This class loads the city data from an xml data set.  Once an instance
 * is created a call to #generateCityMatches will generate a list of potential
 * matches. </p>
 *
 * @since 1.7
 */
public class CityDictionary implements Serializable {

    private static Log log = LogFactory.getLog(CityDictionary.class);

    // initialized flag, only occures once ber deployment. 
    private static boolean initialized;

    // list of cities.
    private static ArrayList cityDictionary;

    private static final String DATA_RESOURCE_PATH = "/WEB-INF/classes/org/icefaces/sample/location/resources/city.xml.zip";

    /**
     * Creates a new instnace of CityDictionary.  The city dictionary is unpacked
     * and initialized during construction.  This will result in a short delay
     * of 2-3 seconds on the server as a result of processing the large file.
     */
    public CityDictionary() {

        try {
            log.info(MessageBundleLoader.getMessage("find.initializingDictionary"));

            // initialized the bean, load xml database.
            synchronized (this) {
                init();
            }

        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error(MessageBundleLoader.getMessage("find.initializingDictionary"));
            }
        }
    }

    /**
     * Comparator utility for sorting city names.
     */
    private static final Comparator LABEL_COMPARATOR = new Comparator() {

        // compare method for city entries.
        public int compare(Object o1, Object o2) {
            SelectItem selectItem1 = (SelectItem) o1;
            SelectItem selectItem2 = (SelectItem) o2;
            // compare ignoring case, give the user a more automated feel when typing
            return selectItem1.getLabel().compareToIgnoreCase(selectItem2.getLabel());
        }
    };

    /**
     * Gets the cityDictionary of cities.
     *
     * @return cityDictionary list in sorted by city name, ascending.
     */
    public List getDictionary() {
        return cityDictionary;
    }

    /**
     * Generates a short list of cities that match the given searchWord.  The
     * length of the list is specified by the maxMatches attribute.
     *
     * @param searchWord city name to search for
     * @param maxMatches max number of possibilities to return
     * @return list of SelectItem objects which contain potential city names.
     */
    public ArrayList generateCityMatches(String searchWord, int maxMatches) {

        ArrayList matchList = new ArrayList(maxMatches);

        // ensure the autocomplete search word is present
        if ((searchWord == null) || (searchWord.trim().length() == 0)) {
            return matchList;
        }

        try {

            int insert = Collections.binarySearch(cityDictionary, new SelectItem("", searchWord), LABEL_COMPARATOR);

            // less then zero if we have a partial match
            if (insert < 0) {
                insert = Math.abs(insert) - 1;
            }

            for (int i = 0; i < maxMatches; i++) {
                // quit the match list creation if the index is larger than
                // max entries in the cityDictionary if we have added maxMatches.
                if ((insert + i) >= cityDictionary.size() || i >= maxMatches) {
                    break;
                }
                matchList.add(cityDictionary.get(insert + i));
            }
        } catch (Throwable e) {
            log.error(MessageBundleLoader.getMessage("find.findingMatches"), e);
        }
        // assign new matchList
        return matchList;
    }

    /**
     * Reads the zipped xml city cityDictionary and loads it into memory.
     *
     * @throws java.io.IOException If anything goes wrong acquiring or readying the zip file of city data.
     */
    private static void init() throws IOException {

        if (!initialized) {

            initialized = true;

            // Loading of the resource must be done the "JSF way" so that
            // it is agnostic about it's environment (portlet vs servlet).
            // First we get the resource as an InputStream
            FacesContext fc = FacesContext.getCurrentInstance();
            ExternalContext ec = fc.getExternalContext();
            InputStream is = ec.getResourceAsStream(DATA_RESOURCE_PATH);

            //is a zip file.
            ZipInputStream zipStream = new ZipInputStream(is);

            //Prime the stream by reading the first entry.  The way
            //we have it currently configured, there should only be
            //one.
            ZipEntry firstEntry = zipStream.getNextEntry();

            //Pass the ZipInputStream to the XMLDecoder so that it
            //can read in the list of cities and associated data.
            XMLDecoder xDecoder = new XMLDecoder(zipStream);
            List cityList = (List) xDecoder.readObject();

            //Close the decoder and the stream.
            xDecoder.close();
            zipStream.close();

            if (cityList == null) {
                throw new IOException();
            }

            cityDictionary = new ArrayList(cityList.size());
            City tmpCity = null;
            for (int i = 0, max = cityList.size(); i < max; i++) {
                tmpCity = (City) cityList.get(i);
                if (tmpCity != null && tmpCity.getCity() != null) {
                    cityDictionary.add(new SelectItem(tmpCity, tmpCity.getCity()));
                }
            }
            cityList.clear();
            Collections.sort(cityDictionary, LABEL_COMPARATOR);
        }

    }
}