source: other-projects/trunk/gs3-webservices-democlient/src/GS3DemoClient/org/greenstone/gs3client/data/QueryResponseData.java@ 15222

Last change on this file since 15222 was 15222, checked in by ak19, 16 years ago

Greenstone3 web services demo-clientadded to GS3's other-projects

File size: 15.7 KB
Line 
1/**
2 *#########################################################################
3 * QueryResponseData.java - part of the demo-client for Greenstone 3,
4 * of the Greenstone digital library suite from the New Zealand Digital
5 * Library Project at the * University of Waikato, New Zealand.
6 * <BR><BR>
7 * Copyright (C) 2008 New Zealand Digital Library Project
8 * <BR><BR>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * <BR><BR>
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *########################################################################
19 */
20
21package org.greenstone.gs3client.data;
22
23import java.util.Vector;
24import java.util.HashMap;
25
26import org.w3c.dom.Element;
27import org.greenstone.gsdl3.util.GSXML;
28
29
30// 2 classes in file: public-access QueryResponseData and TermData,
31// the second is a static inner class of the first.
32
33/**
34 * Represents the data fields that may be present in a response
35 * to a Query-process request. Specifically, this class keeps track of
36 * all the DocumentNodes returned in response to a query request.
37 * It inherits Map nodeIDsToNodes of (nodeID, NodeData ref) pairs which
38 * maintains the NodeData object refs in order of their insertion into
39 * the Map (LinkedHashMap).
40 * !!! QueryResponseData will only store DocumentNodeData object refs in
41 * the nodeIDsToNodes Map.
42 * An object of this class can be reused after instatiation by calling
43 * setResponseData() with a new query response XML message. This will
44 * first call clear() to clear/release its references to all the old data.
45 * @author ak19
46*/
47public class QueryResponseData extends ResponseData {
48 /* Storing information returned about the results of a query
49 * in a Query response XML message */
50 protected String numDocsMatched;
51 protected String numDocsReturned;
52 protected String queryField; // store it just in case we ever need it
53 protected TermData[] termList;
54 /** Metadata of the query's response - not a documentNode's metadata! */
55 protected HashMap metadataList;
56
57 /** Default constructor */
58 public QueryResponseData() {
59 super();
60 metadataList = new HashMap();
61 }
62
63 /** Resets the internal data members of this QueryResponseData object of
64 * their values so that this QueryResponseData can be reused for the
65 * next Query response message. */
66 public void clear() {
67 super.clear(); // clears Map nodeIDsToNodes of (nodeID, NodeData ref) pairs
68
69 this.metadataList.clear();
70 termList = null;
71 numDocsMatched = numDocsReturned = queryField = "";
72 System.gc();
73 }
74
75 /** Given the response to a query message (XML with root &lt;message&gt;
76 * or &lt;response&gt; tag), a QueryResponseData object is created
77 * to store all the document Identifiers and document data returned
78 * as well as information about the terms that were searched on.
79 * Furthermore, metadata such as the number of Docs that matched and
80 * were returned (if any such are present in the response-message)
81 * are also stored.
82 * It first performs a clear to empty its data members and then fills
83 * them with the new Query response message's data.
84 * @param responseMessageTag is the XML DOM Element representing a query
85 * response XML message.
86 */
87 public void setResponseData(Element responseMessageTag) {
88 this.clear(); // clear anything stored from prev search
89
90 Element listTag = ParseUtil.getFirstDescElementCalled(
91 responseMessageTag, GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
92 if(listTag == null)
93 return; // should not be the case, unless search term was empty
94 // have to go back and deal with that separately
95 Vector v = ParseUtil.getAllChildElementsCalled(listTag,
96 GSXML.DOC_NODE_ELEM);
97 if(v != null) {
98 for(int i = 0; i < v.size(); i++) {
99 DocumentNodeData docNode
100 = new DocumentNodeData((Element)v.get(i));
101 nodeIDsToNodes.put(docNode.nodeID, docNode);
102 }
103 v.clear();
104 v = null;
105 }
106 listTag = null;
107
108 // Get any term elements there might be - there may be none for
109 // some queries' responses
110 listTag = ParseUtil.getFirstDescElementCalled(responseMessageTag,
111 GSXML.TERM_ELEM+GSXML.LIST_MODIFIER);
112 if(listTag != null) {
113 v = ParseUtil.getAllChildElementsCalled(listTag,
114 GSXML.TERM_ELEM);
115 if(v != null) {
116 termList = new TermData[v.size()];
117 for(int i = 0; i < termList.length; i++)
118 termList[i] = new TermData((Element)v.get(i));
119 v.clear();
120 v = null;
121 }
122 listTag = null;
123 }
124
125 // Get any metadata elements there might be - there may be none
126 // for some queries' responses
127 listTag = ParseUtil.getFirstDescElementCalled(responseMessageTag,
128 GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
129 if(listTag != null) {
130 v = ParseUtil.getAllChildElementsCalled(
131 listTag, GSXML.METADATA_ELEM);
132 if(v != null) {
133 // metadataList = new HashMap(v.size());
134 for(int i = 0; i < v.size(); i++) {
135 //create a new metadata object
136 MetaData meta = new MetaData((Element)v.get(i));
137 //add the metadata object into the HashMap keyed by its name
138 metadataList.put(meta.name, meta);
139 }
140 v.clear();
141 v = null;
142 }
143 listTag = null;
144
145 // DIFFERENT FROM MANUAL: in GS3, the value of these metadata.names
146 // are set in the body-text, not set as an attribute-value.
147 // That means, Metadata.value is not what we want, but
148 // Metadata.bodyText
149 MetaData m = (MetaData)metadataList.get("numDocsMatched");
150 this.numDocsMatched = (m == null) ? "" : m.bodyText;
151 m = (MetaData)metadataList.get("numDocsReturned");
152 this.numDocsReturned = (m == null) ? "" : m.bodyText;
153 m = (MetaData)metadataList.get("query");
154 this.queryField = (m == null) ? "" : m.bodyText;
155 } else { //empty metadataList
156 this.numDocsMatched = this.numDocsReturned = this.queryField = "";
157 }
158 }
159
160 /* Accessor methods */
161 /* Information on the search results/query-response: */
162 /** @return the number of matching documents for the query information */
163 public String getNumDocsMatched() { return numDocsMatched; }
164 /** @return the number of documents returned for the query information */
165 public String getNumDocsReturned() { return numDocsReturned; }
166 /** @return the query field information */
167 public String getQueryField() { return queryField; }
168
169 /** @return the list of termData (search terms along with frequency
170 * information, etc.) */
171 public TermData[] getTermList() { return termList; }
172
173
174 /** Given an nodeID, returns the DocumentNodeData object with that nodeID
175 * if any. Otherwise, null is returned.
176 * Superclass has method getNodeForID that returns NodeData instead.
177 * This is just a convenience method.
178 * @param ID is the nodeID of the DocumentNodeData to be returned.
179 * @return the DocumentNodeData object for the given ID or null if not
180 * present. */
181 public DocumentNodeData getDocNodeForID(String ID) {
182 return (DocumentNodeData)nodeIDsToNodes.get(ID);
183 }
184
185 /** @return an array of the DocumentNodeData objects of the documents
186 * returned by the executed Query Response and stored in this
187 * QueryResponseData object. I.e. the documentNodes of the documents in
188 * the search results.
189 * In cases of an error (such as not being able to connect to a collection
190 * like Infomine) this method may return an empty array (length = 0)
191 * if docIDsToDocNodes is empty! */
192 public DocumentNodeData[] getDocumentNodeList() {
193 DocumentNodeData[] docNodeList
194 = new DocumentNodeData[nodeIDsToNodes.size()];
195 nodeIDsToNodes.values().toArray(docNodeList);
196 // populates docNodeList[]
197 return docNodeList;
198 }
199
200 /** @return an array of the IDs of the list of documentNodes maintained
201 * by this QueryResponseData object (the documentNodes of the documents'
202 * in the search results).
203 * In cases of an error (such as not being able to connect to collection
204 * such as Infomine) this method may return an empty array (length = 0)
205 * if docIDsToDocNodes is empty! */
206 public String[] getDocumentNodeIDs() {
207 String[] docNodeIDs = new String[nodeIDsToNodes.size()];
208 nodeIDsToNodes.keySet().toArray(docNodeIDs);
209 // above statement has now populated docNodeIDs[]
210 return docNodeIDs;
211 }
212
213 /** @return metadata of the query's response. This includes values such
214 * as numDocsMatched, numDocsReturned, query. This does not return any
215 * document's metadata! */
216 public String getMetaValueForName(String name) {
217 Object o = metadataList.get(name);
218 if(o != null) {
219 MetaData m = (MetaData)o;
220 return m.bodyText;
221 }
222 return "";
223 }
224
225 /** This method can be called after a DocumentStructureRetrieve request
226 * (for the entire structure of all/many of its documents) has returned
227 * a response. Given the response message (XML) element, this method
228 * attempts to set the document structure for all the documentNodeData
229 * objects it has, using the nodeStructure tags in the
230 * response-message-XML. But only those documentNodeData whose nodeIDs
231 * are mentioned in the response-message-XML are actually set!
232 * This method returns a null vector if the responseMessage XMl does not
233 * contain any &lt;documentNodeList&gt; element with &lt;documentNode&gt;s
234 * each with &lt;nodeStructure&gt; children.
235 * Otherwise it returns a Vector of all the rootNodes of the list of
236 * docNodes that this QueryResponseData object maintains. */
237 public Vector setStructureForDocs(Element messageTag) {
238 Element docList = ParseUtil.getFirstDescElementCalled(
239 messageTag, GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
240 if(docList == null)
241 return null;
242
243 // Get only child <docNode> elements of <docList>, don't get
244 // all descendent <docNode>s here!
245 Vector docNodes = ParseUtil.getAllChildElementsCalled(
246 docList, GSXML.DOC_NODE_ELEM);
247 if(docNodes == null)
248 return null;
249
250 Vector rootNodes = new Vector(docNodes.size());
251 for(int i = 0; i < docNodes.size(); i++) {
252 Element docNode = (Element)docNodes.get(i);
253 // Now find the documentNodeData for which we are going to set
254 // its nodeStructure and fow which we're going to find the root
255 String id = (String)docNode.getAttribute(GSXML.NODE_ID_ATT);
256 DocumentNodeData document
257 = (DocumentNodeData)this.nodeIDsToNodes.get(id);
258
259 // get the <nodeStructure> element
260 Element nodeStructure = ParseUtil.getFirstDescElementCalled(
261 docNode, GSXML.NODE_STRUCTURE_ELEM);
262 if(nodeStructure == null || document == null)
263 continue; // skip to look at next docNode
264 // else
265 // nodeStructure should contain exactly one child: the root doc
266 document.setDescendentsOfRootNode(nodeStructure, nodeIDsToNodes);
267
268 // Now add the root of that document to our vector rootNodes
269 rootNodes.add(document.getRoot());
270 }
271 return rootNodes;
272 }
273
274
275 /** @return some summary info on how the search went. This may include
276 * how many documents matched the query, and how many of them have been
277 * returned. With mult-term queries, the frequencies of each separate
278 * term is also returned for display. */
279 public String toString() {
280 // When running a Form Search on collection gs3mgppdemo through the
281 // browser at http://localhost:8080/greenstone3/
282 // and searching on terms "snail" and "water", the output is like:
283 // "Word count:snail: 433, water: 608
284 // 11 documents matched the query. (11 documents returned.)"
285 // Snail and water are TermData.names, and 433 and 608 are the
286 //frequencies in which those terms occurred in the collection
287
288 StringBuffer buf = new StringBuffer();
289 if(this.termList != null) {
290 buf.append("Word count: ");
291 for(int i = 0; i < termList.length; i++)
292 buf.append(termList[i] + ", "); // calls toString on TermData
293 // get rid of final comma-space and replace with newline
294 buf.replace(buf.length()-2, buf.length(), "\n");
295 }
296 if(!this.numDocsMatched.equals("")) {
297 buf.append(this.numDocsMatched);
298 buf.append(" document(s) matched the query.");
299 }
300 if(!this.numDocsReturned.equals("")) {
301 buf.append(" (");
302 buf.append(this.numDocsReturned);
303 buf.append(" document(s) returned.)");
304 }
305 return buf.toString();
306 }
307
308 /** Static inner class Term represents a &lt;term&gt; XML element
309 * (these are nested in a &lt;termList&gt;) - see manual p. 45:
310 * &lt;term name="str" numDocsMatched="int" freq="int" field="int" stem="int"/&gt;
311 * &lt;equivTermList&gt;
312 * &lt;term name="str" numDocsMatched="int" freq="int" /&gt;
313 * &lt;term name="str" numDocsMatched="int" freq="int" /&gt;
314 * ...
315 * &lt;/equivTermList&gt;
316 * &lt;/term&gt;
317 * Can import this class as import gs3client.QueryResponseData.TermData
318 * and can then use it just as "TermData" (don't need fully qualified name).
319 */
320 public static class TermData {
321 /* Information about the TermData */
322 public final String name;
323 public final String numDocsMatch;
324 public final String freq;
325
326 // Member fields that might not always be set (may be ""):
327 public final String field;
328 public final String stem;
329
330 /** The terms nested inside an &lt;equivTermList&gt; */
331 protected TermData[] equivTermList;
332
333 /** Constructs a Term object to represent the &lt;term&gt; element passed
334 * in here as an argument. Given a &lt;term&gt;&lt;/term&gt; element, it
335 * sets this object's members.
336 * @param termTag is a &lt;term&gt;&lt;/term&gt; element */
337 public TermData(Element termTag) {
338 this.name = termTag.hasAttribute(GSXML.NAME_ATT) ?
339 termTag.getAttribute(GSXML.NAME_ATT) : "";
340 this.field = termTag.hasAttribute("field") ?
341 termTag.getAttribute("field") : "";
342 this.stem = termTag.hasAttribute("stem") ?
343 termTag.getAttribute("stem") : "";
344 this.freq = termTag.hasAttribute("freq") ?
345 termTag.getAttribute("freq") : "";
346 //Integer.parseInt(termTag.getAttribute("freq")) : 0;
347 this.numDocsMatch = termTag.hasAttribute("numDocsMatch") ?
348 termTag.getAttribute("numDocsMatch") : "";
349 //Integer.parseInt(termTag.getAttribute("numDocsMatch")) : 0;
350
351 setEquivTermList(termTag);
352 }
353
354 /** Uses the &lt;equivTermList&gt;...&lt;/equivTermList&gt; tag,
355 * which may cotnain more TermData.
356 * @param termTag is a &lt;term&gt;&lt;/term&gt; element that may
357 * contain a &lt;equivTermList&gt;...&lt;/equivTermList&gt; element */
358 public void setEquivTermList(Element termTag) {
359 Element equivList = ParseUtil.getFirstChildElementCalled(
360 termTag, "equivTerm"+GSXML.LIST_MODIFIER);
361 if(equivList != null) {
362 Vector equivTerms = ParseUtil.getAllChildElementsCalled(
363 equivList, GSXML.TERM_ELEM); // <term> children
364 if(equivTerms != null) {
365 equivTermList = new TermData[equivTerms.size()];
366 for(int i = 0; i < equivTermList.length; i++)
367 equivTermList[i] = new TermData(
368 (Element)equivTerms.get(i));
369 }
370 }
371 }
372
373 /** @return any list of &lt;equivTermList&gt;...&lt;/equivTermList&gt;
374 * TermData maintained by this TermData object. Null if there are none. */
375 public TermData[] getEquivTermList() { return equivTermList; }
376
377 // Frequency is always a number
378 /** @return a String representation of this TermData: the name
379 * and frequency */
380 public String toString() { return this.name + ": " + this.freq; }
381
382 /** @return a String displaying the member contents of this TermData.
383 * Useful for debugging purposes. */
384 public String show() {
385 StringBuffer buf = new StringBuffer("name: " + this.name);
386 buf.append(" numDocsMatched: " + this.numDocsMatch);
387 buf.append(" freq: " + freq);
388 buf.append(" field: " + field);
389 buf.append(" stem: " + stem + "\n");
390 if(equivTermList != null) {
391 buf.append("EquivTermList:\n");
392 for(int i = 0; i < equivTermList.length; i++)
393 buf.append(" " + equivTermList[i].show() + "\n");
394 }
395 return buf.toString();
396 }
397 }
398}
Note: See TracBrowser for help on using the repository browser.