source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/DocumentAction.java@ 28382

Last change on this file since 28382 was 28382, checked in by davidb, 11 years ago

Elimination of the 'this.doc' field from the Action baseclass and the subclasses that rely on it. For Greenstone3 purposes it is unsafe to create this object in the constructor to the action and then store it for other methods to access. This is because the Greenstone 3 (and in particular calls to 'process' operate in a multi-threaded context, that is managed by the Servlet server (e.g. Tomcat by default). Calls to DOM methods are not guaranteed to be thread safe, this became apparent when we started looking in to an exception that was being thrown, and centred around use of the DOM method 'item(i)'. The change this commit makes is to remove 'this.doc' being stored as a field. A document is now created in the top level of a call to 'process()' and when a DOM reference is needed in a subsequent method an Element variable (typically passed in as a parameter to the method) is used (through 'Document doc = element.getOwnerDocument()') to gain access to the DOM

  • Property svn:keywords set to Author Date Id Revision
File size: 42.7 KB
Line 
1/*
2 * DocumentAction.java
3 * Copyright (C) 2002 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.action;
20
21// Greenstone classes
22import org.greenstone.gsdl3.core.ModuleInterface;
23import org.greenstone.gsdl3.util.*;
24
25// XML classes
26import org.w3c.dom.Document;
27import org.w3c.dom.Element;
28import org.w3c.dom.Node;
29import org.w3c.dom.Text;
30import org.w3c.dom.NodeList;
31
32// General Java classes
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.HashSet;
36import java.io.File;
37import java.io.Serializable;
38
39import org.apache.log4j.*;
40
41/** Action class for retrieving Documents via the message router */
42public class DocumentAction extends Action
43{
44
45 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.action.DocumentAction.class.getName());
46
47 // this is used to specify that the sibling nodes of a selected one should be obtained
48 public static final String SIBLING_ARG = "sib";
49 public static final String GOTO_PAGE_ARG = "gp";
50 public static final String ENRICH_DOC_ARG = "end";
51 public static final String EXPAND_DOCUMENT_ARG = "ed";
52 public static final String EXPAND_CONTENTS_ARG = "ec";
53 public static final String REALISTIC_BOOK_ARG = "book";
54
55 /**
56 * if this is set to true, when a document is displayed, any annotation type
57 * services (enrich) will be offered to the user as well
58 */
59 protected boolean provide_annotations = false;
60
61 protected boolean highlight_query_terms = false;
62
63 public boolean configure()
64 {
65 super.configure();
66 String highlight = (String) config_params.get("highlightQueryTerms");
67 if (highlight != null && highlight.equals("true"))
68 {
69 highlight_query_terms = true;
70 }
71 String annotate = (String) config_params.get("displayAnnotationService");
72 if (annotate != null && annotate.equals("true"))
73 {
74 provide_annotations = true;
75 }
76 return true;
77 }
78
79 public Node process(Node message_node)
80 {
81 // for now, no subaction eventually we may want to have subactions such as text assoc or something ?
82
83 Element message = this.converter.nodeToElement(message_node);
84 Document doc = message.getOwnerDocument();
85
86 // the response
87 Element result = doc.createElement(GSXML.MESSAGE_ELEM);
88 Element page_response = doc.createElement(GSXML.RESPONSE_ELEM);
89 result.appendChild(page_response);
90
91 // get the request - assume only one
92 Element request = (Element) GSXML.getChildByTagName(message, GSXML.REQUEST_ELEM);
93 Element cgi_paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
94 HashMap<String, Serializable> params = GSXML.extractParams(cgi_paramList, false);
95
96 // just in case there are some that need to get passed to the services
97 HashMap service_params = (HashMap) params.get("s0");
98
99 String collection = (String) params.get(GSParams.COLLECTION);
100 String document_id = (String) params.get(GSParams.DOCUMENT);
101 if (document_id != null && document_id.equals(""))
102 {
103 document_id = null;
104 }
105 String href = (String) params.get(GSParams.HREF);//for an external link : get the href URL if it is existing in the params list
106 if (href != null && href.equals(""))
107 {
108 href = null;
109 }
110 String rl = (String) params.get(GSParams.RELATIVE_LINK);//for an external link : get the rl value if it is existing in the params list
111 if (document_id == null && href == null)
112 {
113 logger.error("no document specified!");
114 return result;
115 }
116 if (rl != null && rl.equals("0"))
117 {
118 // this is a true external link, we should have been directed to a different page or action
119 logger.error("rl value was 0, shouldn't get here");
120 return result;
121 }
122 String document_type = (String) params.get(GSParams.DOCUMENT_TYPE);
123 if (document_type != null && document_type.equals(""))
124 {
125 //document_type = "hierarchy";
126 document_type = null; // we'll get it later if not already specified
127 }
128 //whether to retrieve siblings or not
129 boolean get_siblings = false;
130 String sibs = (String) params.get(SIBLING_ARG);
131 if (sibs != null && sibs.equals("1"))
132 {
133 get_siblings = true;
134 }
135
136 String doc_id_modifier = "";
137 String sibling_num = (String) params.get(GOTO_PAGE_ARG);
138 if (sibling_num != null && !sibling_num.equals(""))
139 {
140 // we have to modify the doc name
141 doc_id_modifier = "." + sibling_num + ".ss";
142 }
143
144 boolean expand_document = false;
145 String ed_arg = (String) params.get(EXPAND_DOCUMENT_ARG);
146 if (ed_arg != null && ed_arg.equals("1"))
147 {
148 expand_document = true;
149 }
150
151 boolean expand_contents = false;
152 if (expand_document)
153 { // we always expand the contents with the text
154 expand_contents = true;
155 }
156 else
157 {
158 String ec_arg = (String) params.get(EXPAND_CONTENTS_ARG);
159 if (ec_arg != null && ec_arg.equals("1"))
160 {
161 expand_contents = true;
162 }
163 }
164
165 UserContext userContext = new UserContext(request);
166
167 //append site metadata
168 addSiteMetadata(page_response, userContext);
169 addInterfaceOptions(page_response);
170
171 // get the additional data needed for the page
172 getBackgroundData(page_response, collection, userContext);
173 Element format_elem = (Element) GSXML.getChildByTagName(page_response, GSXML.FORMAT_ELEM);
174
175 // the_document is where all the doc info - structure and metadata etc
176 // is added into, to be returned in the page
177 Element the_document = doc.createElement(GSXML.DOCUMENT_ELEM);
178 page_response.appendChild(the_document);
179
180 // create a basic doc list containing the current node
181 Element basic_doc_list = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
182 Element current_doc = doc.createElement(GSXML.DOC_NODE_ELEM);
183 basic_doc_list.appendChild(current_doc);
184 if (document_id != null)
185 {
186 current_doc.setAttribute(GSXML.NODE_ID_ATT, document_id + doc_id_modifier);
187 }
188 else
189 {
190 current_doc.setAttribute(GSXML.HREF_ID_ATT, href);
191 // do we need this??
192 current_doc.setAttribute(GSXML.ID_MOD_ATT, doc_id_modifier);
193 }
194
195 if (document_type == null)
196 {
197 document_type = getDocumentType(basic_doc_list, collection, userContext, page_response);
198 }
199 if (document_type != null)
200 {
201 // set the doctype from the cgi arg or from the server as an attribute
202 the_document.setAttribute(GSXML.DOC_TYPE_ATT, document_type);
203 }
204 else
205 {
206 logger.error("doctype is null!!!***********");
207 }
208
209 // Create a parameter list to specify the required structure information
210 Element ds_param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
211
212 if (service_params != null)
213 {
214 GSXML.addParametersToList(doc, ds_param_list, service_params);
215 }
216
217 Element ds_param = null;
218 boolean get_structure = false;
219 boolean get_structure_info = false;
220 if (document_type.equals(GSXML.DOC_TYPE_PAGED))
221 {
222 get_structure_info = true;
223
224 if (expand_contents)
225 {
226 ds_param = doc.createElement(GSXML.PARAM_ELEM);
227 ds_param_list.appendChild(ds_param);
228 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
229 ds_param.setAttribute(GSXML.VALUE_ATT, "entire");
230 }
231
232 // get the info needed for paged naviagtion
233 ds_param = doc.createElement(GSXML.PARAM_ELEM);
234 ds_param_list.appendChild(ds_param);
235 ds_param.setAttribute(GSXML.NAME_ATT, "info");
236 ds_param.setAttribute(GSXML.VALUE_ATT, "numSiblings");
237 ds_param = doc.createElement(GSXML.PARAM_ELEM);
238 ds_param_list.appendChild(ds_param);
239 ds_param.setAttribute(GSXML.NAME_ATT, "info");
240 ds_param.setAttribute(GSXML.VALUE_ATT, "numChildren");
241 ds_param = doc.createElement(GSXML.PARAM_ELEM);
242 ds_param_list.appendChild(ds_param);
243 ds_param.setAttribute(GSXML.NAME_ATT, "info");
244 ds_param.setAttribute(GSXML.VALUE_ATT, "siblingPosition");
245
246 if (get_siblings)
247 {
248 ds_param = doc.createElement(GSXML.PARAM_ELEM);
249 ds_param_list.appendChild(ds_param);
250 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
251 ds_param.setAttribute(GSXML.VALUE_ATT, "siblings");
252 }
253
254 }
255 else if (document_type.equals(GSXML.DOC_TYPE_HIERARCHY) || document_type.equals(GSXML.DOC_TYPE_PAGED_HIERARCHY))
256 {
257 get_structure = true;
258 if (expand_contents)
259 {
260 ds_param = doc.createElement(GSXML.PARAM_ELEM);
261 ds_param_list.appendChild(ds_param);
262 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
263 ds_param.setAttribute(GSXML.VALUE_ATT, "entire");
264 }
265 else
266 {
267 // get the info needed for table of contents
268 ds_param = doc.createElement(GSXML.PARAM_ELEM);
269 ds_param_list.appendChild(ds_param);
270 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
271 ds_param.setAttribute(GSXML.VALUE_ATT, "ancestors");
272 ds_param = doc.createElement(GSXML.PARAM_ELEM);
273 ds_param_list.appendChild(ds_param);
274 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
275 ds_param.setAttribute(GSXML.VALUE_ATT, "children");
276 if (get_siblings)
277 {
278 ds_param = doc.createElement(GSXML.PARAM_ELEM);
279 ds_param_list.appendChild(ds_param);
280 ds_param.setAttribute(GSXML.NAME_ATT, "structure");
281 ds_param.setAttribute(GSXML.VALUE_ATT, "siblings");
282 }
283 }
284 }
285 else
286 {
287 // we dont need any structure
288 }
289
290 boolean has_dummy = false;
291 if (get_structure || get_structure_info)
292 {
293
294 // Build a request to obtain the document structure
295 Element ds_message = doc.createElement(GSXML.MESSAGE_ELEM);
296 String to = GSPath.appendLink(collection, "DocumentStructureRetrieve");// Hard-wired?
297 Element ds_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
298 ds_message.appendChild(ds_request);
299 ds_request.appendChild(ds_param_list);
300
301 // add the node list we created earlier
302 ds_request.appendChild(basic_doc_list);
303
304 // Process the document structure retrieve message
305 Element ds_response_message = (Element) this.mr.process(ds_message);
306 if (processErrorElements(ds_response_message, page_response))
307 {
308 return result;
309 }
310
311 // get the info and print out
312 String path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
313 path = GSPath.appendLink(path, GSXML.DOC_NODE_ELEM);
314 path = GSPath.appendLink(path, "nodeStructureInfo");
315 Element ds_response_struct_info = (Element) GSXML.getNodeByPath(ds_response_message, path);
316 // get the doc_node bit
317 if (ds_response_struct_info != null)
318 {
319 the_document.appendChild(doc.importNode(ds_response_struct_info, true));
320 }
321 path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
322 path = GSPath.appendLink(path, GSXML.DOC_NODE_ELEM);
323 path = GSPath.appendLink(path, GSXML.NODE_STRUCTURE_ELEM);
324 Element ds_response_structure = (Element) GSXML.getNodeByPath(ds_response_message, path);
325
326 if (ds_response_structure != null)
327 {
328 // add the contents of the structure bit into the_document
329 NodeList structs = ds_response_structure.getChildNodes();
330 for (int i = 0; i < structs.getLength(); i++)
331 {
332 the_document.appendChild(doc.importNode(structs.item(i), true));
333 }
334 }
335 else
336 {
337 // no structure nodes, so put in a dummy doc node
338 Element doc_node = doc.createElement(GSXML.DOC_NODE_ELEM);
339 if (document_id != null)
340 {
341 doc_node.setAttribute(GSXML.NODE_ID_ATT, document_id);
342 }
343 else
344 {
345 doc_node.setAttribute(GSXML.HREF_ID_ATT, href);
346
347 }
348 the_document.appendChild(doc_node);
349 has_dummy = true;
350 }
351 }
352 else
353 { // a simple type - we dont have a dummy node for simple
354 // should think about this more
355 // no structure request, so just put in a dummy doc node
356 Element doc_node = doc.createElement(GSXML.DOC_NODE_ELEM);
357 if (document_id != null)
358 {
359 doc_node.setAttribute(GSXML.NODE_ID_ATT, document_id);
360 }
361 else
362 {
363 doc_node.setAttribute(GSXML.HREF_ID_ATT, href);
364 }
365 the_document.appendChild(doc_node);
366 has_dummy = true;
367 }
368
369 // Build a request to obtain some document metadata
370 Element dm_message = doc.createElement(GSXML.MESSAGE_ELEM);
371 String to = GSPath.appendLink(collection, "DocumentMetadataRetrieve"); // Hard-wired?
372 Element dm_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
373 dm_message.appendChild(dm_request);
374 // Create a parameter list to specify the required metadata information
375
376 HashSet<String> meta_names = new HashSet<String>();
377 meta_names.add("Title"); // the default
378 if (format_elem != null)
379 {
380 getRequiredMetadataNames(format_elem, meta_names);
381 }
382
383 Element extraMetaListElem = (Element) GSXML.getChildByTagName(request, GSXML.EXTRA_METADATA + GSXML.LIST_MODIFIER);
384 if (extraMetaListElem != null)
385 {
386 NodeList extraMetaList = extraMetaListElem.getElementsByTagName(GSXML.EXTRA_METADATA);
387 for (int i = 0; i < extraMetaList.getLength(); i++)
388 {
389 meta_names.add(((Element) extraMetaList.item(i)).getAttribute(GSXML.NAME_ATT));
390 }
391 }
392
393 Element dm_param_list = createMetadataParamList(doc,meta_names);
394 if (service_params != null)
395 {
396 GSXML.addParametersToList(doc, dm_param_list, service_params);
397 }
398
399 dm_request.appendChild(dm_param_list);
400
401 // create the doc node list for the metadata request
402 Element dm_doc_list = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
403 dm_request.appendChild(dm_doc_list);
404
405 // Add each node from the structure response into the metadata request
406 NodeList doc_nodes = the_document.getElementsByTagName(GSXML.DOC_NODE_ELEM);
407 for (int i = 0; i < doc_nodes.getLength(); i++)
408 {
409 Element doc_node = (Element) doc_nodes.item(i);
410 String doc_node_id = doc_node.getAttribute(GSXML.NODE_ID_ATT);
411
412 // Add the documentNode to the list
413 Element dm_doc_node = doc.createElement(GSXML.DOC_NODE_ELEM);
414 dm_doc_list.appendChild(dm_doc_node);
415 dm_doc_node.setAttribute(GSXML.NODE_ID_ATT, doc_node_id);
416 dm_doc_node.setAttribute(GSXML.NODE_TYPE_ATT, doc_node.getAttribute(GSXML.NODE_TYPE_ATT));
417 }
418
419 // we also want a metadata request to the top level document to get
420 // assocfilepath - this could be cached too
421 Element doc_meta_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
422 dm_message.appendChild(doc_meta_request);
423 Element doc_meta_param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
424 if (service_params != null)
425 {
426 GSXML.addParametersToList(doc, doc_meta_param_list, service_params);
427 }
428
429 doc_meta_request.appendChild(doc_meta_param_list);
430 Element doc_param = doc.createElement(GSXML.PARAM_ELEM);
431 doc_meta_param_list.appendChild(doc_param);
432 doc_param.setAttribute(GSXML.NAME_ATT, "metadata");
433 doc_param.setAttribute(GSXML.VALUE_ATT, "assocfilepath");
434
435 // create the doc node list for the metadata request
436 Element doc_list = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
437 doc_meta_request.appendChild(doc_list);
438
439 Element doc_node = doc.createElement(GSXML.DOC_NODE_ELEM);
440 // the node we want is the root document node
441 if (document_id != null)
442 {
443 doc_node.setAttribute(GSXML.NODE_ID_ATT, document_id + ".rt");
444 }
445 else
446 {
447 doc_node.setAttribute(GSXML.HREF_ID_ATT, href);// + ".rt");
448 // can we assume that href is always a top level doc??
449 //doc_node.setAttribute(GSXML.ID_MOD_ATT, ".rt");
450 //doc_node.setAttribute("externalURL", has_rl);
451 }
452 doc_list.appendChild(doc_node);
453
454 Element dm_response_message = (Element) this.mr.process(dm_message);
455 if (processErrorElements(dm_response_message, page_response))
456 {
457 return result;
458 }
459
460 String path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
461 Element dm_response_doc_list = (Element) GSXML.getNodeByPath(dm_response_message, path);
462
463 // Merge the metadata with the structure information
464 NodeList dm_response_docs = dm_response_doc_list.getChildNodes();
465 for (int i = 0; i < doc_nodes.getLength(); i++)
466 {
467 GSXML.mergeMetadataLists(doc_nodes.item(i), dm_response_docs.item(i));
468 }
469 // get the top level doc metadata out
470 Element doc_meta_response = (Element) dm_response_message.getElementsByTagName(GSXML.RESPONSE_ELEM).item(1);
471 Element top_doc_node = (Element) GSXML.getNodeByPath(doc_meta_response, "documentNodeList/documentNode");
472 GSXML.mergeMetadataLists(the_document, top_doc_node);
473
474 // Build a request to obtain some document content
475 Element dc_message = doc.createElement(GSXML.MESSAGE_ELEM);
476 to = GSPath.appendLink(collection, "DocumentContentRetrieve"); // Hard-wired?
477 Element dc_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
478 dc_message.appendChild(dc_request);
479
480 // Create a parameter list to specify the request parameters - empty for now
481 Element dc_param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
482 if (service_params != null)
483 {
484 GSXML.addParametersToList(doc, dc_param_list, service_params);
485 }
486
487 dc_request.appendChild(dc_param_list);
488
489 // get the content
490 // the doc list for the content request is the same as the one for the structure request unless we want the whole document, in which case its the same as for the metadata request.
491 if (expand_document)
492 {
493 dc_request.appendChild(dm_doc_list);
494 }
495 else
496 {
497 dc_request.appendChild(basic_doc_list);
498 }
499 logger.debug("request = " + XMLConverter.getString(dc_message));
500 Element dc_response_message = (Element) this.mr.process(dc_message);
501 if (processErrorElements(dc_response_message, page_response))
502 {
503 return result;
504 }
505
506 Element dc_response_doc_list = (Element) GSXML.getNodeByPath(dc_response_message, path);
507
508 if (expand_document)
509 {
510 // Merge the content with the structure information
511 NodeList dc_response_docs = dc_response_doc_list.getChildNodes();
512 for (int i = 0; i < doc_nodes.getLength(); i++)
513 {
514 Node content = GSXML.getChildByTagName((Element) dc_response_docs.item(i), "nodeContent");
515 if (content != null)
516 {
517 if (highlight_query_terms)
518 {
519 content = highlightQueryTerms(request, (Element) content);
520 }
521 doc_nodes.item(i).appendChild(doc.importNode(content, true));
522 }
523 //GSXML.mergeMetadataLists(doc_nodes.item(i), dm_response_docs.item(i));
524 }
525 }
526 else
527 {
528 //path = GSPath.appendLink(path, GSXML.DOC_NODE_ELEM);
529 Element dc_response_doc = (Element) GSXML.getChildByTagName(dc_response_doc_list, GSXML.DOC_NODE_ELEM);
530 Element dc_response_doc_content = (Element) GSXML.getChildByTagName(dc_response_doc, GSXML.NODE_CONTENT_ELEM);
531 //Element dc_response_doc_external = (Element) GSXML.getChildByTagName(dc_response_doc, "external");
532
533 if (dc_response_doc_content == null)
534 {
535 // no content to add
536 if (dc_response_doc.getAttribute("external").equals("true"))
537 {
538
539 //if (dc_response_doc_external != null)
540 //{
541 String href_id = dc_response_doc.getAttribute(GSXML.HREF_ID_ATT);
542
543 the_document.setAttribute("selectedNode", href_id);
544 the_document.setAttribute("external", href_id);
545 }
546 return result;
547 }
548 if (highlight_query_terms)
549 {
550 dc_response_doc.removeChild(dc_response_doc_content);
551
552 dc_response_doc_content = highlightQueryTerms(request, dc_response_doc_content);
553 dc_response_doc.appendChild(dc_response_doc.getOwnerDocument().importNode(dc_response_doc_content, true));
554 }
555
556 if (provide_annotations)
557 {
558 String service_selected = (String) params.get(ENRICH_DOC_ARG);
559 if (service_selected != null && service_selected.equals("1"))
560 {
561 // now we can modifiy the response doc if needed
562 String enrich_service = (String) params.get(GSParams.SERVICE);
563 // send a message to the service
564 Element enrich_message = doc.createElement(GSXML.MESSAGE_ELEM);
565 Element enrich_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, enrich_service, userContext);
566 enrich_message.appendChild(enrich_request);
567 // check for parameters
568 HashMap e_service_params = (HashMap) params.get("s1");
569 if (e_service_params != null)
570 {
571 Element enrich_pl = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
572 GSXML.addParametersToList(doc, enrich_pl, e_service_params);
573 enrich_request.appendChild(enrich_pl);
574 }
575 Element e_doc_list = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
576 enrich_request.appendChild(e_doc_list);
577 e_doc_list.appendChild(doc.importNode(dc_response_doc, true));
578
579 Node enrich_response = this.mr.process(enrich_message);
580
581 String[] links = { GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER, GSXML.DOC_NODE_ELEM, GSXML.NODE_CONTENT_ELEM };
582 path = GSPath.createPath(links);
583 dc_response_doc_content = (Element) GSXML.getNodeByPath(enrich_response, path);
584
585 }
586 } // if provide_annotations
587
588 // use the returned id rather than the sent one cos there may have
589 // been modifiers such as .pr that are removed.
590 String modified_doc_id = dc_response_doc.getAttribute(GSXML.NODE_ID_ATT);
591 the_document.setAttribute("selectedNode", modified_doc_id);
592 if (has_dummy)
593 {
594 // change the id if necessary and add the content
595 Element dummy_node = (Element) doc_nodes.item(0);
596
597 dummy_node.setAttribute(GSXML.NODE_ID_ATT, modified_doc_id);
598 dummy_node.appendChild(doc.importNode(dc_response_doc_content, true));
599 // hack for simple type
600 if (document_type.equals(GSXML.DOC_TYPE_SIMPLE))
601 {
602 // we dont want the internal docNode, just want the content and metadata in the document
603 // rethink this!!
604 the_document.removeChild(dummy_node);
605
606 NodeList dummy_children = dummy_node.getChildNodes();
607 //for (int i=0; i<dummy_children.getLength(); i++) {
608 for (int i = dummy_children.getLength() - 1; i >= 0; i--)
609 {
610 // special case as we don't want more than one metadata list
611 if (dummy_children.item(i).getNodeName().equals(GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER))
612 {
613 GSXML.mergeMetadataFromList(the_document, dummy_children.item(i));
614 }
615 else
616 {
617 the_document.appendChild(dummy_children.item(i));
618 }
619 }
620 }
621
622 the_document.setAttribute(GSXML.NODE_ID_ATT, modified_doc_id);
623 }
624 else
625 {
626 // Merge the document content with the metadata and structure information
627 for (int i = 0; i < doc_nodes.getLength(); i++)
628 {
629 Node dn = doc_nodes.item(i);
630 String dn_id = ((Element) dn).getAttribute(GSXML.NODE_ID_ATT);
631 if (dn_id.equals(modified_doc_id))
632 {
633 dn.appendChild(doc.importNode(dc_response_doc_content, true));
634 break;
635 }
636 }
637 }
638 }
639 logger.debug("(DocumentAction) Page:\n" + GSXML.xmlNodeToString(result));
640 return result;
641 }
642
643 /**
644 * tell the param class what its arguments are if an action has its own
645 * arguments, this should add them to the params object - particularly
646 * important for args that should not be saved
647 */
648 public boolean addActionParameters(GSParams params)
649 {
650 params.addParameter(GOTO_PAGE_ARG, false);
651 params.addParameter(ENRICH_DOC_ARG, false);
652 params.addParameter(EXPAND_DOCUMENT_ARG, false);
653 params.addParameter(EXPAND_CONTENTS_ARG, false);
654 params.addParameter(REALISTIC_BOOK_ARG, false);
655
656 return true;
657 }
658
659 /**
660 * this method gets the collection description, the format info, the list of
661 * enrich services, etc - stuff that is needed for the page, but is the same
662 * whatever the query is - should be cached
663 */
664 protected boolean getBackgroundData(Element page_response, String collection, UserContext userContext)
665 {
666 Document doc = page_response.getOwnerDocument();
667
668 // create a message to process - contains requests for the collection
669 // description, the format element, the enrich services on offer
670 // these could all be cached
671 Element info_message = doc.createElement(GSXML.MESSAGE_ELEM);
672 String path = GSPath.appendLink(collection, "DocumentContentRetrieve");
673 // the format request - ignore for now, where does this request go to??
674 Element format_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_FORMAT, path, userContext);
675 info_message.appendChild(format_request);
676
677 // the enrich_services request - only do this if provide_annotations is true
678
679 if (provide_annotations)
680 {
681 Element enrich_services_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", userContext);
682 enrich_services_request.setAttribute(GSXML.INFO_ATT, "serviceList");
683 info_message.appendChild(enrich_services_request);
684 }
685
686 Element info_response = (Element) this.mr.process(info_message);
687
688 // the collection is the first response
689 NodeList responses = info_response.getElementsByTagName(GSXML.RESPONSE_ELEM);
690 Element format_resp = (Element) responses.item(0);
691
692 Element format_elem = (Element) GSXML.getChildByTagName(format_resp, GSXML.FORMAT_ELEM);
693 if (format_elem != null)
694 {
695 Element global_format_elem = (Element) GSXML.getChildByTagName(format_resp, GSXML.GLOBAL_FORMAT_ELEM);
696 if (global_format_elem != null)
697 {
698 GSXSLT.mergeFormatElements(format_elem, global_format_elem, false);
699 }
700
701 // set the format type
702 format_elem.setAttribute(GSXML.TYPE_ATT, "display");
703 page_response.appendChild(doc.importNode(format_elem, true));
704 }
705
706 if (provide_annotations)
707 {
708 Element services_resp = (Element) responses.item(1);
709
710 // a new message for the mr
711 Element enrich_message = doc.createElement(GSXML.MESSAGE_ELEM);
712 NodeList e_services = services_resp.getElementsByTagName(GSXML.SERVICE_ELEM);
713 boolean service_found = false;
714 for (int j = 0; j < e_services.getLength(); j++)
715 {
716 if (((Element) e_services.item(j)).getAttribute(GSXML.TYPE_ATT).equals("enrich"))
717 {
718 Element s = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, ((Element) e_services.item(j)).getAttribute(GSXML.NAME_ATT), userContext);
719 enrich_message.appendChild(s);
720 service_found = true;
721 }
722 }
723 if (service_found)
724 {
725 Element enrich_response = (Element) this.mr.process(enrich_message);
726
727 NodeList e_responses = enrich_response.getElementsByTagName(GSXML.RESPONSE_ELEM);
728 Element service_list = doc.createElement(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
729 for (int i = 0; i < e_responses.getLength(); i++)
730 {
731 Element e_resp = (Element) e_responses.item(i);
732 Element e_service = (Element) doc.importNode(GSXML.getChildByTagName(e_resp, GSXML.SERVICE_ELEM), true);
733 e_service.setAttribute(GSXML.NAME_ATT, e_resp.getAttribute(GSXML.FROM_ATT));
734 service_list.appendChild(e_service);
735 }
736 page_response.appendChild(service_list);
737 }
738 } // if provide_annotations
739 return true;
740
741 }
742
743 protected String getDocumentType(Element basic_doc_list, String collection, UserContext userContext, Element page_response)
744 {
745 Document doc = basic_doc_list.getOwnerDocument();
746
747 Element ds_message = doc.createElement(GSXML.MESSAGE_ELEM);
748 String to = GSPath.appendLink(collection, "DocumentStructureRetrieve");// Hard-wired?
749 Element ds_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
750 ds_message.appendChild(ds_request);
751
752 // Create a parameter list to specify the required structure information
753 Element ds_param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
754 Element ds_param = doc.createElement(GSXML.PARAM_ELEM);
755 ds_param_list.appendChild(ds_param);
756 ds_param.setAttribute(GSXML.NAME_ATT, "info");
757 ds_param.setAttribute(GSXML.VALUE_ATT, "documentType");
758
759 ds_request.appendChild(ds_param_list);
760
761 // add the node list we created earlier
762 ds_request.appendChild(basic_doc_list);
763
764 // Process the document structure retrieve message
765 Element ds_response_message = (Element) this.mr.process(ds_message);
766 if (processErrorElements(ds_response_message, page_response))
767 {
768 return null;
769 }
770
771 String[] links = { GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER, GSXML.DOC_NODE_ELEM, "nodeStructureInfo" };
772 String path = GSPath.createPath(links);
773 Element info_elem = (Element) GSXML.getNodeByPath(ds_response_message, path);
774 Element doctype_elem = GSXML.getNamedElement(info_elem, "info", "name", "documentType");
775 if (doctype_elem != null)
776 {
777 String doc_type = doctype_elem.getAttribute("value");
778 return doc_type;
779 }
780 return null;
781 }
782
783 /**
784 * this involves a bit of a hack to get the equivalent query terms - has to
785 * requery the query service - uses the last selected service name. (if it
786 * ends in query). should this action do the query or should it send a
787 * message to the query action? but that will involve lots of extra stuff.
788 * also doesn't handle phrases properly - just highlights all the terms
789 * found in the text.
790 */
791 protected Element highlightQueryTerms(Element request, Element dc_response_doc_content)
792 {
793 Document doc = request.getOwnerDocument();
794
795 // do the query again to get term info
796 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
797 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
798
799 HashMap previous_params = (HashMap) params.get("p");
800 if (previous_params == null)
801 {
802 return dc_response_doc_content;
803 }
804 String service_name = (String) previous_params.get(GSParams.SERVICE);
805 if (service_name == null || !service_name.endsWith("Query"))
806 { // hack for now - we only do highlighting if we were in a query last - ie not if we were in a browse thingy
807 logger.debug("invalid service, not doing highlighting");
808 return dc_response_doc_content;
809 }
810 String collection = (String) params.get(GSParams.COLLECTION);
811 UserContext userContext = new UserContext(request);
812 String to = GSPath.appendLink(collection, service_name);
813
814 Element mr_query_message = doc.createElement(GSXML.MESSAGE_ELEM);
815 Element mr_query_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
816 mr_query_message.appendChild(mr_query_request);
817
818 // paramList
819 HashMap service_params = (HashMap) params.get("s1");
820
821 Element query_param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
822 GSXML.addParametersToList(doc, query_param_list, service_params);
823 mr_query_request.appendChild(query_param_list);
824
825 // do the query
826 Element mr_query_response = (Element) this.mr.process(mr_query_message);
827
828 String path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.TERM_ELEM + GSXML.LIST_MODIFIER);
829 Element query_term_list_element = (Element) GSXML.getNodeByPath(mr_query_response, path);
830 if (query_term_list_element == null)
831 {
832 // no term info
833 logger.error("No query term information.\n");
834 return dc_response_doc_content;
835 }
836
837 String content = GSXML.getNodeText(dc_response_doc_content);
838
839 String metadata_path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
840 Element metadata_list = (Element) GSXML.getNodeByPath(mr_query_response, metadata_path);
841
842 HashSet<String> query_term_variants = new HashSet<String>();
843 NodeList equivalent_terms_nodelist = query_term_list_element.getElementsByTagName("equivTermList");
844 if (equivalent_terms_nodelist == null || equivalent_terms_nodelist.getLength() == 0)
845 {
846 NodeList terms_nodelist = query_term_list_element.getElementsByTagName("term");
847 if (terms_nodelist != null && terms_nodelist.getLength() > 0)
848 {
849 for (int i = 0; i < terms_nodelist.getLength(); i++)
850 {
851 String termValue = ((Element) terms_nodelist.item(i)).getAttribute("name");
852 String termValueU = null;
853 String termValueL = null;
854
855 if (termValue.length() > 1)
856 {
857 termValueU = termValue.substring(0, 1).toUpperCase() + termValue.substring(1);
858 termValueL = termValue.substring(0, 1).toLowerCase() + termValue.substring(1);
859 }
860 else
861 {
862 termValueU = termValue.substring(0, 1).toUpperCase();
863 termValueL = termValue.substring(0, 1).toLowerCase();
864 }
865
866 query_term_variants.add(termValueU);
867 query_term_variants.add(termValueL);
868 }
869 }
870 }
871 else
872 {
873 for (int i = 0; i < equivalent_terms_nodelist.getLength(); i++)
874 {
875 Element equivalent_terms_element = (Element) equivalent_terms_nodelist.item(i);
876 String[] equivalent_terms = GSXML.getAttributeValuesFromList(equivalent_terms_element, GSXML.NAME_ATT);
877 for (int j = 0; j < equivalent_terms.length; j++)
878 {
879 query_term_variants.add(equivalent_terms[j]);
880 }
881 }
882 }
883
884 ArrayList<ArrayList<HashSet<String>>> phrase_query_term_variants_hierarchy = new ArrayList<ArrayList<HashSet<String>>>();
885
886 Element query_element = GSXML.getNamedElement(metadata_list, GSXML.METADATA_ELEM, GSXML.NAME_ATT, "query");
887 String performed_query = GSXML.getNodeText(query_element) + " ";
888
889 ArrayList<HashSet<String>> phrase_query_p_term_variants_list = new ArrayList<HashSet<String>>();
890 int term_start = 0;
891 boolean in_term = false;
892 boolean in_phrase = false;
893 for (int i = 0; i < performed_query.length(); i++)
894 {
895 char character = performed_query.charAt(i);
896 boolean is_character_letter_or_digit = Character.isLetterOrDigit(character);
897
898 // Has a query term just started?
899 if (in_term == false && is_character_letter_or_digit == true)
900 {
901 in_term = true;
902 term_start = i;
903 }
904
905 // Or has a term just finished?
906 else if (in_term == true && is_character_letter_or_digit == false)
907 {
908 in_term = false;
909 String term = performed_query.substring(term_start, i);
910
911 Element term_element = GSXML.getNamedElement(query_term_list_element, GSXML.TERM_ELEM, GSXML.NAME_ATT, term);
912 if (term_element != null)
913 {
914
915 HashSet<String> phrase_query_p_term_x_variants = new HashSet<String>();
916
917 NodeList term_equivalent_terms_nodelist = term_element.getElementsByTagName("equivTermList");
918 if (term_equivalent_terms_nodelist == null || term_equivalent_terms_nodelist.getLength() == 0)
919 {
920 String termValueU = null;
921 String termValueL = null;
922
923 if (term.length() > 1)
924 {
925 termValueU = term.substring(0, 1).toUpperCase() + term.substring(1);
926 termValueL = term.substring(0, 1).toLowerCase() + term.substring(1);
927 }
928 else
929 {
930 termValueU = term.substring(0, 1).toUpperCase();
931 termValueL = term.substring(0, 1).toLowerCase();
932 }
933
934 phrase_query_p_term_x_variants.add(termValueU);
935 phrase_query_p_term_x_variants.add(termValueL);
936 }
937 else
938 {
939 for (int j = 0; j < term_equivalent_terms_nodelist.getLength(); j++)
940 {
941 Element term_equivalent_terms_element = (Element) term_equivalent_terms_nodelist.item(j);
942 String[] term_equivalent_terms = GSXML.getAttributeValuesFromList(term_equivalent_terms_element, GSXML.NAME_ATT);
943 for (int k = 0; k < term_equivalent_terms.length; k++)
944 {
945 phrase_query_p_term_x_variants.add(term_equivalent_terms[k]);
946 }
947 }
948 }
949 phrase_query_p_term_variants_list.add(phrase_query_p_term_x_variants);
950
951 if (in_phrase == false)
952 {
953 phrase_query_term_variants_hierarchy.add(phrase_query_p_term_variants_list);
954 phrase_query_p_term_variants_list = new ArrayList<HashSet<String>>();
955 }
956 }
957 }
958 // Watch for phrases (surrounded by quotes)
959 if (character == '\"')
960 {
961 // Has a phrase just started?
962 if (in_phrase == false)
963 {
964 in_phrase = true;
965 }
966 // Or has a phrase just finished?
967 else if (in_phrase == true)
968 {
969 in_phrase = false;
970 phrase_query_term_variants_hierarchy.add(phrase_query_p_term_variants_list);
971 }
972
973 phrase_query_p_term_variants_list = new ArrayList<HashSet<String>>();
974 }
975 }
976
977 return highlightQueryTermsInternal(doc, content, query_term_variants, phrase_query_term_variants_hierarchy);
978 }
979
980 /**
981 * Highlights query terms in a piece of text.
982 */
983 private Element highlightQueryTermsInternal(Document doc, String content, HashSet<String> query_term_variants, ArrayList<ArrayList<HashSet<String>>> phrase_query_term_variants_hierarchy)
984 {
985 // Convert the content string to an array of characters for speed
986 char[] content_characters = new char[content.length()];
987 content.getChars(0, content.length(), content_characters, 0);
988
989 // Now skim through the content, identifying word matches
990 ArrayList<WordMatch> word_matches = new ArrayList<WordMatch>();
991 int word_start = 0;
992 boolean in_word = false;
993 boolean preceding_word_matched = false;
994 boolean inTag = false;
995 for (int i = 0; i < content_characters.length; i++)
996 {
997 //We don't want to find words inside HTML tags
998 if (content_characters[i] == '<')
999 {
1000 inTag = true;
1001 continue;
1002 }
1003 else if (inTag && content_characters[i] == '>')
1004 {
1005 inTag = false;
1006 }
1007 else if (inTag)
1008 {
1009 continue;
1010 }
1011
1012 boolean is_character_letter_or_digit = Character.isLetterOrDigit(content_characters[i]);
1013
1014 // Has a word just started?
1015 if (in_word == false && is_character_letter_or_digit == true)
1016 {
1017 in_word = true;
1018 word_start = i;
1019 }
1020
1021 // Or has a word just finished?
1022 else if (in_word == true && is_character_letter_or_digit == false)
1023 {
1024 in_word = false;
1025
1026 // Check if the word matches any of the query term equivalents
1027 String word = new String(content_characters, word_start, (i - word_start));
1028 if (query_term_variants.contains(word))
1029 {
1030 // We have found a matching word, so remember its location
1031 word_matches.add(new WordMatch(word, word_start, i, preceding_word_matched));
1032 preceding_word_matched = true;
1033 }
1034 else
1035 {
1036 preceding_word_matched = false;
1037 }
1038 }
1039 }
1040
1041 // Don't forget the last word...
1042 if (in_word == true)
1043 {
1044 // Check if the word matches any of the query term equivalents
1045 String word = new String(content_characters, word_start, (content_characters.length - word_start));
1046 if (query_term_variants.contains(word))
1047 {
1048 // We have found a matching word, so remember its location
1049 word_matches.add(new WordMatch(word, word_start, content_characters.length, preceding_word_matched));
1050 }
1051 }
1052
1053 ArrayList<Integer> highlight_start_positions = new ArrayList<Integer>();
1054 ArrayList<Integer> highlight_end_positions = new ArrayList<Integer>();
1055
1056 // Deal with phrases now
1057 ArrayList<PartialPhraseMatch> partial_phrase_matches = new ArrayList<PartialPhraseMatch>();
1058 for (int i = 0; i < word_matches.size(); i++)
1059 {
1060 WordMatch word_match = word_matches.get(i);
1061
1062 // See if any partial phrase matches are extended by this word
1063 if (word_match.preceding_word_matched)
1064 {
1065 for (int j = partial_phrase_matches.size() - 1; j >= 0; j--)
1066 {
1067 PartialPhraseMatch partial_phrase_match = partial_phrase_matches.remove(j);
1068 ArrayList phrase_query_p_term_variants_list = phrase_query_term_variants_hierarchy.get(partial_phrase_match.query_phrase_number);
1069 HashSet phrase_query_p_term_x_variants = (HashSet) phrase_query_p_term_variants_list.get(partial_phrase_match.num_words_matched);
1070 if (phrase_query_p_term_x_variants.contains(word_match.word))
1071 {
1072 partial_phrase_match.num_words_matched++;
1073
1074 // Has a complete phrase match occurred?
1075 if (partial_phrase_match.num_words_matched == phrase_query_p_term_variants_list.size())
1076 {
1077 // Check for overlaps by looking at the previous highlight range
1078 if (!highlight_end_positions.isEmpty())
1079 {
1080 int last_highlight_index = highlight_end_positions.size() - 1;
1081 int last_highlight_end = highlight_end_positions.get(last_highlight_index).intValue();
1082 if (last_highlight_end > partial_phrase_match.start_position)
1083 {
1084 // There is an overlap, so remove the previous phrase match
1085 int last_highlight_start = highlight_start_positions.remove(last_highlight_index).intValue();
1086 highlight_end_positions.remove(last_highlight_index);
1087 partial_phrase_match.start_position = last_highlight_start;
1088 }
1089 }
1090
1091 highlight_start_positions.add(new Integer(partial_phrase_match.start_position));
1092 highlight_end_positions.add(new Integer(word_match.end_position));
1093 }
1094 // No, but add the partial match back into the list for next time
1095 else
1096 {
1097 partial_phrase_matches.add(partial_phrase_match);
1098 }
1099 }
1100 }
1101 }
1102 else
1103 {
1104 partial_phrase_matches.clear();
1105 }
1106
1107 // See if this word is at the start of any of the phrases
1108 for (int p = 0; p < phrase_query_term_variants_hierarchy.size(); p++)
1109 {
1110 ArrayList phrase_query_p_term_variants_list = phrase_query_term_variants_hierarchy.get(p);
1111 HashSet phrase_query_p_term_1_variants = (HashSet) phrase_query_p_term_variants_list.get(0);
1112 if (phrase_query_p_term_1_variants.contains(word_match.word))
1113 {
1114 // If this phrase is just one word long, we have a complete match
1115 if (phrase_query_p_term_variants_list.size() == 1)
1116 {
1117 highlight_start_positions.add(new Integer(word_match.start_position));
1118 highlight_end_positions.add(new Integer(word_match.end_position));
1119 }
1120 // Otherwise we have the start of a potential phrase match
1121 else
1122 {
1123 partial_phrase_matches.add(new PartialPhraseMatch(word_match.start_position, p));
1124 }
1125 }
1126 }
1127 }
1128
1129 // Now add the annotation tags into the document at the correct points
1130 Element content_element = doc.createElement(GSXML.NODE_CONTENT_ELEM);
1131
1132 int last_wrote = 0;
1133 for (int i = 0; i < highlight_start_positions.size(); i++)
1134 {
1135 int highlight_start = highlight_start_positions.get(i).intValue();
1136 int highlight_end = highlight_end_positions.get(i).intValue();
1137
1138 // Print anything before the highlight range
1139 if (last_wrote < highlight_start)
1140 {
1141 String preceding_text = new String(content_characters, last_wrote, (highlight_start - last_wrote));
1142 content_element.appendChild(doc.createTextNode(preceding_text));
1143 }
1144
1145 // Print the highlight text, annotated
1146 if (highlight_end > last_wrote)
1147 {
1148 String highlight_text = new String(content_characters, highlight_start, (highlight_end - highlight_start));
1149 Element annotation_element = GSXML.createTextElement(doc, "annotation", highlight_text);
1150 annotation_element.setAttribute("type", "query_term");
1151 content_element.appendChild(annotation_element);
1152 last_wrote = highlight_end;
1153 }
1154 }
1155
1156 // Finish off any unwritten text
1157 if (last_wrote < content_characters.length)
1158 {
1159 String remaining_text = new String(content_characters, last_wrote, (content_characters.length - last_wrote));
1160 content_element.appendChild(doc.createTextNode(remaining_text));
1161 }
1162
1163 return content_element;
1164 }
1165
1166 static private class WordMatch
1167 {
1168 public String word;
1169 public int start_position;
1170 public int end_position;
1171 public boolean preceding_word_matched;
1172
1173 public WordMatch(String word, int start_position, int end_position, boolean preceding_word_matched)
1174 {
1175 this.word = word;
1176 this.start_position = start_position;
1177 this.end_position = end_position;
1178 this.preceding_word_matched = preceding_word_matched;
1179 }
1180 }
1181
1182 static private class PartialPhraseMatch
1183 {
1184 public int start_position;
1185 public int query_phrase_number;
1186 public int num_words_matched;
1187
1188 public PartialPhraseMatch(int start_position, int query_phrase_number)
1189 {
1190 this.start_position = start_position;
1191 this.query_phrase_number = query_phrase_number;
1192 this.num_words_matched = 1;
1193 }
1194 }
1195}
Note: See TracBrowser for help on using the repository browser.