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

Last change on this file since 29318 was 28966, checked in by kjdon, 10 years ago

Lots of changes. Mainly to do with removing this.doc from everywhere. Document is not thread safe. Now we tend to create a new Document everytime we are starting a new page/message etc. in service this.desc_doc is available as teh document to create service info stuff. But it should only be used for this and not for other messages. newDOM is now static for XMLConverter. method param changes for some GSXML methods.

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