001 // Copyright May 4, 2006 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014 package org.apache.tapestry.dojo.form;
015
016 import java.util.HashMap;
017 import java.util.Map;
018
019 import org.apache.tapestry.IDirect;
020 import org.apache.tapestry.IJSONRender;
021 import org.apache.tapestry.IMarkupWriter;
022 import org.apache.tapestry.IRequestCycle;
023 import org.apache.tapestry.IScript;
024 import org.apache.tapestry.PageRenderSupport;
025 import org.apache.tapestry.Tapestry;
026 import org.apache.tapestry.TapestryUtils;
027 import org.apache.tapestry.engine.DirectServiceParameter;
028 import org.apache.tapestry.engine.IEngineService;
029 import org.apache.tapestry.engine.ILink;
030 import org.apache.tapestry.form.IPropertySelectionModel;
031 import org.apache.tapestry.form.ValidatableField;
032 import org.apache.tapestry.form.ValidatableFieldSupport;
033 import org.apache.tapestry.json.IJSONWriter;
034 import org.apache.tapestry.valid.ValidatorException;
035
036 /**
037 * An html field similar to a <code>select</code> input field that
038 * is wrapped by a dojo ComboBox widget.
039 *
040 * @author jkuhnert
041 */
042 public abstract class Autocompleter extends AbstractFormWidget
043 implements ValidatableField, IJSONRender, IDirect
044 {
045
046 /**
047 *
048 * {@inheritDoc}
049 */
050 protected void renderFormWidget(IMarkupWriter writer, IRequestCycle cycle)
051 {
052 renderDelegatePrefix(writer, cycle);
053
054 writer.begin("select");
055 writer.attribute("name", getName());
056
057 if (isDisabled())
058 writer.attribute("disabled", "disabled");
059
060 if (getSubmitOnChange())
061 writer.attribute("onchange", "javascript: this.form.events.submit();");
062
063 renderIdAttribute(writer, cycle);
064
065 renderDelegateAttributes(writer, cycle);
066
067 getValidatableFieldSupport().renderContributions(this, writer, cycle);
068
069 // Apply informal attributes.
070 renderInformalParameters(writer, cycle);
071
072 writer.end();
073 renderDelegateSuffix(writer, cycle);
074
075 DirectServiceParameter dsp =
076 new DirectServiceParameter(this, new Object[]{},
077 new String[]{getId()}, true);
078 ILink link = getDirectService().getLink(true, dsp);
079
080 Map parms = new HashMap();
081 parms.put("id", getClientId());
082
083 StringBuffer str = new StringBuffer("{");
084 str.append("dataUrl:'").append(link.getURL()).append("&filter=%{searchString}',")
085 .append("mode:'remote',")
086 .append("widgetId:'").append(getName()).append("', ")
087 .append("name:'").append(getName()).append("'");
088
089 IPropertySelectionModel model = getModel();
090 if (model == null)
091 throw Tapestry.createRequiredParameterException(this, "model");
092
093 int count = model.getOptionCount();
094 Object value = getValue();
095
096 for (int i = 0; i < count; i++) {
097 Object option = model.getOption(i);
098
099 if (isEqual(option, value)) {
100 str.append(", comboBoxValue:'").append(model.getValue(i)).append("',")
101 .append("comboBoxSelectionValue:'").append(model.getLabel(i))
102 .append("'");
103 break;
104 }
105 }
106
107 str.append("}");
108
109 parms.put("props", str.toString());
110
111 PageRenderSupport prs = TapestryUtils.getPageRenderSupport(cycle, this);
112 getScript().execute(cycle, prs, parms);
113 }
114
115 /**
116 * {@inheritDoc}
117 */
118 public void renderComponent(IJSONWriter writer, IRequestCycle cycle)
119 {
120 IPropertySelectionModel model = getModel();
121
122 if (model == null)
123 throw Tapestry.createRequiredParameterException(this, "model");
124
125 int count = model.getOptionCount();
126
127 for (int i = 0; i < count; i++)
128 {
129 String value = model.getValue(i);
130 String label = model.getLabel(i);
131
132 if (getFilter() == null || getFilter().trim().length() <= 0) {
133 writer.put(value, label);
134 continue;
135 }
136
137 // primitive filter, for now
138 // TODO: Create filter interface in IPropertySelectionModel
139 if (getFilter() != null
140 && label.toLowerCase().indexOf(getFilter().toLowerCase()) > -1) {
141 writer.put(value, label);
142 }
143 }
144 }
145
146 /**
147 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
148 */
149 protected void rewindFormWidget(IMarkupWriter writer, IRequestCycle cycle)
150 {
151 String value = cycle.getParameter(getName() + "_selected");
152
153 Object object = getModel().translateValue(value);
154
155 try
156 {
157 getValidatableFieldSupport().validate(this, writer, cycle, object);
158
159 setValue(object);
160 }
161 catch (ValidatorException e)
162 {
163 getForm().getDelegate().record(e);
164 }
165 }
166
167 private boolean isEqual(Object left, Object right)
168 {
169 // Both null, or same object, then are equal
170
171 if (left == right)
172 return true;
173
174 // If one is null, the other isn't, then not equal.
175
176 if (left == null || right == null)
177 return false;
178
179 // Both non-null; use standard comparison.
180
181 return left.equals(right);
182 }
183
184 /**
185 * {@inheritDoc}
186 */
187 public boolean isStateful()
188 {
189 return false;
190 }
191
192 /**
193 * Triggerd by using filterOnChange logic.
194 *
195 * {@inheritDoc}
196 */
197 public void trigger(IRequestCycle cycle)
198 {
199 setFilter(cycle.getParameter("filter"));
200 }
201
202 public abstract IPropertySelectionModel getModel();
203
204 /** @since 4.1 */
205 public abstract boolean isFilterOnChange();
206
207 /** @since 2.2 * */
208 public abstract boolean getSubmitOnChange();
209
210 /** @since 2.2 * */
211 public abstract Object getValue();
212
213 /** @since 2.2 * */
214 public abstract void setValue(Object value);
215
216 /** @since 4.1 */
217 public abstract void setFilter(String value);
218
219 /** @since 4.1 */
220 public abstract String getFilter();
221
222 /**
223 * Injected.
224 */
225 public abstract ValidatableFieldSupport getValidatableFieldSupport();
226
227 /**
228 * Injected.
229 * @return
230 */
231 public abstract IEngineService getDirectService();
232
233 /**
234 * Injected.
235 * @return
236 */
237 public abstract IScript getScript();
238
239 /**
240 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired()
241 */
242 public boolean isRequired()
243 {
244 return getValidatableFieldSupport().isRequired(this);
245 }
246 }