/** * '$RCSfile$' * Copyright: 2005 University of New Mexico and the * Regents of the University of California and the * National Center for Ecological Analysis and Synthesis * * '$Author: costa $' * '$Date: 2006-06-14 15:35:26 +0000 (Wed, 14 Jun 2006) $' * '$Revision: 3008 $' * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.ucsb.nceas.metacat.advancedsearch; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import edu.ucsb.nceas.metacat.client.*; import edu.ucsb.nceas.utilities.*; /** * @author dcosta * * AdvancedSearch class constructs queries that use the pathquery feature of * Metacat. It can execute either an advanced search, where the user fills in * fields in a web form, or a simple search on a string. */ public class AdvancedSearch { // Object variables private AdvancedSearchBean advancedSearchBean = null; private String caseSensitive; private final String globalOperator; private boolean hasSubjectSearch = false; private boolean hasAuthorSearch = false; private boolean hasSpatialSearch = false; private boolean hasTaxonomicSearch = false; private boolean hasTemporalSearch = false; private boolean hasSiteFilter = false; private int indentLevel = 2; private boolean isCaseSensitive = false; private AdvancedSearchPathQuery pathQuery; private AdvancedSearchQueryGroup queryGroup; private String queryString; private String site; private final String title = "Advanced Search"; /** * Constructor. Used when the user has filled in an Advanced Search form. * The property values are contained in the advancedSearchBean object. * For a simple search, the advancedSearchBean object is passed as null. * * @param advancedSearchBean An AdvancedSearch bean. */ public AdvancedSearch(final AdvancedSearchBean advancedSearchBean) { int allAny = AdvancedSearchBean.MATCH_ALL; String indent = getIndent(indentLevel * 1); if (advancedSearchBean != null) { allAny = advancedSearchBean.getFormAllAny(); this.isCaseSensitive = advancedSearchBean.isCaseSensitive(); site = advancedSearchBean.getSiteValue(); } if (allAny == AdvancedSearchBean.MATCH_ALL) { globalOperator = "INTERSECT"; } else { globalOperator = "UNION"; } if (isCaseSensitive == true) { this.caseSensitive = "true"; } else { this.caseSensitive = "false"; } this.queryGroup = new AdvancedSearchQueryGroup(globalOperator, indent); this.pathQuery = new AdvancedSearchPathQuery(title, queryGroup, indent); this.advancedSearchBean = advancedSearchBean; } /** * Adds a string to an ArrayList of terms. An auxiliary method to the * parseTermsAdvanced() method. * * @param terms ArrayList of strings. * @param term the new string to add to the ArrayList, but only if * it isn't an empty string. */ private void addTerm(ArrayList terms, final StringBuffer term) { final String s = term.toString().trim(); if (s.length() > 0) { terms.add(s); } } /** * A full subject query searches the title, abstract, and keyword sections of * the document. Individual searches on these sections is also supported. */ private void buildQuerySubject() { int allAny = advancedSearchBean.getSubjectAllAny(); String emlField; String indent; final String innerOperator = "UNION"; AdvancedSearchQueryGroup innerQuery = null; final String outerOperator; AdvancedSearchQueryGroup outerQuery; AdvancedSearchQueryTerm qt; String searchMode; final String subjectField = advancedSearchBean.getSubjectField(); final int subjectQueryType = advancedSearchBean.getSubjectQueryType(); String term; ArrayList terms; String value = advancedSearchBean.getSubjectValue(); if ((value != null) && (!(value.equals("")))) { hasSubjectSearch = true; if (allAny == AdvancedSearchBean.MATCH_ALL) { outerOperator = "INTERSECT"; } else { outerOperator = "UNION"; } indent = getIndent(indentLevel * 2); outerQuery = new AdvancedSearchQueryGroup(outerOperator, indent); terms = parseTermsAdvanced(value); searchMode = metacatSearchMode(subjectQueryType); for (int i = 0; i < terms.size(); i++) { term = (String) terms.get(i); indent = getIndent(indentLevel * 3); innerQuery = new AdvancedSearchQueryGroup(innerOperator, indent); indent = getIndent(indentLevel * 4); if (subjectField.equals("ALL") || subjectField.equals("TITLE")) { emlField = "dataset/title"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, term, indent); innerQuery.addQueryTerm(qt); } if (subjectField.equals("ALL") || subjectField.equals("ABSTRACT")) { emlField = "dataset/abstract/para"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, term, indent); innerQuery.addQueryTerm(qt); emlField = "dataset/abstract/section/para"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, term, indent); innerQuery.addQueryTerm(qt); } if (subjectField.equals("ALL") || subjectField.equals("KEYWORDS")) { emlField = "keywordSet/keyword"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, term, indent); innerQuery.addQueryTerm(qt); } outerQuery.addQueryGroup(innerQuery); } // Minimize the number of query groups that get created, depending on // which criteria the user specified. // if (terms.size() > 1) { queryGroup.addQueryGroup(outerQuery); } else if (terms.size() == 1){ queryGroup.addQueryGroup(innerQuery); } } } /** * An author query will search the creator/individualName/surName field, the * creator/organizationName field, or an intersection of both fields. */ private void buildQueryAuthor() { boolean addQueryGroup = false; final int creatorSurnameQueryType = advancedSearchBean.getCreatorSurnameQueryType(); final int creatorOrganizationQueryType = advancedSearchBean.getCreatorOrganizationQueryType(); String emlField; String indent = getIndent(indentLevel * 2); AdvancedSearchQueryGroup qg = new AdvancedSearchQueryGroup(globalOperator, indent); AdvancedSearchQueryTerm qt; String searchMode; String value = advancedSearchBean.getCreatorSurname(); indent = getIndent(indentLevel * 3); if ((value != null) && (!(value.equals("")))) { emlField = "dataset/creator/individualName/surName"; searchMode = metacatSearchMode(creatorSurnameQueryType); qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, value, indent); qg.addQueryTerm(qt); addQueryGroup = true; } value = advancedSearchBean.getCreatorOrganization(); if ((value != null) && (!(value.equals("")))) { emlField = "creator/organizationName"; searchMode = metacatSearchMode(creatorOrganizationQueryType); qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, value, indent); qg.addQueryTerm(qt); addQueryGroup = true; } if (addQueryGroup) { hasAuthorSearch = true; queryGroup.addQueryGroup(qg); } } /** * Two kinds of spatial searches are supported. The first is on a specific * named location. The second is on north/south/east/west bounding * coordinates. An intersection of both searches is done if the user * specifies search values for a named location as well as one or more * bounding coordinates. */ private void buildQuerySpatialCriteria() { boolean addBoundingValues = false; boolean addGeographicDescription = false; final boolean boundaryContained = advancedSearchBean.isBoundaryContained(); String emlField; final String operator = "INTERSECT"; String indent = getIndent(indentLevel * 2); AdvancedSearchQueryGroup qgBounding; AdvancedSearchQueryGroup qgGeographicDescription; AdvancedSearchQueryGroup qgSpatial; AdvancedSearchQueryTerm qt; String searchMode; String value, northValue, southValue, eastValue, westValue; qgSpatial = new AdvancedSearchQueryGroup(globalOperator, indent); indent = getIndent(indentLevel * 3); qgBounding = new AdvancedSearchQueryGroup(operator, indent); qgGeographicDescription = new AdvancedSearchQueryGroup(operator, indent); indent = getIndent(indentLevel * 4); /* Check whether user specified a named location. */ value = advancedSearchBean.getLocationName(); if ((value != null) && (!(value.equals("")))) { searchMode = "contains"; emlField = "geographicCoverage/geographicDescription"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, value, indent); qgGeographicDescription.addQueryTerm(qt); addGeographicDescription = true; } /* * If the user selects the boundaryContained checkbox, use the following * logical expression. N, S, E, and W are the boundaries of the bounding * box, while N', S', E', and W' are the boundaries specified in a given * EML document: * (N' <= N) && (S' >= S) && (E' <= E) && (W' >= W) */ if (boundaryContained) { northValue = advancedSearchBean.getNorthBound(); if ((northValue != null) && (!(northValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/northBoundingCoordinate"; searchMode = "less-than-equals"; qt=new AdvancedSearchQueryTerm(searchMode,caseSensitive,emlField, northValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } southValue = advancedSearchBean.getSouthBound(); if ((southValue != null) && (!(southValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/southBoundingCoordinate"; searchMode = "greater-than-equals"; qt=new AdvancedSearchQueryTerm(searchMode,caseSensitive,emlField, southValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } eastValue = advancedSearchBean.getEastBound(); if ((eastValue != null) && (!(eastValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/eastBoundingCoordinate"; searchMode = "less-than-equals"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, eastValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } westValue = advancedSearchBean.getWestBound(); if ((westValue != null) && (!(westValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/westBoundingCoordinate"; searchMode = "greater-than-equals"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, westValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } } /* * Else, if the user does not select the boundaryContained checkbox, use the * following logical expression. N, S, E, and W are the boundaries of the * bounding box, while N', S', E', and W' are the boundaries specified in a * given EML document: * (N' > S) && (S' < N) && (E' > W) && (W' < E) */ else { northValue = advancedSearchBean.getNorthBound(); if ((northValue != null) && (!(northValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/southBoundingCoordinate"; searchMode = "less-than"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, northValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } southValue = advancedSearchBean.getSouthBound(); if ((southValue != null) && (!(southValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/northBoundingCoordinate"; searchMode = "greater-than"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, southValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } eastValue = advancedSearchBean.getEastBound(); if ((eastValue != null) && (!(eastValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/westBoundingCoordinate"; searchMode = "less-than"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, eastValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } westValue = advancedSearchBean.getWestBound(); if ((westValue != null) && (!(westValue.equals("")))) { emlField = "geographicCoverage/boundingCoordinates/eastBoundingCoordinate"; searchMode = "greater-than"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, westValue, indent); qgBounding.addQueryTerm(qt); addBoundingValues = true; } } // Minimize the number of query groups that get created, depending on // which criteria the user specified. // if (addBoundingValues || addGeographicDescription) { hasSpatialSearch = true; if (addBoundingValues && addGeographicDescription) { qgSpatial.addQueryGroup(qgBounding); qgSpatial.addQueryGroup(qgGeographicDescription); queryGroup.addQueryGroup(qgSpatial); } else if (addBoundingValues) { queryGroup.addQueryGroup(qgBounding); } else { queryGroup.addQueryGroup(qgGeographicDescription); } } } /** * Two kinds of temporal searches are supported. The first is on a named * time scale. The second is on a specific start date and/or end date. */ private void buildQueryTemporalCriteria() { boolean addQueryGroup = false; boolean addQueryGroupDates = false; boolean addQueryGroupNamed = false; final String dateField = advancedSearchBean.getDateField(); String emlField; final String operator = "INTERSECT"; String indent = getIndent(indentLevel * 2); final int namedTimescaleQueryType = advancedSearchBean.getNamedTimescaleQueryType(); AdvancedSearchQueryGroup qg= new AdvancedSearchQueryGroup(operator, indent); AdvancedSearchQueryGroup qgNamed, qgDates, qgDatesStart, qgDatesEnd; AdvancedSearchQueryTerm qt; String searchMode; final String namedTimescale, startDate, endDate; indent = getIndent(indentLevel * 3); namedTimescale = advancedSearchBean.getNamedTimescale(); startDate = advancedSearchBean.getStartDate(); endDate = advancedSearchBean.getEndDate(); /* If the user specified a named timescale, check to see whether it occurs * in any of three possible places: singleDateTime, beginDate, or endDate. */ qgNamed = new AdvancedSearchQueryGroup("UNION", indent); if ((namedTimescale != null) && (!(namedTimescale.equals("")))) { indent = getIndent(indentLevel * 4); searchMode = metacatSearchMode(namedTimescaleQueryType); emlField = "temporalCoverage/singleDateTime/alternativeTimeScale/timeScaleName"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, namedTimescale, indent); qgNamed.addQueryTerm(qt); emlField = "temporalCoverage/rangeOfDates/beginDate/alternativeTimeScale/timeScaleName"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, namedTimescale, indent); qgNamed.addQueryTerm(qt); emlField = "temporalCoverage/rangeOfDates/endDate/alternativeTimeScale/timeScaleName"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, namedTimescale, indent); qgNamed.addQueryTerm(qt); addQueryGroupNamed = true; } qgDates = new AdvancedSearchQueryGroup("INTERSECT", indent); // If a start date was specified, search for temporal coverage and/or a // pubDate greater than or equal to the start date. // if ((startDate != null) && (!(startDate.equals("")))) { indent = getIndent(indentLevel * 4); qgDatesStart = new AdvancedSearchQueryGroup("UNION", indent); indent = getIndent(indentLevel * 5); searchMode = "greater-than-equals"; if (dateField.equals("ALL") || dateField.equals("COLLECTION")) { emlField = "temporalCoverage/rangeOfDates/beginDate/calendarDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, startDate, indent); qgDatesStart.addQueryTerm(qt); emlField = "temporalCoverage/singleDateTime/calendarDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, startDate, indent); qgDatesStart.addQueryTerm(qt); } if (dateField.equals("ALL") || dateField.equals("PUBLICATION")) { emlField = "dataset/pubDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, startDate, indent); qgDatesStart.addQueryTerm(qt); } qgDates.addQueryGroup(qgDatesStart); addQueryGroupDates = true; } // If an end date was specified, search for temporal coverage and/or a // pubDate less than or equal to the end date. // if ((endDate != null) && (!(endDate.equals("")))) { indent = getIndent(indentLevel * 4); qgDatesEnd = new AdvancedSearchQueryGroup("UNION", indent); indent = getIndent(indentLevel * 5); searchMode = "less-than-equals"; if (dateField.equals("ALL") || dateField.equals("COLLECTION")) { emlField = "temporalCoverage/rangeOfDates/endDate/calendarDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, endDate, indent); qgDatesEnd.addQueryTerm(qt); emlField = "temporalCoverage/singleDateTime/calendarDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, endDate, indent); qgDatesEnd.addQueryTerm(qt); } if (dateField.equals("ALL") || dateField.equals("PUBLICATION")) { emlField = "dataset/pubDate"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, endDate, indent); qgDatesEnd.addQueryTerm(qt); } qgDates.addQueryGroup(qgDatesEnd); addQueryGroupDates = true; } if (addQueryGroupNamed) { qg.addQueryGroup(qgNamed); addQueryGroup = true; } if (addQueryGroupDates) { qg.addQueryGroup(qgDates); addQueryGroup = true; } if (addQueryGroup) { hasTemporalSearch = true; queryGroup.addQueryGroup(qg); } } /** * A taxon query searches the taxonomicClassification/taxonRankValue field, * matching the field if the user-specified value is contained in the field. */ private void buildQueryTaxon() { boolean addQueryGroup = false; final String emlField; String indent = getIndent(indentLevel * 2); final String operator = "INTERSECT"; AdvancedSearchQueryGroup qg= new AdvancedSearchQueryGroup(operator, indent); AdvancedSearchQueryTerm qt; final String searchMode; int taxonQueryType = advancedSearchBean.getTaxonQueryType(); final String value = advancedSearchBean.getTaxon(); indent = getIndent(indentLevel * 3); if ((value != null) && (!(value.equals("")))) { emlField = "taxonomicClassification/taxonRankValue"; searchMode = metacatSearchMode(taxonQueryType); qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, value, indent); qg.addQueryTerm(qt); addQueryGroup = true; } if (addQueryGroup) { hasTaxonomicSearch = true; queryGroup.addQueryGroup(qg); } } /** * Build a site filter. If the AdvancedSearch's site value is non-null, add a * query group that limits the results to a particular LTER site. Do this * by searching for a packageId attribute that starts with "knb-lter-xyz" * where "xyz" is the three-letter site acronym, or for a site keyword * phrase (e.g. "Kellogg Biological Station") anywhere in the documment. */ private void buildSiteFilter() { String attributeValue = ""; String emlField = ""; String indent = getIndent(indentLevel * 2); final LTERSite lterSite = new LTERSite(site); final String operator = "UNION"; AdvancedSearchQueryGroup qg= new AdvancedSearchQueryGroup(operator, indent); AdvancedSearchQueryTerm qt; String searchMode; final String siteKeyword; if (lterSite.isValidSite()) { hasSiteFilter = true; indent = getIndent(indentLevel * 3); // Handle some LTER sites with irregular naming conventions in their EML // For CAP and CWT, we need to search on the system attribute rather than // the packageId attribute // if (site.equals("CAP") || site.equals("CWT")) { emlField = "/eml/@system"; attributeValue = lterSite.getSystem(); } else { // For other LTER sites, search the packageId emlField = "/eml/@packageId"; attributeValue = lterSite.getPackageId(); } searchMode = "starts-with"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, attributeValue, indent); qg.addQueryTerm(qt); // Search for site keyword phrase siteKeyword = lterSite.getSiteKeyword(); emlField = ""; searchMode = "contains"; qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, siteKeyword, indent); qg.addQueryTerm(qt); queryGroup.addQueryGroup(qg); } } /** * Counts the number of search types on the form. For example, if the user * has filled in values for both a subject search and a spatial search, * would return 2. * * @return searchTypes An integer representing the total number of searches * that the user has filled in for this advanced search. */ private int countSearchTypes () { int searchTypes = 0; if (hasSubjectSearch == true) { searchTypes++; } if (hasAuthorSearch == true) { searchTypes++; } if (hasSpatialSearch == true) { searchTypes++; } if (hasTaxonomicSearch == true) { searchTypes++; } if (hasTemporalSearch == true) { searchTypes++; } if (hasSiteFilter == true) { searchTypes++; } return searchTypes; } /** * Builds and runs an advanced search, returning HTML result string. * * @param metacatURL URL to the metacat servlet * @param metacat A metacat client object, possible null. * @param qformat The qformat (skin) to use when displaying results. * @param xslPath File path to the resultset.xsl stylesheet. * @return htmlString HTML string representation of the search results. */ public String executeAdvancedSearch(final String metacatURL, final Metacat metacat, final String qformat, final String xslPath) { String htmlString = ""; int searchTypes; buildQuerySubject(); buildQueryAuthor(); buildQueryTaxon(); buildQuerySpatialCriteria(); buildQueryTemporalCriteria(); buildSiteFilter(); // Count the number of search types the user has entered. searchTypes = countSearchTypes(); // If the user has entered values for only one type of search criteria, // then optimize the search by setting the QueryGroup object's // includeOuterQueryGroup to false. This will strip off the outer query // group and result in a more simplified SQL statement. // if (searchTypes == 1) { queryGroup.setIncludeOuterQueryGroup(false); } queryString = pathQuery.toString(); htmlString = this.runQuery(metacatURL, metacat, qformat, xslPath); return htmlString; } /** * Builds and runs a simple search, returning HTML result string. * For a simple search, the AdvancedSearchBean object can be null because * all we need is a string value to search on. * * @param metacatURL URL to the metacat servlet * @param metacat A metacat client object, possible null. * @param qformat The qformat (skin) to use when displaying results. * @param xslPath File path to the resultset.xsl stylesheet. * @param value String value to search on. * * @return htmlString HTML string representation of the search results. */ public String executeSearch(final String metacatURL, final Metacat metacat, final String qformat, final String xslPath, String value) { String emlField = ""; String htmlString = ""; String indent = getIndent(indentLevel * 2); AdvancedSearchQueryTerm qt; String searchMode = "contains"; indent = getIndent(indentLevel * 3); /* Check whether user specified an empty search string. */ if ((value == null) || (value.equals(""))) { value = "%"; } qt = new AdvancedSearchQueryTerm(searchMode, caseSensitive, emlField, value, indent); queryGroup.addQueryTerm(qt); queryString = pathQuery.toString(); htmlString = this.runQuery(metacatURL, metacat, qformat, xslPath); return htmlString; } /** * Returns a string of spaces that corresponds to the current indent level. * * @param indentLevel The number of spaces to be indented. * @return A string containing indentLevel number of spaces. */ private String getIndent(final int indentLevel) { StringBuffer indent = new StringBuffer(12); for (int i = 0; i < indentLevel; i++) { indent.append(" "); } return indent.toString(); } /** * Given a AdvancedSearchBean query type, return the corresponding Metacat * searchmode string. * * @param queryType An int indicating the query type as specified in * the AdvancedSearchBean class. * @return searchmode A string, the Metacat search mode value. */ private String metacatSearchMode(final int queryType) { final String searchMode; switch (queryType) { case AdvancedSearchBean.CONTAINS: searchMode = "contains"; break; case AdvancedSearchBean.EXACT_MATCH: searchMode = "equals"; break; case AdvancedSearchBean.STARTS_WITH: searchMode = "starts-with"; break; case AdvancedSearchBean.ENDS_WITH: searchMode = "ends-with"; break; default: searchMode = "contains"; break; } return searchMode; } /** * Parses search terms from a string. In this simple implementation, the * string is considered to be a list of tokens separated by spaces. The more * advanced implementation (parserTermsAdvanced) parses quoted strings * containing spaces as a term. This method can be eliminated if we are * satisfied that parseTermsAdvanced() is working properly. * * @param value The string value as entered by the user. * * @return terms An ArrayList of String objects. Each space-separated * token is a single term. * private ArrayList parseTerms(final String value) { StringTokenizer st; ArrayList terms = new ArrayList(); String token; final int tokenCount; st = new StringTokenizer(value, " "); tokenCount = st.countTokens(); for (int i = 0; i < tokenCount; i++) { token = st.nextToken(); terms.add(token); } return terms; } */ /** * Parses search terms from a string. In this advanced implementation, * double-quoted strings that contain spaces are considered a single term. * * @param value The string value as entered by the user. * * @return terms An ArrayList of String objects. Each string is a term. */ private ArrayList parseTermsAdvanced(String value) { char c; StringBuffer currentTerm = new StringBuffer(100); boolean keepSpaces = false; final int stringLength; ArrayList terms = new ArrayList(); value = value.trim(); stringLength = value.length(); for (int i = 0; i < stringLength; i++) { c = value.charAt(i); if (c == '\"') { // Termination of a quote-enclosed term. Add the current term to the // list and start a new term. if (keepSpaces) { addTerm(terms, currentTerm); currentTerm = new StringBuffer(100); } keepSpaces = !(keepSpaces); // Toggle keepSpaces to its opposite value. } else if (c == ' ') { // If we are inside a quote-enclosed term, append the space. if (keepSpaces) { currentTerm.append(c); } // Else, add the current term to the list and start a new term. else { addTerm(terms, currentTerm); currentTerm = new StringBuffer(100); } } else { // Append any non-quote, non-space characters to the current term. currentTerm.append(c); } } // Add the final term to the list. addTerm(terms, currentTerm); return terms; } /** * Runs the Metacat query for a browse search, simple search, or advanced * search. * * @param metacatURL URL to the metacat servlet * @param metacat A metacat client object, possible null. * @param qformat The qformat (skin) to use when displaying results. * @param xslPath File path to the resultset.xsl stylesheet. * @return htmlString HTML string representation of the search results. */ private String runQuery(final String metacatURL, Metacat metacat, final String qformat, final String xslPath) { String htmlString = ""; Reader reader; String resultset = ""; String sessionId; StringReader stringReader; Stylizer stylizer = new Stylizer(); if (metacat == null) { try { metacat = MetacatFactory.createMetacatConnection(metacatURL); } catch (MetacatInaccessibleException mie) { System.err.println("Metacat Inaccessible:\n" + mie.getMessage()); } } sessionId = metacat.getSessionId(); try { System.err.println("Starting query..."); stringReader = new StringReader(queryString); reader = metacat.query(stringReader); resultset = IOUtil.getAsString(reader, true); System.err.println("Query result:\n" + resultset); htmlString = stylizer.resultsetToHTML(resultset, sessionId, metacatURL, qformat, xslPath); } catch (Exception e) { System.err.println("General exception:\n" + e.getMessage()); e.printStackTrace(); } return(htmlString); } /** * Main program to run a test query from the command line. * * Pass the server name, server port, and path to resultset.xsl as the first * three command line arguments: * * @param argv[0] The server name, e.g. "earth.lternet.edu" * @param argv[1] The server port, e.g. "8080", or 0 if no port needs * to be specified. * @param argv[2] The context string, e.g. "knb" * @param argv[3] The path to the resultset.xsl stylesheet, e.g. * "C:/Tomcat5/webapps/query/style/common/resultset.xsl" */ public static void main(String[] argv) { AdvancedSearch advancedSearch; AdvancedSearchBean advancedSearchBean = new AdvancedSearchBean(); String contextString = argv[2]; MetacatHelper metacatHelper = new MetacatHelper(); String metacatURL; String qformat = "lter"; final String serverName = argv[0]; final Integer serverPortInteger = new Integer(argv[1]); final int serverPort = serverPortInteger.intValue(); final String xslPath = argv[3]; advancedSearchBean.setSubjectField("ALL"); advancedSearchBean.setSubjectValue("bird"); //advancedSearchBean.setCreatorSurname("Walsh"); //advancedSearchBean.setCreatorSurnameQueryType(0); //advancedSearchBean.setCreatorOrganization("Georgia Coastal Ecosystems"); //advancedSearchBean.setTaxon("Crustacea"); //advancedSearchBean.setLocationName("Georgia"); //advancedSearchBean.setNorthBound("31.5"); //advancedSearchBean.setSouthBound("10"); //advancedSearchBean.setEastBound("-50"); //advancedSearchBean.setWestBound("-90"); //advancedSearchBean.setNamedTimescale("Phanerozoic"); //advancedSearchBean.setStartDate("2001-01-01"); //advancedSearchBean.setEndDate("2001-07-01"); advancedSearch = new AdvancedSearch(advancedSearchBean); metacatURL = metacatHelper.constructMetacatURL(serverName, serverPort, contextString); advancedSearch.executeAdvancedSearch(metacatURL, null, qformat, xslPath); } }