source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/GS2BrowseAction.java@ 31873

Last change on this file since 31873 was 31873, checked in by kjdon, 7 years ago

modified so that we always get the siblings in any HLIsts, not jsut the top level ones. So if you have two hlists, eg years, then months, both will stay expanded as you browse through

  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1package org.greenstone.gsdl3.action;
2
3import java.io.Serializable;
4import java.util.HashMap;
5import java.util.HashSet;
6
7import org.apache.log4j.Logger;
8import org.greenstone.gsdl3.util.GSParams;
9import org.greenstone.gsdl3.util.GSPath;
10import org.greenstone.gsdl3.util.GSXML;
11import org.greenstone.gsdl3.util.GSXSLT;
12import org.greenstone.gsdl3.util.OID;
13import org.greenstone.gsdl3.util.UserContext;
14import org.greenstone.gsdl3.util.XMLConverter;
15import org.w3c.dom.Document;
16import org.w3c.dom.Element;
17import org.w3c.dom.Node;
18import org.w3c.dom.NodeList;
19
20/** action for GS2 style classifier browsing */
21public class GS2BrowseAction extends Action
22{
23
24 public static final String CLASSIFIER_ARG = "cl";
25
26 public static final String DESCENDANTS_ARG = "descendants";
27
28 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.action.GS2BrowseAction.class.getName());
29
30 /** process the request */
31 public Node process(Node message_node)
32 {
33 Element message = GSXML.nodeToElement(message_node);
34 Document doc = message.getOwnerDocument();
35
36 // get the request - assume only one
37 Element request = (Element) GSXML.getChildByTagName(message, GSXML.REQUEST_ELEM);
38
39 // the result
40 Element result = doc.createElement(GSXML.MESSAGE_ELEM);
41 Element response = classifierBrowse(request);
42 result.appendChild(response);
43 return result;
44 }
45
46 protected Element classifierBrowse(Element request)
47 {
48 Document doc = request.getOwnerDocument();
49 Element page_response = doc.createElement(GSXML.RESPONSE_ELEM);
50
51 // extract the params from the cgi-request, and check that we have a coll specified
52 Element cgi_paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
53 HashMap<String, Serializable> params = GSXML.extractParams(cgi_paramList, false);
54
55 String service_name = (String) params.get(GSParams.SERVICE);
56 String collection = (String) params.get(GSParams.COLLECTION);
57 if (collection == null || collection.equals(""))
58 {
59 logger.error("classifierBrowse, need to specify a collection!");
60 return page_response;
61 }
62
63 boolean get_descendants = false;
64 String descendants_str = (String) params.get(DESCENDANTS_ARG);
65 if (descendants_str != null && descendants_str.equals("1"))
66 {
67 get_descendants = true;
68 }
69
70 UserContext userContext = new UserContext(request);
71 String to = GSPath.appendLink(collection, service_name);
72
73 // the first part of the response is the service description
74 // for now get this again from the service.
75 // this should be cached somehow later on.
76
77 Element info_message = doc.createElement(GSXML.MESSAGE_ELEM);
78 Element info_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, to, userContext);
79 info_message.appendChild(info_request);
80
81 // also get the format stuff now if there is some
82 Element format_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_FORMAT, to, userContext);
83 info_message.appendChild(format_request);
84 // process the requests
85
86 Element info_response = (Element) this.mr.process(info_message);
87
88 // the two responses
89 NodeList responses = info_response.getElementsByTagName(GSXML.RESPONSE_ELEM);
90 Element service_response = (Element) responses.item(0);
91 Element format_response = (Element) responses.item(1);
92
93 Element service_description = (Element) GSXML.getChildByTagName(service_response, GSXML.SERVICE_ELEM);
94 page_response.appendChild(doc.importNode(service_description, true));
95
96 //append site metadata
97 addSiteMetadata(page_response, userContext);
98 addInterfaceOptions(page_response);
99
100 // if rt=d, then we are just displaying the service
101 String request_type = (String) params.get(GSParams.REQUEST_TYPE);
102 if (request_type.equals("d"))
103 {
104 //return the page that we have so far
105 return page_response;
106 }
107
108 // get the node that the user has clicked on
109 String classifier_node = (String) params.get(CLASSIFIER_ARG);
110
111 // if the node is not defined, return the page that we have so far
112 if (classifier_node == null || classifier_node.equals(""))
113 {
114 return page_response;
115 }
116
117 // the id of the classifier is the top id of the selected node
118 String top_id = OID.getTop(classifier_node);
119 HashSet<String> doc_meta_names = new HashSet<String>();
120 HashSet<String> class_meta_names = new HashSet<String>();
121 // add in the defaults
122 doc_meta_names.add("Title");
123 doc_meta_names.add("root_assocfilepath");
124 class_meta_names.add("Title");
125
126 // add the format info into the response
127 Element format_elem = (Element) GSXML.getChildByTagName(format_response, GSXML.FORMAT_ELEM);
128 if (format_elem != null)
129 {
130 // find the one for the classifier we are in
131 Element this_format = GSXML.getNamedElement(format_elem, GSXML.CLASSIFIER_ELEM, GSXML.NAME_ATT, top_id);
132 if (this_format == null)
133 {
134 this_format = (Element) GSXML.getChildByTagName(format_elem, GSXML.DEFAULT_ELEM);
135 }
136
137 if (this_format != null)
138 {
139 Element global_format_elem = (Element) GSXML.getChildByTagName(format_response, GSXML.GLOBAL_FORMAT_ELEM);
140 if (global_format_elem != null)
141 {
142 GSXSLT.mergeFormatElements(this_format, global_format_elem, false);
143 }
144
145 Element new_format = GSXML.duplicateWithNewName(doc, this_format, GSXML.FORMAT_ELEM, false);
146 extractMetadataNames(new_format, doc_meta_names, class_meta_names);
147
148 Element extraMetaListElem = (Element) GSXML.getChildByTagName(request, GSXML.EXTRA_METADATA + GSXML.LIST_MODIFIER);
149 if (extraMetaListElem != null)
150 {
151 NodeList extraMetaList = extraMetaListElem.getElementsByTagName(GSXML.EXTRA_METADATA);
152 for (int i = 0; i < extraMetaList.getLength(); i++)
153 {
154 doc_meta_names.add(((Element) extraMetaList.item(i)).getAttribute(GSXML.NAME_ATT));
155 }
156 }
157
158 // set the format type
159 new_format.setAttribute(GSXML.TYPE_ATT, "browse");
160
161 page_response.appendChild(new_format);
162 }
163 }
164
165 // find out if this classifier is horizontal at top
166 Element class_list = (Element) GSXML.getChildByTagName(service_description, GSXML.CLASSIFIER_ELEM + GSXML.LIST_MODIFIER);
167 Element this_classifier = GSXML.getNamedElement(class_list, GSXML.CLASSIFIER_ELEM, GSXML.NAME_ATT, top_id);
168
169 // get the browse structure for the selected node
170 Element classify_message = doc.createElement(GSXML.MESSAGE_ELEM);
171 Element classify_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
172 classify_message.appendChild(classify_request);
173
174 //Create a parameter list to specify the required structure information
175 // for now, always get ancestors and children
176 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
177 classify_request.appendChild(param_list);
178 Element param = doc.createElement(GSXML.PARAM_ELEM);
179 param_list.appendChild(param);
180 param.setAttribute(GSXML.NAME_ATT, "structure");
181 param.setAttribute(GSXML.VALUE_ATT, "ancestors");
182 param = doc.createElement(GSXML.PARAM_ELEM);
183 param_list.appendChild(param);
184 param.setAttribute(GSXML.NAME_ATT, "structure");
185 param.setAttribute(GSXML.VALUE_ATT, "children");
186
187 if (get_descendants)
188 {
189 param = doc.createElement(GSXML.PARAM_ELEM);
190 param_list.appendChild(param);
191 param.setAttribute(GSXML.NAME_ATT, "structure");
192 param.setAttribute(GSXML.VALUE_ATT, "descendants");
193 }
194
195
196 // put the classifier node into a classifier node list
197 Element classifier_list = doc.createElement(GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
198 Element classifier = doc.createElement(GSXML.CLASS_NODE_ELEM);
199 classifier.setAttribute(GSXML.NODE_ID_ATT, classifier_node);
200 classifier_list.appendChild(classifier);
201 classify_request.appendChild(classifier_list);
202
203 // process the request
204 Element classify_response = (Element) this.mr.process(classify_message);
205
206 String path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
207 Element class_node_list = (Element) GSXML.getNodeByPath(classify_response, path);
208
209 path = GSPath.appendLink(GSXML.CLASS_NODE_ELEM, GSXML.NODE_STRUCTURE_ELEM);
210 // assume that we always get back the top level CL1 node - this becomes the page_classifier node
211 path = GSPath.appendLink(path, GSXML.CLASS_NODE_ELEM);
212 Element cl_structure = (Element) GSXML.getNodeByPath(class_node_list, path);
213 if (cl_structure == null)
214 {
215 logger.error("classifier structure request returned no structure");
216 return page_response;
217 }
218 Document cl_struct_doc = cl_structure.getOwnerDocument();
219
220 //If the user is viewing a horizontal classifier then we need to get extra information - siblings along the horizontal
221
222 // cl_structure is the classifier structure containing ancestors and children of current node.
223 // this test means that we won't do the right thing if we happened to have an HList inside a VList. Should really be testing to see if any nodes are HLists...
224 if (cl_structure.getAttribute(GSXML.CHILD_TYPE_ATT).equals(GSXML.HLIST))
225 {
226
227 //If we have a horizontal classifier and we have had the top-level node requested (e.g. CL1, CL2 etc.)
228 //then we want to get the children of the first classifier node (e.g. the children of CL2.1)
229 if (OID.isTop(classifier_node)) {
230
231 boolean firstChildIsClassifierNode = false;
232 NodeList classifierChildrenNodes = GSXML.getChildrenByTagName(cl_structure, GSXML.CLASS_NODE_ELEM);
233 for (int i = 0; i < classifierChildrenNodes.getLength(); i++)
234 {
235 Element currentChild = (Element) classifierChildrenNodes.item(i);
236 if (currentChild.getAttribute(GSXML.NODE_ID_ATT).endsWith(".1"))
237 {
238 firstChildIsClassifierNode = true;
239 }
240 }
241
242 if (firstChildIsClassifierNode)
243 {
244 // get the children of the first child
245 Element childStructure = getClassifierChildrenFromID(doc, classifier_node + ".1", request, collection, service_name);
246
247 // and now insert it into the structure we already have
248 NodeList nodesToSearch = cl_structure.getElementsByTagName(GSXML.CLASS_NODE_ELEM);
249 for (int i = 0; i < nodesToSearch.getLength(); i++)
250 {
251 Element currentElem = (Element) nodesToSearch.item(i);
252 if (currentElem.getAttribute(GSXML.NODE_ID_ATT).equals(classifier_node + ".1"))
253 {
254 Element parent = (Element) currentElem.getParentNode();
255 parent.insertBefore(cl_struct_doc.importNode(childStructure, true), currentElem);
256 parent.removeChild(currentElem);
257 break;
258 }
259 }
260 }
261 } // if OID.isTop(classifier_node)
262 //If we have a horizontal classifier and we have NOT had the top-level node requested then we need to
263 //make sure we get the full list of top-level children to display (e.g. if the user has requested
264 //CL2.1.1 we also need to make sure we have CL2.2, CL2.3, CL2.4 etc.)
265 else
266 {
267 // we need to get siblings of any HList nodes so the HLists remain expanded in the display
268 // start at the bottom (the specified element) and work up teh list of parents expanding any that have hlists
269 NodeList elems = GSXML.getNamedElements(cl_structure, GSXML.CLASS_NODE_ELEM, GSXML.NODE_ID_ATT, classifier_node);
270 Element current_node = null;
271 String current_id = classifier_node;
272 if (elems != null) {
273 // there should only be one node in the list
274 current_node = (Element)elems.item(0);
275 }
276 if (current_node != null) {
277 Element parent_node = (Element)current_node.getParentNode();
278 while(parent_node != null && parent_node.getNodeName().equals(GSXML.CLASS_NODE_ELEM)) {
279 if (((Element)parent_node).getAttribute(GSXML.CHILD_TYPE_ATT).equals(GSXML.HLIST)) {
280 // get the children of the parent
281 Element new_parent_elem = getClassifierChildrenFromID(cl_struct_doc, parent_node.getAttribute(GSXML.NODE_ID_ATT), request, collection, service_name);
282
283 // put current node into this new structure
284 Element to_be_replaced = GSXML.getNamedElement(new_parent_elem, GSXML.CLASS_NODE_ELEM, GSXML.NODE_ID_ATT, current_id);
285 if (to_be_replaced != null) {
286 new_parent_elem.insertBefore(current_node, to_be_replaced);
287 new_parent_elem.removeChild(to_be_replaced);
288 if (OID.isTop(parent_node.getAttribute(GSXML.NODE_ID_ATT))) {
289 // we can't go up any further.
290 cl_structure = new_parent_elem;
291 parent_node = null;
292
293 } else {
294 Element next_parent = (Element)parent_node.getParentNode();
295 next_parent.insertBefore(new_parent_elem, parent_node );
296 next_parent.removeChild(parent_node);
297 current_node = new_parent_elem;
298 current_id = current_node.getAttribute(GSXML.NODE_ID_ATT);
299 parent_node = next_parent;
300 }
301 } else {
302 // something went wrong, we'll stop this
303 parent_node = null;
304 }
305 } else {
306 current_node = parent_node;
307 current_id = current_node.getAttribute(GSXML.NODE_ID_ATT);
308 parent_node = (Element)current_node.getParentNode();
309 }
310 }
311 } // if current_node != null
312
313 }
314 }
315
316 Element page_classifier = null;
317 // add the single classifier node as the page classifier
318 page_classifier = GSXML.duplicateWithNewName(doc, cl_structure, GSXML.CLASSIFIER_ELEM, true);
319 page_response.appendChild(page_classifier);
320 page_classifier.setAttribute(GSXML.NAME_ATT, top_id);
321
322 // get the metadata for each classifier node,
323 // then for each document node
324
325 Element metadata_message = doc.createElement(GSXML.MESSAGE_ELEM);
326
327 boolean did_classifier = false;
328 boolean did_documents = false;
329
330 // if there are classifier nodes
331 // create a metadata request for the classifier, and add it to
332 // the the message
333 NodeList cl_nodes = page_classifier.getElementsByTagName(GSXML.CLASS_NODE_ELEM);
334
335 if (cl_nodes.getLength() > 0)
336 {
337 did_classifier = true;
338 Element cl_meta_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, to + "MetadataRetrieve", userContext);
339 metadata_message.appendChild(cl_meta_request);
340
341 Element new_cl_nodes_list = doc.createElement(GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
342 cl_meta_request.appendChild(new_cl_nodes_list);
343
344 for (int c = 0; c < cl_nodes.getLength(); c++)
345 {
346
347 Element cl = doc.createElement(GSXML.CLASS_NODE_ELEM);
348 cl.setAttribute(GSXML.NODE_ID_ATT, ((Element) cl_nodes.item(c)).getAttribute(GSXML.NODE_ID_ATT));
349 new_cl_nodes_list.appendChild(cl);
350 }
351
352 // create and add in the param list - for now get all the metadata
353 // should be based on info sent in from the recept, and the
354 // format stuff
355 Element cl_param_list = createMetadataParamList(doc,class_meta_names);
356 cl_meta_request.appendChild(cl_param_list);
357
358 }
359
360 // if there are document nodes in the classification (happens
361 // sometimes), create a second request for document metadata and
362 // append to the message
363 NodeList doc_nodes = page_classifier.getElementsByTagName(GSXML.DOC_NODE_ELEM);
364 if (doc_nodes.getLength() > 0)
365 {
366 did_documents = true;
367 Element doc_meta_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, GSPath.appendLink(collection, "DocumentMetadataRetrieve"), userContext);
368 metadata_message.appendChild(doc_meta_request);
369
370 Element doc_list = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
371 doc_meta_request.appendChild(doc_list);
372
373 for (int c = 0; c < doc_nodes.getLength(); c++)
374 {
375
376 Element d = doc.createElement(GSXML.DOC_NODE_ELEM);
377 d.setAttribute(GSXML.NODE_ID_ATT, ((Element) doc_nodes.item(c)).getAttribute(GSXML.NODE_ID_ATT));
378 doc_list.appendChild(d);
379 }
380
381 // create and add in the param list - add all for now
382 Element doc_param_list = createMetadataParamList(doc,doc_meta_names);
383 doc_meta_request.appendChild(doc_param_list);
384
385 }
386
387 // process the metadata requests
388 Element metadata_response = (Element) this.mr.process(metadata_message);
389 if (did_classifier)
390 {
391 // the classifier one will be the first response
392 // add the metadata lists for each node back into the
393 // page_classifier nodes
394 path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
395 Node pathNode = GSXML.getNodeByPath(metadata_response, path);
396 if (pathNode == null)
397 {
398 return page_response;
399 }
400 //NodeList meta_response_cls = (Element)pathNode.getChildNodes(); // can't handle empty elements from converting formatted strings (with empty newlines) into XML
401 NodeList meta_response_cls = ((Element) pathNode).getElementsByTagName(GSXML.CLASS_NODE_ELEM);
402 for (int i = 0; i < cl_nodes.getLength(); i++)
403 {
404 GSXML.mergeMetadataLists(cl_nodes.item(i), meta_response_cls.item(i));
405 }
406 }
407
408 if (did_documents)
409 {
410 NodeList meta_response_docs = null;
411 if (!did_classifier)
412 {
413 // its the first response
414 path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
415 Node pathNode = GSXML.getNodeByPath(metadata_response, path);
416 if (pathNode == null)
417 {
418 return page_response;
419 }
420
421 meta_response_docs = pathNode.getChildNodes();
422
423 }
424 else
425 { // its the second response
426 Node nodes = GSXML.getChildByTagName(metadata_response.getElementsByTagName(GSXML.RESPONSE_ELEM).item(1), GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
427 if (nodes == null)
428 {
429 return page_response;
430 }
431 meta_response_docs = nodes.getChildNodes();
432 }
433
434 for (int i = 0; i < doc_nodes.getLength(); i++)
435 {
436 GSXML.mergeMetadataLists(doc_nodes.item(i), meta_response_docs.item(i));
437 }
438 }
439
440 logger.debug("(GS2BrowseAction) Page:\n" + this.converter.getPrettyString(page_response));
441 return page_response;
442 }
443
444 private Element getClassifierChildrenFromID(Document doc, String id, Element request, String collection, String service_name)
445 {
446 UserContext userContext = new UserContext(request);
447 String to = GSPath.appendLink(collection, service_name);
448
449 Document msg_doc = XMLConverter.newDOM();
450 Element firstClassifierNodeChildrenMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
451 Element firstClassifierNodeChildrenRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
452 firstClassifierNodeChildrenMessage.appendChild(firstClassifierNodeChildrenRequest);
453
454 Element paramList = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
455 firstClassifierNodeChildrenRequest.appendChild(paramList);
456
457 // Element ancestorParam = doc.createElement(GSXML.PARAM_ELEM);
458 // paramList.appendChild(ancestorParam);
459 // ancestorParam.setAttribute(GSXML.NAME_ATT, "structure");
460 // ancestorParam.setAttribute(GSXML.VALUE_ATT, "ancestors");
461
462 Element childrenParam = msg_doc.createElement(GSXML.PARAM_ELEM);
463 paramList.appendChild(childrenParam);
464 childrenParam.setAttribute(GSXML.NAME_ATT, "structure");
465 childrenParam.setAttribute(GSXML.VALUE_ATT, "children");
466
467 Element classifierToGetList = msg_doc.createElement(GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
468 Element classifierToGet = msg_doc.createElement(GSXML.CLASS_NODE_ELEM);
469 classifierToGet.setAttribute(GSXML.NODE_ID_ATT, id);
470 classifierToGetList.appendChild(classifierToGet);
471 firstClassifierNodeChildrenRequest.appendChild(classifierToGetList);
472
473 Element firstClassifierNodeChildrenResponse = (Element) this.mr.process(firstClassifierNodeChildrenMessage);
474
475 String nsPath = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.CLASS_NODE_ELEM + GSXML.LIST_MODIFIER);
476 Element topClassifierNode = (Element) GSXML.getNodeByPath(firstClassifierNodeChildrenResponse, nsPath);
477 nsPath = GSPath.appendLink(GSXML.CLASS_NODE_ELEM, GSXML.NODE_STRUCTURE_ELEM);
478 nsPath = GSPath.appendLink(nsPath, GSXML.CLASS_NODE_ELEM);
479 Element childStructure = (Element) GSXML.getNodeByPath(topClassifierNode, nsPath);
480 return (Element)doc.importNode(childStructure, true);
481 }
482
483 protected void extractMetadataNames(Element new_format, HashSet<String> doc_meta_names, HashSet<String> class_meta_names)
484 {
485 NodeList templates = new_format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "template");
486 for (int i = 0; i < templates.getLength(); i++)
487 {
488 Element template = (Element) templates.item(i);
489 String match = template.getAttribute(GSXML.MATCH_ATT);
490 String name = template.getAttribute(GSXML.NAME_ATT);
491 if (match != null && match.length() > 0)
492 {
493 if (match.startsWith("documentNode"))
494 {
495 getRequiredMetadataNames(template, doc_meta_names);
496 }
497 else if (match.startsWith("classifierNode")) // not match.equals, as we want to match nodes like: classifierNode[@classifierStyle = 'VList']
498 {
499 getRequiredMetadataNames(template, class_meta_names);
500 }
501 }
502 else
503 {
504 getRequiredMetadataNames(template, doc_meta_names);
505 getRequiredMetadataNames(template, class_meta_names);
506 }
507 }
508 }
509
510}
Note: See TracBrowser for help on using the repository browser.