1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts2.dojo.components;
23
24 import java.util.Random;
25
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.struts2.components.ComboBox;
30 import org.apache.struts2.views.annotations.StrutsTag;
31 import org.apache.struts2.views.annotations.StrutsTagAttribute;
32 import org.apache.struts2.views.annotations.StrutsTagSkipInheritance;
33
34 import com.opensymphony.xwork2.util.ValueStack;
35
36 /***
37 * <!-- START SNIPPET: javadoc -->
38 * <p>The autocomplete tag is a combobox that can autocomplete text entered on the input box. If an action
39 * is used to populate the autocompleter, the output of the action must be a well formed JSON string. </p>
40 * <p>The autocompleter follows this rule to find its datasource:<p>
41 * <p>1. If the response is an array, assume that it contains 2-dimension array elements, like:
42 * <pre>
43 * [
44 * ["Alabama", "AL"],
45 * ["Alaska", "AK"]
46 * ]
47 * </pre>
48 * <p>2. If a value is specified in the "dataFieldName" attribute, and the response has a field with that
49 * name, assume that's the datasource, which can be an array of 2-dimension array elements, or a map,
50 * like (assuming dataFieldName="state"):</p>
51 * <pre>
52 * {
53 * "state" : [
54 * ["Alabama","AL"],
55 * ["Alaska","AK"]
56 * ]
57 * }
58 * or
59 * {
60 * "state" : {
61 * "Alabama" : "AL",
62 * "Alaska" : "AK"
63 * }
64 * }
65 * </pre>
66 * </pre>
67 * <p>3. If there is a field that starts with the value specified on the "name" attribute, assume
68 * that's the datasource, like (assuming name="state"):</p>
69 * <pre>
70 * {
71 * "states" : [
72 * ["Alabama","AL"],
73 * ["Alaska","AK"]
74 * ]
75 * }
76 * </pre>
77 * <p>4. Use first array that is found, like:<p>
78 * <pre>
79 * {
80 * "anything" : [
81 * ["Alabama", "AL"],
82 * ["Alaska", "AK"]
83 * ]
84 * }
85 * <p>5. If the response is a map, use it (recommended as it is the easiest one to generate):
86 * <pre>
87 * {
88 * "Alabama" : "AL",
89 * "Alaska" : "AK"
90 * }
91 * </pre>
92 * <!-- END SNIPPET: javadoc -->
93 * <p>Examples</p>
94 * <!-- START SNIPPET: example1 -->
95 * <sx:autocompleter name="autocompleter1" href="%{jsonList}"/>
96 * <!-- END SNIPPET: example1 -->
97 *
98 * <!-- START SNIPPET: example2 -->
99 * <s:autocompleter name="test" list="{'apple','banana','grape','pear'}" autoComplete="false"/>
100 * <!-- END SNIPPET: example2 -->
101 *
102 * <!-- START SNIPPET: example3 -->
103 * <sx:autocompleter name="mvc" href="%{jsonList}" loadOnTextChange="true" loadMinimumCount="3"/>
104 *
105 * The text entered on the autocompleter is passed as a parameter to the url specified in "href", like (text is "struts"):
106 *
107 * http://host/example/myaction.do?mvc=struts
108 * <!-- END SNIPPET: example3 -->
109 *
110 * <!-- START SNIPPET: example4 -->
111 * <form id="selectForm">
112 * <sx:autocompleter name="select" list="{'fruits','colors'}" valueNotifyTopics="/changed" />
113 * </form>
114 * <sx:autocompleter href="%{jsonList}" formId="selectForm" listenTopics="/changed"/>
115 * <!-- END SNIPPET: example4 -->
116 *
117 * <!-- START SNIPPET: example5 -->
118 * <sx:autocompleter href="%{jsonList}" id="auto"/>
119 * <script type="text/javascript">
120 * function getValues() {
121 * var autoCompleter = dojo.widget.byId("auto");
122 *
123 * //key (in the states example above, "AL")
124 * var key = autoCompleter.getSelectedKey();
125 * alert(key);
126 *
127 * //value (in the states example above, "Alabama")
128 * var value = autoCompleter.getSelectedValue();
129 * alert(value);
130 *
131 * //text currently on the textbox (anything the user typed)
132 * var text = autoCompleter.getText();
133 * alert(text);
134 * }
135 *
136 * function setValues() {
137 * var autoCompleter = dojo.widget.byId("auto");
138 *
139 * //key (key will be set to "AL" and value to "Alabama")
140 * autoCompleter.setSelectedKey("AL");
141 *
142 * //value (key will be set to "AL" and value to "Alabama")
143 * autoCompleter.setAllValues("AL", "Alabama");
144 * }
145 * </script>
146 * <!-- END SNIPPET: example5 -->
147 *
148 * <!-- START SNIPPET: example6 -->
149 * <script type="text/javascript">
150 * dojo.event.topic.subscribe("/before", function(event, widget){
151 * alert('inside a topic event. before request');
152 * //event: set event.cancel = true, to cancel request
153 * //widget: widget that published the topic
154 * });
155 * </script>
156 *
157 * <sx:autocompleter beforeNotifyTopics="/before" href="%{#ajaxTest} />
158 * <!-- END SNIPPET: example6 -->
159 *
160 * <!-- START SNIPPET: example7 -->
161 * <script type="text/javascript">
162 * dojo.event.topic.subscribe("/after", function(data, request, widget){
163 * alert('inside a topic event. after request');
164 * //data : JavaScript object from parsing response
165 * //request: XMLHttpRequest object
166 * //widget: widget that published the topic
167 * });
168 * </script>
169 *
170 * <sx:autocompleter afterNotifyTopics="/after" href="%{#ajaxTest}" />
171 * <!-- END SNIPPET: example7 -->
172 *
173 * <!-- START SNIPPET: example8-->
174 * <script type="text/javascript">
175 * dojo.event.topic.subscribe("/error", function(error, request, widget){
176 * alert('inside a topic event. on error');
177 * //error : error object (error.message has the error message)
178 * //request: XMLHttpRequest object
179 * //widget: widget that published the topic
180 * });
181 * </script>
182 *
183 * <sx:autocompleter errorNotifyTopics="/error" href="%{#ajaxTest}" />
184 * <!-- END SNIPPET: example8 -->
185 *
186 * <!-- START SNIPPET: example9 -->
187 * <script type="text/javascript">
188 * dojo.event.topic.subscribe("/value", function(value, key, text, widget){
189 * alert('inside a topic event. after value changed');
190 * //value : selected value (like "Florida" in example above)
191 * //key: selected key (like "FL" in example above)
192 * //text: text typed into textbox
193 * //widget: widget that published the topic
194 * });
195 * </script>
196 *
197 * <sx:autocompleter valueNotifyTopics="/value" href="%{#ajaxTest}" />
198 * <!-- END SNIPPET: example9 -->
199 */
200 @StrutsTag(name="autocompleter", tldTagClass="org.apache.struts2.dojo.views.jsp.ui.AutocompleterTag", description="Renders a combobox with autocomplete and AJAX capabilities")
201 public class Autocompleter extends ComboBox {
202 public static final String TEMPLATE = "autocompleter";
203 final private static String COMPONENT_NAME = Autocompleter.class.getName();
204 private final static transient Random RANDOM = new Random();
205
206 protected String forceValidOption;
207 protected String searchType;
208 protected String autoComplete;
209 protected String delay;
210 protected String disabled;
211 protected String href;
212 protected String dropdownWidth;
213 protected String dropdownHeight;
214 protected String formId;
215 protected String formFilter;
216 protected String listenTopics;
217 protected String notifyTopics;
218 protected String indicator;
219 protected String loadOnTextChange;
220 protected String loadMinimumCount;
221 protected String showDownArrow;
222 protected String templateCssPath;
223 protected String iconPath;
224 protected String keyName;
225 protected String dataFieldName;
226 protected String beforeNotifyTopics;
227 protected String afterNotifyTopics;
228 protected String errorNotifyTopics;
229 protected String valueNotifyTopics;
230 protected String resultsLimit;
231 protected String transport;
232 protected String preload;
233 protected String keyValue;
234
235 public Autocompleter(ValueStack stack, HttpServletRequest request,
236 HttpServletResponse response) {
237 super(stack, request, response);
238 }
239
240 protected String getDefaultTemplate() {
241 return TEMPLATE;
242 }
243
244 public String getComponentName() {
245 return COMPONENT_NAME;
246 }
247
248
249 public void evaluateExtraParams() {
250 super.evaluateExtraParams();
251
252 if (forceValidOption != null)
253 addParameter("forceValidOption", findValue(forceValidOption,
254 Boolean.class));
255 if (searchType != null) {
256 String type = findString(searchType);
257 if(type != null)
258 addParameter("searchType", type.toUpperCase());
259 }
260 if (autoComplete != null)
261 addParameter("autoComplete", findValue(autoComplete, Boolean.class));
262 if (delay != null)
263 addParameter("delay", findValue(delay, Integer.class));
264 if (disabled != null)
265 addParameter("disabled", findValue(disabled, Boolean.class));
266 if (href != null) {
267 addParameter("href", findString(href));
268 addParameter("mode", "remote");
269 }
270 if (dropdownHeight != null)
271 addParameter("dropdownHeight", findValue(dropdownHeight, Integer.class));
272 if (dropdownWidth != null)
273 addParameter("dropdownWidth", findValue(dropdownWidth, Integer.class));
274 if (formFilter != null)
275 addParameter("formFilter", findString(formFilter));
276 if (formId != null)
277 addParameter("formId", findString(formId));
278 if (listenTopics != null)
279 addParameter("listenTopics", findString(listenTopics));
280 if (notifyTopics != null)
281 addParameter("notifyTopics", findString(notifyTopics));
282 if (indicator != null)
283 addParameter("indicator", findString(indicator));
284 if (loadOnTextChange != null)
285 addParameter("loadOnTextChange", findValue(loadOnTextChange, Boolean.class));
286 if (loadMinimumCount != null)
287 addParameter("loadMinimumCount", findValue(loadMinimumCount, Integer.class));
288 if (showDownArrow != null)
289 addParameter("showDownArrow", findValue(showDownArrow, Boolean.class));
290 else
291 addParameter("showDownArrow", Boolean.TRUE);
292 if (templateCssPath != null)
293 addParameter("templateCssPath", findString(templateCssPath));
294 if (iconPath != null)
295 addParameter("iconPath", findString(iconPath));
296 if (dataFieldName != null)
297 addParameter("dataFieldName", findString(dataFieldName));
298 if (keyName != null)
299 addParameter("keyName", findString(keyName));
300 else {
301 keyName = name + "Key";
302 addParameter("keyName", findString(keyName));
303 }
304 if (transport != null)
305 addParameter("transport", findString(transport));
306 if (preload != null)
307 addParameter("preload", findValue(preload, Boolean.class));
308
309 if (keyValue != null)
310 addParameter("nameKeyValue", findString(keyValue));
311 else {
312 String keyNameExpr = "%{" + keyName + "}";
313 addParameter("nameKeyValue", findString(keyNameExpr));
314 }
315
316
317 if (beforeNotifyTopics != null)
318 addParameter("beforeNotifyTopics", findString(beforeNotifyTopics));
319 if (afterNotifyTopics != null)
320 addParameter("afterNotifyTopics", findString(afterNotifyTopics));
321 if (errorNotifyTopics != null)
322 addParameter("errorNotifyTopics", findString(errorNotifyTopics));
323 if (valueNotifyTopics != null)
324 addParameter("valueNotifyTopics", findString(valueNotifyTopics));
325 if (resultsLimit != null)
326 addParameter("searchLimit", findString(resultsLimit));
327
328
329 Boolean parseContent = (Boolean)stack.getContext().get(Head.PARSE_CONTENT);
330 boolean generateId = (parseContent != null ? !parseContent : true);
331
332 addParameter("pushId", generateId);
333 if ((this.id == null || this.id.length() == 0) && generateId) {
334
335
336 int nextInt = RANDOM.nextInt();
337 nextInt = nextInt == Integer.MIN_VALUE ? Integer.MAX_VALUE : Math.abs(nextInt);
338 this.id = "widget_" + String.valueOf(nextInt);
339 }
340 }
341
342 @Override
343 @StrutsTagSkipInheritance
344 public void setTheme(String theme) {
345 super.setTheme(theme);
346 }
347
348 @Override
349 public String getTheme() {
350 return "ajax";
351 }
352
353 protected Object findListValue() {
354 return (list != null) ? findValue(list, Object.class) : null;
355 }
356
357 @StrutsTagAttribute(description="Whether autocompleter should make suggestion on the textbox", type="Boolean", defaultValue="false")
358 public void setAutoComplete(String autoComplete) {
359 this.autoComplete = autoComplete;
360 }
361
362 @StrutsTagAttribute(description="Enable or disable autocompleter", type="Boolean", defaultValue="false")
363 public void setDisabled(String disabled) {
364 this.disabled = disabled;
365 }
366
367 @StrutsTagAttribute(description="Force selection to be one of the options", type="Boolean", defaultValue="false")
368 public void setForceValidOption(String forceValidOption) {
369 this.forceValidOption = forceValidOption;
370 }
371
372 @StrutsTagAttribute(description="The URL used to load the options")
373 public void setHref(String href) {
374 this.href = href;
375 }
376
377 @StrutsTagAttribute(description="Delay before making the search", type="Integer", defaultValue="100")
378 public void setDelay(String searchDelay) {
379 this.delay = searchDelay;
380 }
381
382 @StrutsTagAttribute(description="how the search must be performed, options are: 'startstring', 'startword' " +
383 "and 'substring'", defaultValue="stringstart")
384 public void setSearchType(String searchType) {
385 this.searchType = searchType;
386 }
387
388 @StrutsTagAttribute(description="Dropdown's height in pixels", type="Integer", defaultValue="120")
389 public void setDropdownHeight(String height) {
390 this.dropdownHeight = height;
391 }
392
393 @StrutsTagAttribute(description="Dropdown's width", type="Integer", defaultValue="same as textbox")
394 public void setDropdownWidth(String width) {
395 this.dropdownWidth = width;
396 }
397
398 @StrutsTagAttribute(description="Function name used to filter the fields of the form")
399 public void setFormFilter(String formFilter) {
400 this.formFilter = formFilter;
401 }
402
403 @StrutsTagAttribute(description="Form id whose fields will be serialized and passed as parameters")
404 public void setFormId(String formId) {
405 this.formId = formId;
406 }
407
408 @StrutsTagAttribute(description="Topic that will trigger a reload")
409 public void setListenTopics(String listenTopics) {
410 this.listenTopics = listenTopics;
411 }
412
413 @StrutsTagAttribute(description="Topics that will be published when content is reloaded")
414 public void setNotifyTopics(String onValueChangedPublishTopic) {
415 this.notifyTopics = onValueChangedPublishTopic;
416 }
417
418 @StrutsTagAttribute(description="Id of element that will be shown while request is made")
419 public void setIndicator(String indicator) {
420 this.indicator = indicator;
421 }
422
423 @StrutsTagAttribute(description="Minimum number of characters that will force the content to be loaded", type="Integer", defaultValue="3")
424 public void setLoadMinimumCount(String loadMinimumCount) {
425 this.loadMinimumCount = loadMinimumCount;
426 }
427
428 @StrutsTagAttribute(description="Options will be reloaded everytime a character is typed on the textbox", type="Boolean", defaultValue="true")
429 public void setLoadOnTextChange(String loadOnType) {
430 this.loadOnTextChange = loadOnType;
431 }
432
433 @StrutsTagAttribute(description="Show or hide the down arrow button", type="Boolean", defaultValue="true")
434 public void setShowDownArrow(String showDownArrow) {
435 this.showDownArrow = showDownArrow;
436 }
437
438
439 @StrutsTagAttribute(description="Iteratable source to populate from.")
440 public void setList(String list) {
441 super.setList(list);
442 }
443
444 @StrutsTagAttribute(description="Template css path")
445 public void setTemplateCssPath(String templateCssPath) {
446 this.templateCssPath = templateCssPath;
447 }
448
449 @StrutsTagAttribute(description="Path to icon used for the dropdown")
450 public void setIconPath(String iconPath) {
451 this.iconPath = iconPath;
452 }
453
454 @StrutsTagAttribute(description="Name of the field to which the selected key will be assigned")
455 public void setKeyName(String keyName) {
456 this.keyName = keyName;
457 }
458
459 @StrutsTagAttribute(description="Name of the field in the returned JSON object that contains the data array", defaultValue="Value specified in 'name'")
460 public void setDataFieldName(String dataFieldName) {
461 this.dataFieldName = dataFieldName;
462 }
463
464 @StrutsTagAttribute(description="The css class to use for element")
465 public void setCssClass(String cssClass) {
466 super.setCssClass(cssClass);
467 }
468
469 @StrutsTagAttribute(description="The css style to use for element")
470 public void setCssStyle(String cssStyle) {
471 super.setCssStyle(cssStyle);
472 }
473
474 @StrutsTagAttribute(description="The id to use for the element")
475 public void setId(String id) {
476 super.setId(id);
477 }
478
479 @StrutsTagAttribute(description="The name to set for element")
480 public void setName(String name) {
481 super.setName(name);
482 }
483
484 @StrutsTagAttribute(description="Preset the value of input element")
485 public void setValue(String arg0) {
486 super.setValue(arg0);
487 }
488
489 @StrutsTagAttribute(description="Comma delimmited list of topics that will published after the request(if the request succeeds)")
490 public void setAfterNotifyTopics(String afterNotifyTopics) {
491 this.afterNotifyTopics = afterNotifyTopics;
492 }
493
494 @StrutsTagAttribute(description="Comma delimmited list of topics that will published before the request")
495 public void setBeforeNotifyTopics(String beforeNotifyTopics) {
496 this.beforeNotifyTopics = beforeNotifyTopics;
497 }
498
499 @StrutsTagAttribute(description="Comma delimmited list of topics that will published after the request(if the request fails)")
500 public void setErrorNotifyTopics(String errorNotifyTopics) {
501 this.errorNotifyTopics = errorNotifyTopics;
502 }
503
504 @StrutsTagAttribute(description="Comma delimmited list of topics that will published when a value is selected")
505 public void setValueNotifyTopics(String valueNotifyTopics) {
506 this.valueNotifyTopics = valueNotifyTopics;
507 }
508
509 @StrutsTagAttribute(description="Limit how many results are shown as autocompletion options, set to -1 for unlimited results", defaultValue="30", type = "Integer")
510 public void setResultsLimit(String resultsLimit) {
511 this.resultsLimit = resultsLimit;
512 }
513
514 @StrutsTagAttribute(description="Transport used by Dojo to make the request", defaultValue="XMLHTTPTransport")
515 public void setTransport(String transport) {
516 this.transport = transport;
517 }
518
519 @StrutsTagAttribute(description="Load options when page is loaded", type="Boolean", defaultValue="true")
520 public void setPreload(String preload) {
521 this.preload = preload;
522 }
523
524 @StrutsTagAttribute(description="Initial key value")
525 public void setKeyValue(String keyValue) {
526 this.keyValue = keyValue;
527 }
528 }