1 | |
package org.apache.tapestry.scriptaculous; |
2 | |
|
3 | |
import java.text.ParseException; |
4 | |
import java.util.Arrays; |
5 | |
import java.util.HashMap; |
6 | |
import java.util.Iterator; |
7 | |
import java.util.List; |
8 | |
import java.util.Map; |
9 | |
|
10 | |
import org.apache.hivemind.ApplicationRuntimeException; |
11 | |
import org.apache.hivemind.util.Defense; |
12 | |
import org.apache.tapestry.IActionListener; |
13 | |
import org.apache.tapestry.IDirect; |
14 | |
import org.apache.tapestry.IForm; |
15 | |
import org.apache.tapestry.IMarkupWriter; |
16 | |
import org.apache.tapestry.IRequestCycle; |
17 | |
import org.apache.tapestry.IScript; |
18 | |
import org.apache.tapestry.PageRenderSupport; |
19 | |
import org.apache.tapestry.TapestryUtils; |
20 | |
import org.apache.tapestry.coerce.ValueConverter; |
21 | |
import org.apache.tapestry.engine.DirectServiceParameter; |
22 | |
import org.apache.tapestry.engine.IEngineService; |
23 | |
import org.apache.tapestry.engine.ILink; |
24 | |
import org.apache.tapestry.form.AbstractFormComponent; |
25 | |
import org.apache.tapestry.form.TranslatedField; |
26 | |
import org.apache.tapestry.form.TranslatedFieldSupport; |
27 | |
import org.apache.tapestry.form.ValidatableFieldSupport; |
28 | |
import org.apache.tapestry.json.JSONLiteral; |
29 | |
import org.apache.tapestry.json.JSONObject; |
30 | |
import org.apache.tapestry.link.DirectLink; |
31 | |
import org.apache.tapestry.listener.ListenerInvoker; |
32 | |
import org.apache.tapestry.services.ResponseBuilder; |
33 | |
import org.apache.tapestry.util.SizeRestrictingIterator; |
34 | |
import org.apache.tapestry.valid.ValidatorException; |
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | |
|
56 | |
|
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | |
|
62 | |
|
63 | |
|
64 | |
|
65 | 0 | public abstract class Suggest extends AbstractFormComponent implements TranslatedField, IDirect { |
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | 0 | private static final String[] LITERAL_KEYS = new String[] |
72 | |
{"onFailure", "updateElement", "afterUpdateElement", "callback"}; |
73 | |
|
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | |
public abstract ListenerInvoker getListenerInvoker(); |
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | |
|
87 | |
|
88 | |
public abstract ResponseBuilder getResponse(); |
89 | |
|
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
|
95 | |
public abstract IScript getScript(); |
96 | |
|
97 | |
|
98 | |
|
99 | |
|
100 | |
|
101 | |
|
102 | |
public abstract ValueConverter getValueConverter(); |
103 | |
|
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
public abstract ValidatableFieldSupport getValidatableFieldSupport(); |
110 | |
|
111 | |
|
112 | |
|
113 | |
|
114 | |
|
115 | |
|
116 | |
public abstract TranslatedFieldSupport getTranslatedFieldSupport(); |
117 | |
|
118 | |
|
119 | |
|
120 | |
|
121 | |
|
122 | |
|
123 | |
public abstract IEngineService getEngineService(); |
124 | |
|
125 | |
|
126 | |
|
127 | |
|
128 | |
|
129 | |
public abstract Object getValue(); |
130 | |
public abstract void setValue(Object value); |
131 | |
|
132 | |
public abstract ListItemRenderer getListItemRenderer(); |
133 | |
public abstract void setListItemRenderer(ListItemRenderer renderer); |
134 | |
|
135 | |
public abstract IActionListener getListener(); |
136 | |
|
137 | |
public abstract Object getListSource(); |
138 | |
public abstract void setListSource(Object value); |
139 | |
|
140 | |
public abstract int getMaxResults(); |
141 | |
|
142 | |
public abstract Object getParameters(); |
143 | |
|
144 | |
public abstract String getOptions(); |
145 | |
|
146 | |
public abstract String getUpdateElementClass(); |
147 | |
|
148 | |
|
149 | |
|
150 | |
|
151 | |
|
152 | |
|
153 | |
|
154 | |
public abstract boolean isSearchTriggered(); |
155 | |
public abstract void setSearchTriggered(boolean value); |
156 | |
|
157 | |
public boolean isRequired() |
158 | |
{ |
159 | 0 | return getValidatableFieldSupport().isRequired(this); |
160 | |
} |
161 | |
|
162 | |
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) |
163 | |
{ |
164 | |
|
165 | |
|
166 | |
|
167 | 0 | IForm form = TapestryUtils.getForm(cycle, this); |
168 | 0 | setForm(form); |
169 | |
|
170 | 0 | if (form.wasPrerendered(writer, this)) |
171 | 0 | return; |
172 | |
|
173 | 0 | if (!form.isRewinding() && !cycle.isRewinding() |
174 | |
&& getResponse().isDynamic() && isSearchTriggered()) |
175 | |
{ |
176 | 0 | setName(form); |
177 | |
|
178 | |
|
179 | |
|
180 | 0 | if (cycle.getParameter(getClientId()) == null) |
181 | 0 | return; |
182 | |
|
183 | 0 | renderList(writer, cycle); |
184 | 0 | return; |
185 | |
} |
186 | |
|
187 | |
|
188 | |
|
189 | 0 | super.renderComponent(writer, cycle); |
190 | 0 | } |
191 | |
|
192 | |
|
193 | |
|
194 | |
|
195 | |
|
196 | |
|
197 | |
|
198 | |
|
199 | |
|
200 | |
|
201 | |
public void renderList(IMarkupWriter writer, IRequestCycle cycle) |
202 | |
{ |
203 | 0 | Defense.notNull(getListSource(), "listSource for Suggest component."); |
204 | |
|
205 | 0 | Iterator values = (Iterator)getValueConverter().coerceValue(getListSource(), Iterator.class); |
206 | |
|
207 | 0 | if (isParameterBound("maxResults")) |
208 | |
{ |
209 | 0 | values = new SizeRestrictingIterator(values, getMaxResults()); |
210 | |
} |
211 | |
|
212 | 0 | getListItemRenderer().renderList(writer, cycle, values); |
213 | 0 | } |
214 | |
|
215 | |
protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) |
216 | |
{ |
217 | 0 | String value = getTranslatedFieldSupport().format(this, getValue()); |
218 | 0 | boolean isTextArea = getTemplateTagName().equalsIgnoreCase("textarea"); |
219 | |
|
220 | 0 | renderDelegatePrefix(writer, cycle); |
221 | |
|
222 | 0 | if (isTextArea) |
223 | 0 | writer.begin(getTemplateTagName()); |
224 | |
else |
225 | 0 | writer.beginEmpty(getTemplateTagName()); |
226 | |
|
227 | |
|
228 | 0 | if (!isTextArea) |
229 | |
{ |
230 | 0 | writer.attribute("type", "text"); |
231 | 0 | writer.attribute("autocomplete", "off"); |
232 | |
} |
233 | |
|
234 | 0 | renderIdAttribute(writer, cycle); |
235 | 0 | writer.attribute("name", getName()); |
236 | |
|
237 | 0 | if (isDisabled()) |
238 | 0 | writer.attribute("disabled", "disabled"); |
239 | |
|
240 | 0 | renderInformalParameters(writer, cycle); |
241 | 0 | renderDelegateAttributes(writer, cycle); |
242 | |
|
243 | 0 | getTranslatedFieldSupport().renderContributions(this, writer, cycle); |
244 | 0 | getValidatableFieldSupport().renderContributions(this, writer, cycle); |
245 | |
|
246 | 0 | if (value != null) |
247 | |
{ |
248 | 0 | if (!isTextArea) |
249 | 0 | writer.attribute("value", value); |
250 | |
else |
251 | 0 | writer.print(value); |
252 | |
} |
253 | |
|
254 | 0 | if (!isTextArea) |
255 | 0 | writer.closeTag(); |
256 | |
else |
257 | 0 | writer.end(); |
258 | |
|
259 | 0 | renderDelegateSuffix(writer, cycle); |
260 | |
|
261 | |
|
262 | |
|
263 | 0 | writer.begin("div"); |
264 | 0 | writer.attribute("id", getClientId() + "choices"); |
265 | 0 | writer.attribute("class", getUpdateElementClass()); |
266 | 0 | writer.end(); |
267 | |
|
268 | |
|
269 | |
|
270 | 0 | JSONObject json = null; |
271 | 0 | String options = getOptions(); |
272 | |
|
273 | |
try { |
274 | |
|
275 | 0 | json = options != null ? new JSONObject(options) : new JSONObject(); |
276 | |
|
277 | 0 | } catch (ParseException ex) |
278 | |
{ |
279 | 0 | throw new ApplicationRuntimeException(ScriptaculousMessages.invalidOptions(options, ex), this.getBinding("options").getLocation(), ex); |
280 | 0 | } |
281 | |
|
282 | |
|
283 | |
|
284 | 0 | if (!json.has("onFailure")) |
285 | |
{ |
286 | 0 | json.put("onFailure", "tapestry.error"); |
287 | |
} |
288 | |
|
289 | 0 | if (!json.has("encoding")) |
290 | |
{ |
291 | 0 | json.put("encoding", cycle.getEngine().getOutputEncoding()); |
292 | |
} |
293 | |
|
294 | 0 | for (int i=0; i<LITERAL_KEYS.length; i++) |
295 | |
{ |
296 | 0 | String key = LITERAL_KEYS[i]; |
297 | 0 | if (json.has(key)) |
298 | |
{ |
299 | 0 | json.put(key, new JSONLiteral(json.getString(key))); |
300 | |
} |
301 | |
} |
302 | |
|
303 | 0 | Map parms = new HashMap(); |
304 | 0 | parms.put("inputId", getClientId()); |
305 | 0 | parms.put("updateId", getClientId() + "choices"); |
306 | 0 | parms.put("options", json.toString()); |
307 | |
|
308 | 0 | Object[] specifiedParams = DirectLink.constructServiceParameters(getParameters()); |
309 | 0 | Object[] listenerParams = null; |
310 | 0 | if (specifiedParams != null) |
311 | |
{ |
312 | 0 | listenerParams = new Object[specifiedParams.length + 1]; |
313 | 0 | System.arraycopy(specifiedParams, 0, listenerParams, 1, specifiedParams.length); |
314 | |
} else { |
315 | |
|
316 | 0 | listenerParams = new Object[1]; |
317 | |
} |
318 | |
|
319 | 0 | listenerParams[0] = getClientId(); |
320 | |
|
321 | 0 | ILink updateLink = getEngineService().getLink(isStateful(), new DirectServiceParameter(this, listenerParams)); |
322 | 0 | parms.put("updateUrl", updateLink.getURL()); |
323 | |
|
324 | 0 | PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this); |
325 | 0 | getScript().execute(this, cycle, pageRenderSupport, parms); |
326 | 0 | } |
327 | |
|
328 | |
|
329 | |
|
330 | |
|
331 | |
protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) |
332 | |
{ |
333 | 0 | String value = cycle.getParameter(getName()); |
334 | |
try |
335 | |
{ |
336 | 0 | Object object = getTranslatedFieldSupport().parse(this, value); |
337 | 0 | getValidatableFieldSupport().validate(this, writer, cycle, object); |
338 | |
|
339 | 0 | setValue(object); |
340 | 0 | } catch (ValidatorException e) |
341 | |
{ |
342 | 0 | getForm().getDelegate().recordFieldInputValue(value); |
343 | 0 | getForm().getDelegate().record(e); |
344 | 0 | } |
345 | 0 | } |
346 | |
|
347 | |
|
348 | |
|
349 | |
|
350 | |
|
351 | |
|
352 | |
|
353 | |
public void trigger(IRequestCycle cycle) |
354 | |
{ |
355 | 0 | IActionListener listener = getListener(); |
356 | 0 | if (listener == null) |
357 | 0 | listener = getContainer().getListeners().getImplicitListener(this); |
358 | |
|
359 | 0 | Object[] params = cycle.getListenerParameters(); |
360 | |
|
361 | |
|
362 | 0 | String inputId = (String)params[0]; |
363 | 0 | params[0] = cycle.getParameter(inputId); |
364 | |
|
365 | 0 | cycle.setListenerParameters(params); |
366 | |
|
367 | 0 | setSearchTriggered(true); |
368 | |
|
369 | 0 | getListenerInvoker().invokeListener(listener, this, cycle); |
370 | 0 | } |
371 | |
|
372 | |
public List getUpdateComponents() |
373 | |
{ |
374 | 0 | return Arrays.asList(new Object[] { getClientId() }); |
375 | |
} |
376 | |
|
377 | |
public boolean isAsync() |
378 | |
{ |
379 | 0 | return true; |
380 | |
} |
381 | |
|
382 | |
public boolean isJson() |
383 | |
{ |
384 | 0 | return false; |
385 | |
} |
386 | |
|
387 | |
|
388 | |
|
389 | |
|
390 | |
|
391 | |
protected void finishLoad() |
392 | |
{ |
393 | 0 | setListItemRenderer(DefaultListItemRenderer.SHARED_INSTANCE); |
394 | 0 | } |
395 | |
} |