source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractSearch.java@ 25635

Last change on this file since 25635 was 25635, checked in by sjm84, 12 years ago

Fixing Greenstone 3's use (or lack thereof) of generics, this was done automatically so we may want to change it over time. This change will also auto-format any files that have not already been formatted.

  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/*
2 * AbstractSearch.java
3 * Copyright (C) 2005 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.service;
20
21// Greenstone classes
22import org.greenstone.gsdl3.util.GSXML;
23import org.greenstone.gsdl3.util.GSPath;
24import org.greenstone.gsdl3.util.AbstractSimpleDocument;
25import org.greenstone.gsdl3.util.SimpleDocument;
26
27// XML classes
28import org.w3c.dom.Document;
29import org.w3c.dom.Element;
30import org.w3c.dom.NodeList;
31
32// java classes
33import java.util.ArrayList;
34import java.util.HashMap;
35
36import org.apache.log4j.*;
37
38/**
39 * Partially implements a generic search service
40 *
41 * @author <a href="mailto:[email protected]">Katherine Don</a>
42 */
43
44public abstract class AbstractSearch extends ServiceRack
45{
46
47 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractSearch.class.getName());
48
49 // the search service
50 protected String QUERY_SERVICE = null; // set by subclass
51
52 // compulsory params
53 protected static final String INDEX_PARAM = "index";
54 protected static final String QUERY_PARAM = "query";
55 protected static final String RAW_PARAM = "rawquery";
56
57 // optional standard params - some of these have to be implemented
58 protected static final String MAXDOCS_PARAM = "maxDocs";
59 protected static final String HITS_PER_PAGE_PARAM = "hitsPerPage";
60 protected static final String START_PAGE_PARAM = "startPage";
61
62 protected AbstractSimpleDocument gs_doc = null;
63
64 /** can more than one index be searched at the same time? */
65 protected boolean does_multi_index_search = false;
66 /** does this service support paging of results? */
67 protected boolean does_paging = false;
68 /** does this service support asking for a subset of results? */
69 protected boolean does_chunking = false;
70 /**
71 * the default document type - use if all documents are the same type
72 */
73 protected String default_document_type = null;
74 /**
75 * the default index, or comma separated list if more than one is the
76 * default (with start and end commas, eg ,TI,SU,). Should be set by
77 * configure()
78 */
79 protected String default_index = "";
80
81 protected String default_max_docs = "100";
82
83 protected String default_hits_per_page = "10";
84
85 public AbstractSearch()
86 {
87 }
88
89 /**
90 * Sets up the short service info for service by QUERY_SERVICE (e.g.
91 * TextQuery or AudioQuery) If other services will be provided, should be
92 * added in the subclass configure also looks for search format info, and
93 * document format info
94 */
95 public boolean configure(Element info, Element extra_info)
96 {
97 if (!super.configure(info, extra_info))
98 {
99 return false;
100 }
101
102 logger.info("Configuring AbstractSearch...");
103
104 this.config_info = info;
105
106 // set up short_service_info_
107 // => for now just has id and type. the name (lang dependent)
108 // will be added in if the list is requested.
109
110 Element tq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
111 tq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
112 tq_service.setAttribute(GSXML.NAME_ATT, QUERY_SERVICE);
113 this.short_service_info.appendChild(tq_service);
114
115 // add some format info to service map if there is any
116 // => lookin extra info first look in buildConfig
117
118 Element format = (Element) GSXML.getChildByTagName(info, GSXML.FORMAT_ELEM);
119
120 if (format == null)
121 {
122 String path = GSPath.appendLink(GSXML.SEARCH_ELEM, GSXML.FORMAT_ELEM);
123
124 // Note by xiao: instead of retrieving the first 'format'
125 // element inside the 'search' element, we are trying to
126 // find the real format element which has at least one
127 // 'gsf:template' child element. (extra_info is
128 // collectionConfig.xml)
129
130 //format = (Element) GSXML.getNodeByPath(extra_info, path);
131
132 Element search_elem = (Element) GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
133 NodeList format_elems = null;
134 if (search_elem != null)
135 {
136 format_elems = search_elem.getElementsByTagName(GSXML.FORMAT_ELEM);
137 }
138 for (int i = 0; i < format_elems.getLength(); i++)
139 {
140 format = (Element) format_elems.item(i);
141 if (format.getElementsByTagName("gsf:template").getLength() != 0)
142 {
143 break;
144 }
145 }
146 }//end of if(format==null)
147 //
148 if (format != null)
149 {
150 this.format_info_map.put(QUERY_SERVICE, this.doc.importNode(format, true));
151 }
152
153 // look for document display format - for documentType
154 String path = GSPath.appendLink(GSXML.DISPLAY_ELEM, GSXML.FORMAT_ELEM);
155 Element display_format = (Element) GSXML.getNodeByPath(extra_info, path);
156 if (display_format != null)
157 {
158 // check for docType option.
159 Element doc_type_opt = GSXML.getNamedElement(display_format, "gsf:option", GSXML.NAME_ATT, "documentType");
160 if (doc_type_opt != null)
161 {
162 String value = doc_type_opt.getAttribute(GSXML.VALUE_ATT);
163 if (!value.equals(""))
164 {
165 this.default_document_type = value;
166 }
167 }
168 }
169
170 // Base line for document (might be overriden by sub-classes)
171 gs_doc = new SimpleDocument(this.doc, this.default_document_type);
172
173 return true;
174 }
175
176 /**
177 * returns a basic description for QUERY_SERVICE. If a subclass provides
178 * other services they need to provide their own descriptions
179 */
180 protected Element getServiceDescription(String service, String lang, String subset)
181 {
182 if (!service.equals(QUERY_SERVICE))
183 {
184 return null;
185 }
186
187 Element tq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
188 tq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
189 tq_service.setAttribute(GSXML.NAME_ATT, QUERY_SERVICE);
190 if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
191 {
192 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getServiceName(QUERY_SERVICE, lang)));
193 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getServiceSubmit(QUERY_SERVICE, lang)));
194 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getServiceDescription(QUERY_SERVICE, lang)));
195 }
196 if (subset == null || subset.equals(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER))
197 {
198 Element param_list = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
199 addCustomQueryParams(param_list, lang);
200 addStandardQueryParams(param_list, lang);
201 tq_service.appendChild(param_list);
202 }
203 return tq_service;
204
205 }
206
207 // perhaps these should be changed to search down the class hierarchy for
208 // values - do can just put the info in the resource bundle to use it
209 /** returns the default name for the TextQuery service */
210 protected String getServiceName(String service_id, String lang)
211 {
212 return getTextString(service_id + ".name", lang);
213 }
214
215 /** returns the default description for the TextQuery service */
216 protected String getServiceDescription(String service_id, String lang)
217 {
218 return getTextString(service_id + ".description", lang);
219 }
220
221 /** returns the default submit button text for the TextQuery service */
222 protected String getServiceSubmit(String service_id, String lang)
223 {
224 return getTextString(service_id + ".submit", lang);
225
226 }
227
228 /** adds the standard query params into the service description */
229 protected void addStandardQueryParams(Element param_list, String lang)
230 {
231 // this test is not so good. here we are using absence of default index
232 // to determine whether we have indexes or not. But in other places,
233 // absence of default index just means to use the first one as default.
234 if (!default_index.equals(""))
235 {
236 createParameter(INDEX_PARAM, param_list, lang);
237 }
238 if (does_chunking)
239 {
240 createParameter(MAXDOCS_PARAM, param_list, lang);
241 }
242 if (does_paging)
243 {
244 createParameter(HITS_PER_PAGE_PARAM, param_list, lang);
245 createParameter(START_PAGE_PARAM, param_list, lang);
246 }
247 createParameter(QUERY_PARAM, param_list, lang);
248 }
249
250 /**
251 * adds any service specific query params into the service default
252 * implementation: add nothing. subclasses may need to override this to add
253 * in their specific parameters
254 */
255 protected void addCustomQueryParams(Element param_list, String lang)
256 {
257 // default behaviour, do nothing
258 }
259
260 protected void createParameter(String name, Element param_list, String lang)
261 {
262 createParameter(name, param_list, lang, null);
263 }
264
265 protected void createParameter(String name, Element param_list, String lang, String default_value)
266 {
267 // at this level, not interested in boolean return type
268 createParameterChain(name, param_list, lang, default_value);
269 }
270
271 /**
272 * default implementations for the standard parameters plus some other
273 * common ones index, maxDocs, hitsPerPage, startPage
274 */
275
276 protected boolean createParameterChain(String name, Element param_list, String lang, String default_value)
277 {
278 Element param = null;
279 String param_default = default_value;
280
281 if (name.equals(QUERY_PARAM) || name.equals(RAW_PARAM))
282 {
283 param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_STRING, param_default, null, null);
284 param_list.appendChild(param);
285 return true;
286 }
287 else if (name.equals(INDEX_PARAM))
288 {
289 // should we make these class fields?
290 ArrayList<String> index_ids = new ArrayList<String>();
291 ArrayList<String> index_names = new ArrayList<String>();
292 getIndexData(index_ids, index_names, lang);
293 String param_type = GSXML.PARAM_TYPE_ENUM_SINGLE;
294 if (does_multi_index_search)
295 {
296 param_type = GSXML.PARAM_TYPE_ENUM_MULTI;
297 }
298 if (param_default == null)
299 {
300 param_default = this.default_index;
301 }
302 param = GSXML.createParameterDescription2(this.doc, INDEX_PARAM, getTextString("param." + INDEX_PARAM, lang), param_type, param_default, index_ids, index_names);
303 param_list.appendChild(param);
304 return true;
305 }
306 else if (name.equals(MAXDOCS_PARAM))
307 {
308 if (param_default == null)
309 {
310 param_default = this.default_max_docs;
311 }
312
313 param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_INTEGER, param_default, null, null);
314 param_list.appendChild(param);
315 return true;
316 }
317 else if (name.equals(HITS_PER_PAGE_PARAM))
318 {
319 if (param_default == null)
320 {
321 param_default = this.default_hits_per_page;
322 }
323
324 param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_INTEGER, param_default, null, null);
325 param_list.appendChild(param);
326 return true;
327 }
328 else if (name.equals(START_PAGE_PARAM))
329 {
330 if (param_default == null)
331 {
332 param_default = "1";
333 }
334
335 // start page - set to 1 for the search page
336 param = GSXML.createParameterDescription(this.doc, START_PAGE_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, param_default, null, null);
337 param_list.appendChild(param);
338 return true;
339 }
340
341 // Get to there then none of the above params matched
342 // => return false so the chain can continue
343 return false;
344 }
345
346 /**
347 * create an element to go into the search results list. A node element has
348 * the form <docNode nodeId='xxx' nodeType='leaf' docType='hierarchy'
349 * rank='0.23'/>
350 */
351 protected Element createDocNode(String node_id, String rank)
352 {
353 return this.gs_doc.createDocNode(node_id, rank);
354 }
355
356 /**
357 * returns the document type of the doc that the specified node belongs to.
358 * should be one of GSXML.DOC_TYPE_SIMPLE, GSXML.DOC_TYPE_PAGED,
359 * GSXML.DOC_TYPE_HIERARCHY
360 */
361 protected String getDocType(String node_id)
362 {
363 return this.gs_doc.getDocType(node_id);
364 }
365
366 /**
367 * returns the node type of the specified node. should be one of
368 * GSXML.NODE_TYPE_LEAF, GSXML.NODE_TYPE_INTERNAL, GSXML.NODE_TYPE_ROOT
369 */
370 protected String getNodeType(String node_id, String doc_type)
371 {
372 return this.gs_doc.getNodeType(node_id, doc_type);
373 }
374
375 /** returns true if the node has child nodes */
376 protected boolean hasChildren(String node_id)
377 {
378 return this.gs_doc.hasChildren(node_id);
379 }
380
381 /** returns true if the node has a parent */
382 protected boolean hasParent(String node_id)
383 {
384 return this.gs_doc.hasParent(node_id);
385 }
386
387 /**
388 * get the details about the indexes available must be implemented by
389 * subclass there must be at least one index
390 */
391 abstract protected void getIndexData(ArrayList<String> index_ids, ArrayList<String> index_names, String lang);
392
393}
Note: See TracBrowser for help on using the repository browser.