/* * QueryArgs.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 17:02:24 +0000 (Sat, 11 Apr 2009) $ * * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts * Institute of Technology. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.dspace.search; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import javax.servlet.http.HttpServletRequest; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.sort.SortOption; import org.apache.commons.lang.StringUtils; /** * Contains the arguments for a query. Fill it out and pass to the query engine */ public class QueryArgs { // the query string private String query; // start and count defines a search 'cursor' or page // query will return 'count' hits beginning at offset 'start' private int start = 0; // default values private int pageSize = 10; private SortOption sortOption = null; private String sortOrder = SortOption.DESCENDING; /** number of metadata elements to display before truncating using "et al" */ private int etAl = ConfigurationManager.getIntProperty("webui.itemlist.author-limit"); /** * @return the number of metadata fields at which to truncate with "et al" */ public int getEtAl() { return etAl; } /** * set the number of metadata fields at which to truncate with "et al" * * @param etAl */ public void setEtAl(int etAl) { this.etAl = etAl; } /** * set the query string * * @param newQuery */ public void setQuery(String newQuery) { query = newQuery; } /** * retrieve the query string * * @return the current query string */ public String getQuery() { return query; } /** * set the offset of the desired search results, beginning with 0 ; used to * page results (the default value is 0) * * @param newStart * index of first desired result */ public void setStart(int newStart) { start = newStart; } /** * read the search's starting offset * * @return current index of first desired result */ public int getStart() { return start; } /** * set the count of hits to return; used to implement paged searching see * the initializer for the default value * * @param newSize * number of hits per page */ public void setPageSize(int newSize) { pageSize = newSize; } /** * get the count of hits to return * * @return number of results per page */ public int getPageSize() { return pageSize; } public SortOption getSortOption() { return sortOption; } public void setSortOption(SortOption sortOption) { this.sortOption = sortOption; } public String getSortOrder() { return sortOrder; } public void setSortOrder(String sortOrder) { this.sortOrder = sortOrder; } /** * Builds an advanced-query description string. * * The string is built using the passed in values * query{1,2,3}, field{1,2,3} and conjunction{1,2} taken from * the parameter request. * * @param request the request object to take the values from * * @return the query description string built */ public String buildQuery(HttpServletRequest request) { String newquery = "("; String numFieldStr = request.getParameter("num_search_field"); // for backward compatibility if (numFieldStr == null) numFieldStr ="3"; int numField = Integer.parseInt(numFieldStr); ArrayList query = new ArrayList(); ArrayList field = new ArrayList(); ArrayList conjunction = new ArrayList(); for (int i = 1; i <= numField; i++) { String tmp_query = request.getParameter("query"+i); String tmp_field = request.getParameter("field"+i); // TODO: Ensure a valid field from config // Disarm fields with regexp control characters if (tmp_field != null) { tmp_field = tmp_field.replace('/', ' '); tmp_field = tmp_field.replace('<', ' '); tmp_field = tmp_field.replace('\\', ' '); tmp_field = tmp_field.replace(':', ' '); } if (tmp_query != null && !tmp_query.equals("")) { query.add(tmp_query.trim()); if (tmp_field == null) field.add("ANY"); else field.add(tmp_field.trim()); if (i != numField) { conjunction.add(request.getParameter("conjunction"+i) != null? request.getParameter("conjunction"+i):"AND"); } } } Iterator iquery = query.iterator(); Iterator ifield = field.iterator(); Iterator iconj = conjunction.iterator(); String conj_curr = ""; while (iquery.hasNext()) { newquery = newquery + conj_curr; String query_curr = (String) iquery.next(); String field_curr = (String) ifield.next(); newquery = newquery + buildQueryPart(query_curr,field_curr); if (iconj.hasNext()) { conj_curr = " " + (String)iconj.next() + " "; } } newquery = newquery + ")"; return (newquery); } /** * Builds a query-part using the field and value passed in * with ' --> " (single to double quote) translation. * * @param myquery the value the query will look for * @param myfield the field myquery will be looked for in * * @return the query created */ private String buildQueryPart(String myquery, String myfield) { StringBuilder newQuery = new StringBuilder(); newQuery.append("("); boolean newTerm = true; boolean inPhrase = false; char phraseChar = '\"'; StringTokenizer qtok = new StringTokenizer(myquery, " \t\n\r\f\"\'", true); while (qtok.hasMoreTokens()) { String token = qtok.nextToken(); if (StringUtils.isWhitespace(token)) { if (!inPhrase) { newTerm = true; } newQuery.append(token); } else { // Matched the end of the phrase if (inPhrase && token.charAt(0) == phraseChar) { newQuery.append("\""); inPhrase = false; } else { // If we aren't dealing with a new term, and have a single quote // don't touch it. (for example, the apostrophe in it's). if (!newTerm && token.charAt(0) == '\'') { newQuery.append(token); } else { // Treat - my"phrased query" - as - my "phrased query" if (!newTerm && token.charAt(0) == '\"') { newQuery.append(" "); newTerm = true; } // This is a new term in the query (ie. preceeded by nothing or whitespace) // so apply a field restriction if specified if (newTerm && !myfield.equals("ANY")) { newQuery.append(myfield).append(":"); } // Open a new phrase, and closing at the corresponding character // ie. 'my phrase' or "my phrase" if (token.charAt(0) == '\"' || token.charAt(0) == '\'') { newQuery.append("\""); inPhrase = true; newTerm = false; phraseChar = token.charAt(0); } else { newQuery.append(token); newTerm = false; } } } } } newQuery.append(")"); return newQuery.toString(); } /** * Constructs a HashMap with the keys field{1,2,3}, query{1,2,3} and * conjunction{1,2} taking the values from the passed-in argument * defaulting to "". * * @param request the request-describing object to take the values from * * @return the created HashMap */ public HashMap buildQueryHash(HttpServletRequest request) { HashMap queryHash = new HashMap(); String numFieldStr = request.getParameter("num_search_field"); // for backward compatibility if (numFieldStr == null) numFieldStr = "3"; int numField = Integer.parseInt(numFieldStr); for (int i = 1; i < numField; i++) { queryHash.put("query"+i, (request.getParameter("query"+i) == null) ? "" : request.getParameter("query"+i)); queryHash.put("field"+i, (request.getParameter("field"+i) == null) ? "ANY" : request .getParameter("field"+i)); queryHash.put("conjunction"+i, (request.getParameter("conjunction"+i) == null) ? "AND" : request.getParameter("conjunction"+i)); } queryHash.put("query"+numField, (request.getParameter("query"+numField) == null) ? "" : request.getParameter("query"+numField)); queryHash.put("field"+numField, (request.getParameter("field"+numField) == null) ? "ANY" : request.getParameter("field"+numField)); return (queryHash); } /** * Builds an HTTP query string for some parameters with the value * taken from the request context passed in. * * The returned string includes key/value pairs in the HTTP query string * format (key1=value1&key2=value2...) for the keys query{1,2,3}, * field{1,2,3} and conjunction{1,2} with values taken from request * and defaulting to "". * <P> * Note, that the values are url-encoded using the UTF-8 encoding scheme * as the corresponding W3C recommendation states. * <P> * Also note that neither leading ? (question mark) * nor leading & (ampersand mark) is included. * Take this into account when appending to a real URL. * * @param request the request object to take the values from * * @return the query string that can be used without further * transformationin URLs * */ public String buildHTTPQuery(HttpServletRequest request) throws UnsupportedEncodingException { String querystring = ""; HashMap queryHash = buildQueryHash(request); Iterator i = queryHash.keySet().iterator(); while (i.hasNext()) { String key = (String) i.next(); String value = (String) queryHash.get(key); querystring = querystring + "&" + key + "=" + URLEncoder.encode(value, Constants.DEFAULT_ENCODING); } if (request.getParameter("num_search_field") != null) { querystring = querystring + "&num_search_field="+request.getParameter("num_search_field"); } // return the result with the leading "&" removed return (querystring.substring(1)); } }