source: other-projects/gs3-webservices-java-client/trunk/src/GS3DemoClient/org/greenstone/gs3client/QueryFormControl.java@ 22299

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

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

File size: 18.9 KB
Line 
1/**
2 *#########################################################################
3 * QueryFormControl.java - part of the demo-client for Greenstone 3, of the
4 * Greenstone digital library suite from the New Zealand Digital Library
5 * 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;
22
23import java.awt.Color;
24import java.awt.FlowLayout;
25import java.awt.GridBagLayout;
26import java.awt.GridBagConstraints;
27import java.util.HashMap;
28import java.util.Vector;
29
30import javax.swing.BorderFactory;
31import javax.swing.JCheckBox;
32import javax.swing.JComboBox;
33import javax.swing.JComponent;
34import javax.swing.JLabel;
35import javax.swing.JList;
36import javax.swing.JPanel;
37import javax.swing.JTextField;
38import javax.swing.border.Border;
39
40import org.greenstone.gs3client.data.QueryFormData;
41import org.greenstone.gs3client.data.QueryFormData.QueryFormParam;
42import org.greenstone.gsdl3.util.GSXML;
43import org.w3c.dom.Element;
44import org.w3c.dom.NodeList;
45
46/**
47 * Represents a control in a query form. This wrapper class is a subclass of
48 * QueryFormParam that presents its QueryFormParam details as an appropriate
49 * GUI item *with* label (based on the QueryFormParam's type) for display
50 * in its own JPanel.
51 * This wrapper class is a widget that knows to display this QueryFormParam --
52 * and any child &lt;param&gt;s or &lt;option&gt;s -- in its own JPanel.
53 * @author ak19
54*/
55class QueryFormControl extends QueryFormParam {
56 /** the widget that's a visual representation of this QueryFormParam */
57 protected JComponent ref;
58 /* Width in units of this form control's component
59 * Width only used when calculating layout for type=MULTI */
60 int width; // 1 to 3 units: 1 for checkboxes, 2 for combos,
61 // 3 for lists and textboxes. 0 for everything else.
62
63 /** Constructor that creates a QueryFormControl for a &lt;param&gt; element
64 * @param param - a &lt;param&gt; element in a Query Service's describe
65 * response XML message */
66 public QueryFormControl(Element param) {
67 super(param);
68
69 // work out the widths (else it remains at 0)
70 if(this.type.equals(GSXML.PARAM_TYPE_INTEGER)
71 || this.type.equals(GSXML.PARAM_TYPE_BOOLEAN))
72 {
73 width = 1;
74 } else if(this.type.equals(GSXML.PARAM_TYPE_ENUM_SINGLE)
75 || this.type.equals(GSXML.PARAM_TYPE_ENUM_MULTI))
76 {
77 width = 2;
78 } else if(this.type.equals(GSXML.PARAM_TYPE_STRING))
79 {
80 width = 3;
81 } else { // all other types of controls have 0 width
82 width = 0;
83 }
84 }
85
86 /** Constructor that creates a QueryFormControl for a &lt;param&gt; element
87 * inside a panel.
88 * @param param - a &lt;param&gt; element in a Query Service's describe
89 * response XML message
90 * @param parentPanel - the panel inside which this QueryFormControl is to
91 * display itself. */
92 public QueryFormControl(Element param, JPanel parentPanel) {
93 super(param);
94 createWidget(param); // This will set this.ref
95 // In type=multi case, ref is a panel)
96
97 // The following if statements have to be done in this order
98
99 // For all other values of this.type:
100 // Now, in both textfield and enum cases (but not the others that
101 // haven't been dealt with yet like INVISIBLE, FILE, ENUM, etc),
102 // add the newly created GUI obj (in ref) to the parentPanel.
103 // And attach a label if instructed to do so.
104 if(this.ref != null) {
105 JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
106 panel.add(new JLabel(this.displayItem));
107 panel.add(this.ref);
108 parentPanel.add(panel);
109 }
110
111 // this.ref is still null for MultiPanel, until now when we create it:
112 if(this.type.equals(GSXML.PARAM_TYPE_MULTI)) {
113 createMultiPanel(param, parentPanel);
114 //width = -1;
115 }
116
117 // finally let's change the colours - as long as ref exists
118 // (invisible QueryFormData have no ref Component, so check for
119 // null case)
120 if(this.ref != null) {
121 ColourCombo.changeColor(this.ref.getComponents());
122 ColourCombo.changeColor(this.ref);
123 ColourCombo.changeAncestorColor(this.ref);
124 }
125 }
126
127 /** Creates the appropriate form control for the &lt;param&gt; element,
128 * based on its type.
129 * @param param - a &lt;param&gt; element in a Query Service's describe
130 * response XML message */
131 public void createWidget(Element param)
132 {
133 if(this.type.equals(GSXML.PARAM_TYPE_INTEGER)
134 || this.type.equals(GSXML.PARAM_TYPE_STRING))
135 {
136 createTextField();
137 }
138 else if (this.type.equals(GSXML.PARAM_TYPE_BOOLEAN))
139 {
140 createBooleanField();
141 }
142 else if(this.type.equals(GSXML.PARAM_TYPE_ENUM_SINGLE)
143 || this.type.equals(GSXML.PARAM_TYPE_ENUM_MULTI))
144 {
145 createEnumListing(param); // either a combo or list
146 }
147 } // end method setUpWidget()
148
149 /** Creates check box controls for the &lt;param&gt; elements whose
150 * types are GSXML.PARAM_TYPE_BOOLEAN. */
151 public void createBooleanField() {
152 JCheckBox checkbox = new JCheckBox();
153 if(def.equals("1"))
154 checkbox.setSelected(true);
155 this.ref = checkbox;
156 }
157
158 /** Creates text fields for the &lt;param&gt; elements whose types are
159 * GSXML.PARAM_TYPE_STRING or GSXML.PARAM_TYPE_INTEGER). */
160 public void createTextField() {
161 int cols = this.type.equals(GSXML.PARAM_TYPE_INTEGER) ? 4 : 15;
162 this.ref = new JTextField(cols);
163 if(!def.equals(""))
164 ((JTextField)this.ref).setText(def);
165 }
166
167 /** Creates combo boxes/drop-downs for the &lt;param&gt; elements
168 * whose types are GSXML.PARAM_TYPE_ENUM_SINGLE and listboxes for those
169 * whose types are GSXML.PARAM_TYPE_ENUM_MULTI.
170 * @param param - a &lt;param&gt; element in a Query Service's describe
171 * response XML message */
172 public void createEnumListing(Element param)
173 {
174 NodeList nl = param.getElementsByTagName(GSXML.PARAM_OPTION_ELEM);
175 // If no <option> elements, we do nothing
176 if(nl != null && nl.getLength() > 0) {
177 subElements = new QueryFormData[nl.getLength()];
178 Vector selectedIndices = new Vector(subElements.length);
179 for(int i = 0; i < subElements.length; i++){
180 Element e = (Element)nl.item(i);
181 subElements[i] = new QueryFormData(e);
182 // create list items for each <option>
183
184 // Keep track of which options should be selected by
185 // default, by storing these indices.
186 // Don't use def.equals() but def.indexOf():
187 // i.e. select those items where this.name occurs in def,
188 // because def is a comma separated list of default values
189 if(def.indexOf(subElements[i].name) != -1)
190 selectedIndices.add(new Integer(i));
191 }
192 // Now create the widgets
193 if(this.type.equals(GSXML.PARAM_TYPE_ENUM_MULTI)) {
194 this.ref = new JList(subElements);
195 // by default, JList is set to what we want:
196 // MULTIPLE_INTERVAL_SELECTION
197 if(!def.equals("")) {
198 int[] selIndices = new int[selectedIndices.size()];
199 for(int i = 0; i < selectedIndices.size(); i++)
200 selIndices[i] =
201 ((Integer)selectedIndices.get(i)).intValue();
202
203 ((JList)this.ref).setSelectedIndices(selIndices);
204 }
205 }
206 else { // ENUM_SINGLE and BOOLEAN get a JComboBox
207 this.ref = new JComboBox(subElements);
208 if(!def.equals("")) { // for JCombobox, only 1 item default at a time
209 ((JComboBox)this.ref).setSelectedIndex(
210 ((Integer)selectedIndices.get(0)).intValue());
211 }
212 }
213 }
214 }
215
216 /**
217 * A multi-panel is created for a *set* of controls that occur more than
218 * once.
219 * Processes &lt;param type="multi"&gt; in a Query Service's describe
220 * response XML. (I.e. the type is GSXML.PARAM_TYPE_MULTI)
221 * Complicated case: can have 'occurs' field set. And this &lt;param&gt;
222 * element can have multiple &lt;param&gt;s itself, each with the 'ignore'
223 * possibly set and each with its own &lt;option&gt; elements.
224 * @param param - a &lt;param&gt; element in a Query Service's describe
225 * response XML message
226 * @param parentPanel - the panel inside which the multipanel is to display
227 * itself.
228 */
229 public void createMultiPanel(Element param, JPanel parentPanel) {
230 // Create the widgets for MULTI at least once:
231 int num_occurs = occurs.equals("") ? 1 : Integer.parseInt(occurs);
232 NodeList nl = param.getElementsByTagName(GSXML.PARAM_ELEM);
233
234 // This panel is now a multi panel: contains multiple associated
235 // widgets each in their own panels along with their labels.
236 // All *grouped* sub <param> elements in a panel each, and these
237 // panels on top of each other.
238 GridBagLayout gbLayout = new GridBagLayout();
239 GridBagConstraints gbConstraints = new GridBagConstraints();
240 // fixed for all components
241 //gbConstraints.anchor = GridBagConstraints.NORTHWEST;
242 gbConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
243 gbConstraints.ipadx = 5;
244 gbConstraints.ipady = 3;
245 gbConstraints.fill = GridBagConstraints.BOTH;
246 gbConstraints.weightx = 0;
247 gbConstraints.weighty = 0;
248 JPanel panel = new JPanel(gbLayout);
249 //new GraphPaperLayout(new Dimension(nl.getLength(), num_occurs+1)));
250 // num_occurs + 1: extra row to allow for the row of headings
251 Border lineBorder = BorderFactory.createLineBorder(Color.GRAY);
252 panel.setBorder(lineBorder);
253 this.ref = panel; // this QueryFormControl objects JComp ref variable
254
255 // For GridBagLayout calculations
256 int[] colWidths = null; // store the width of each column
257 int colPosition = 0; // the column position of the widget in the layout
258
259 // Instantiating and positioning the Query Form controls
260 if(nl != null && nl.getLength() > 0) {
261 colWidths = new int[nl.getLength()]; // to store the width of the columns
262 // For each <param> child of Element param, we create a QueryFormParam
263 // object which is stored in the subElements array
264 // We do this 'occurs' number of times (minimum 'occurs' is once)
265 subElements = new QueryFormParam[nl.getLength() * num_occurs];
266 for(int j = 1; j <= num_occurs; j++) {
267 // Now process all <param> elements and put them into this.ref panel
268 int numParams = nl.getLength();
269 for(int i = 0; i < numParams; i++){
270 Element e = (Element)nl.item(i); // get the child
271 QueryFormControl paramEl = new QueryFormControl(e);
272 subElements[(j-1) * numParams+i] = paramEl;
273
274 // GridBagLayout calculations
275 if(j == 1) { // When processing the first row, store the col widths
276 colWidths[i] = paramEl.width;
277 }
278 // Calculate the column position of the form control in the layout
279 // ColPosition of current control is the sum of the widths of all
280 // previous controls (all controls to the left of it in this row)
281 if(i > 0) {
282 // increment column position of this control by previous
283 // widget's width
284 colPosition += colWidths[i-1];
285 } else { // i == 0, reset colPosition to 0
286 colPosition = 0;
287 }
288
289 // If 'ignore' is set, check whether we should ignore the
290 // widget at this occurs-iteration of the loop
291 if(!paramEl.ignore.equals("")) {
292 int ignorePos = Integer.parseInt(paramEl.ignore);
293 if(ignorePos != (j-1)) {
294 // occurs-iteration (outer loop) when indexed from 0
295 // doesn't match ignorePos so can set up this widget
296 paramEl.createWidget(e);
297 // false means label not attached to widget
298 // j is row, i is col
299 this.addGridBagComponent(panel, paramEl.ref, gbLayout,
300 gbConstraints, colPosition, j, paramEl.width, 1);
301 }
302 // else if the outer loop iteration-index matched,
303 // ignore widget
304 }
305 else { // when 'ignore' member var not set, set up this widget
306 paramEl.createWidget(e);
307 this.addGridBagComponent(panel, paramEl.ref, gbLayout,
308 gbConstraints, colPosition, j, paramEl.width, 1);
309 }
310 } // end for on subElements
311 // add each panel of grouped param elements to this multi panel
312 // this.panel.add(singlePanel);
313 } // end for on num_occurs
314
315 // Now add a heading row: labelling each column in the multipanel:
316 // JPanel headingPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
317 // get the labels from the subelements' displayElement:
318 for(int i = 0; i < subElements.length/num_occurs; i++) {
319 // Calculate the column position of the form control in the layout
320 // ColPosition of current control is the sum of the widths of all
321 // previous controls (all controls to the left of it in this row)
322 if(i == 0) {
323 colPosition = 0;
324 } else { // i > 0
325 colPosition += colWidths[i-1];
326 }
327 JLabel colHeading = new JLabel(subElements[i].displayItem);
328 // add it to the top of this multi panel
329 this.addGridBagComponent(panel, colHeading,
330 gbLayout, gbConstraints, colPosition, 0, 1, 1);
331 // col i, row 0, size (1,1)
332 }
333
334 // Add the multi panel to the parent.
335 // First it is nested in a panel laid out with FlowLayout in order
336 // to not have the multipanel centred in an ugly manner.
337 JPanel outerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
338 outerPanel.add(panel); // same as adding this.ref
339 parentPanel.add(outerPanel);
340 } // end if
341
342 } // end processing <param type=multi>
343
344 /**
345 * The multi-panel is laid out with a GridBagLayout in order to make
346 * the layout more attractive (so that the form controls don't have as
347 * odd sizes as before when GraphPaperLayout was used).
348 * This method sets the constraints for the given control and then adds
349 * it to the given JPanel.
350 * This method is called to add a control (or heading label) to the
351 * multi-panel using GridBagLayout.
352 * @param panel is the panel to add the control (or heading label) to.
353 * @param comp is the Query Form control or heading JLabel to be laid out.
354 * @param gbLayout is the GridBagLayout object used to lay out the panel.
355 * @param gbConstraints is the GridBagConstraints object for the panel's
356 * GridBagLayout.
357 * @param col is the column value of the top-left of the control (for
358 * setting the GridBagLayout's gridx value).
359 * @param row is the row value of the top-left of the control (for
360 * setting the GridBagLayout's gridy value).
361 * @param w is the number of columns spanned by the control (for setting
362 * the GridBagLayout's gridwidth value).
363 * @param h is the number of rows spanned by the control (for setting
364 * the GridBagLayout's gridheight value).
365 */
366 protected void addGridBagComponent(JPanel panel, JComponent comp,
367 GridBagLayout gbLayout, GridBagConstraints gbConstraints,
368 int col, int row, int w, int h)
369 {
370 // different for all components
371 gbConstraints.gridx = col;
372 gbConstraints.gridy = row;
373 gbConstraints.gridwidth = w;
374 gbConstraints.gridheight = h;
375
376 gbLayout.setConstraints(comp, gbConstraints);
377 panel.add(comp);
378 }
379
380 /** If called after the form containing these param controls has been
381 * displayed and the user has entered/selected values (i.e. after the search
382 * button to execute a query has been pressed), then this method puts
383 * the controls' associated QueryFormParam names and the user-
384 * entered/-selected values into the HashMap nameValsmap (= the argument).
385 * Note that for the GSXML.PARAM_TYPE_MULTI case - where each related set
386 * of controls may occur several times (depending on member variable
387 * 'occurs') - the value entered/selected for each control that has the
388 * same *name* (= key into the HashMap) is added into the HashMap as
389 * a comma-separated list associated with its name-key.
390 * This method requires that argument nameValsmap is a non-null HashMap.
391 * @param nameValsMap - a non-null HashMap. At the end of this method,
392 * the nameValsMap HashMap will contain mappings from query parameter field
393 * names to their user-entered values (for the field's associated
394 * form controls).
395 */
396 public void setSelectedValue(HashMap nameValsMap) {
397 // When setSelectedValue() is called on subElements
398 // that have their ignore member var set at any stage,
399 // their this.ref will be null, so can add "" for those cases.
400 if(this.ref == null) {
401 //Also the case for *ignored* ParamElementGUIs whose widgets
402 //never got instantiated (to do with member var 'ignore')
403
404 //In these cases, there is no value that the user input,
405 //so just add ""
406 if(nameValsMap.containsKey(this.name)) {
407 //just append to existing value for key:
408 String cumVal = (String)nameValsMap.get(this.name);
409 cumVal += ",";
410 nameValsMap.put(this.name, cumVal);
411 } else {
412 // widget.type=invisible, set to default
413 if(this.type.equals(GSXML.PARAM_TYPE_INVISIBLE)
414 && !this.def.equals(""))
415 nameValsMap.put(this.name, this.def);
416 else //put empty string in there for now
417 nameValsMap.put(this.name, "");
418 }
419 return; //that's it, don't go any further
420 }
421
422 // Complex type: can contain multiple child parameters/options
423 // Need all names that match to have associated values in
424 // comma-separated list
425 if(this.type.equals(GSXML.PARAM_TYPE_MULTI)) {
426 for(int i = 0; i < subElements.length; i++) {
427 ((QueryFormControl)subElements[i]).setSelectedValue(
428 nameValsMap);
429 }
430 } else if(this.type.equals(GSXML.PARAM_TYPE_ENUM_MULTI)) {
431 // JList
432 JList list = (JList)this.ref;
433 Object[] selectedParams = list.getSelectedValues();
434 for(int i = 0; i < selectedParams.length; i++) {
435 String value = ((QueryFormData)selectedParams[i]).name;
436 if(nameValsMap.containsKey(this.name))
437 //append value to existing value for the same key
438 value = (String)nameValsMap.get(this.name) + "," + value;
439 nameValsMap.put(this.name, value);
440 }
441 } else if(this.type.equals(GSXML.PARAM_TYPE_ENUM_SINGLE)) {
442 // JCombobox
443 JComboBox combo = (JComboBox)this.ref;
444 QueryFormData selectedParam = (QueryFormData)combo.getSelectedItem();
445 String value = selectedParam.name;
446 if(nameValsMap.containsKey(this.name)) // key exists, so append
447 value = (String)nameValsMap.get(this.name) + "," + value;
448 nameValsMap.put(this.name, value);
449 } else if (this.type.equals(GSXML.PARAM_TYPE_BOOLEAN)) {
450 JCheckBox checkbox = (JCheckBox)this.ref;
451 String value = "0"; //"off"
452 if(checkbox.isSelected()) //boolean is "on"
453 value = "1";
454 if(nameValsMap.containsKey(this.name)) // key exists, so append
455 value = (String)nameValsMap.get(this.name) + "," + value;
456 nameValsMap.put(this.name, value);
457 } else if(this.type.equals(GSXML.PARAM_TYPE_INTEGER)
458 || this.type.equals(GSXML.PARAM_TYPE_STRING)) {
459 // Textfield
460 JTextField field = (JTextField)this.ref;
461 String value = field.getText();
462 if(nameValsMap.containsKey(this.name)) // key exists, so append
463 value = (String)nameValsMap.get(this.name) + "," + value;
464 nameValsMap.put(this.name, value);
465 }
466 }
467}
Note: See TracBrowser for help on using the repository browser.