001: package org.araneaframework.uilib.form.control;
002:
003: import java.io.Serializable;
004: import java.util.List;
005: import org.apache.commons.lang.StringEscapeUtils;
006: import org.araneaframework.InputData;
007: import org.araneaframework.OutputData;
008: import org.araneaframework.core.ActionListener;
009: import org.araneaframework.http.HttpOutputData;
010: import org.araneaframework.uilib.ConfigurationContext;
011: import org.araneaframework.uilib.support.TextType;
012:
013: /**
014: * TextControl with AJAX autocompletion support.
015: *
016: * @author Steven Jentson (steven@webmedia.ee)
017: * @author Taimo Peelo (taimo@araneaframework.org)
018: */
019: public class AutoCompleteTextControl extends TextControl {
020: public static final String LISTENER_NAME = "autocomplete";
021:
022: protected long minCompletionLength = 1;
023: protected DataProvider dataProvider;
024: protected ResponseBuilder responseBuilder;
025:
026: public AutoCompleteTextControl() {
027: }
028:
029: /**
030: * @param minCompletionLength number of chars that must be input before suggestions are provided
031: */
032: public AutoCompleteTextControl(long minCompletionLength) {
033: this .minCompletionLength = minCompletionLength;
034: }
035:
036: public AutoCompleteTextControl(TextType textType) {
037: super (textType);
038: }
039:
040: /**
041: * @param minCompletionLength number of chars that must be input before suggestions are provided
042: */
043: public AutoCompleteTextControl(TextType textType,
044: long minCompletionLength) {
045: super (textType);
046: this .minCompletionLength = minCompletionLength;
047: }
048:
049: protected void init() throws Exception {
050: super .init();
051: addActionListener(LISTENER_NAME,
052: new AutoCompleteActionListener());
053: }
054:
055: public void setDataProvider(DataProvider dataProvider) {
056: this .dataProvider = dataProvider;
057: }
058:
059: /** @since 1.0.4 */
060: public void setResponseBuilder(ResponseBuilder responseBuilder) {
061: this .responseBuilder = responseBuilder;
062: }
063:
064: /**
065: * @return {@link ResponseBuilder} that will be used to build response with suggestions.
066: * @since 1.0.4 */
067: public ResponseBuilder getResponseBuilder() {
068: return resolveResponseBuilder();
069: }
070:
071: public interface DataProvider extends Serializable {
072: public List getSuggestions(String input);
073: }
074:
075: private class AutoCompleteActionListener implements ActionListener {
076: public void processAction(Object actionId, InputData input,
077: OutputData output) throws Exception {
078: String str = innerData == null ? null
079: : ((String[]) innerData)[0];
080: List suggestions = dataProvider.getSuggestions(str);
081:
082: ResponseBuilder responseBuilder = resolveResponseBuilder();
083:
084: HttpOutputData httpOutput = (HttpOutputData) output;
085: String xml = responseBuilder
086: .getResponseContent(suggestions);
087:
088: httpOutput.setContentType(responseBuilder
089: .getResponseContentType());
090: httpOutput.getWriter().write(xml);
091: }
092: }
093:
094: /** @since 1.0.4 */
095: protected ResponseBuilder resolveResponseBuilder() {
096: ResponseBuilder result = this .responseBuilder;
097: if (result == null) {
098: ConfigurationContext confCtx = (ConfigurationContext) getEnvironment()
099: .getEntry(ConfigurationContext.class);
100: if (confCtx != null)
101: result = (ResponseBuilder) confCtx
102: .getEntry(ConfigurationContext.AUTO_COMPLETE_RESPONSE_BUILDER);
103: }
104:
105: if (result == null)
106: result = new DefaultResponseBuilder();
107:
108: return result;
109: }
110:
111: /**
112: * Autocompletion response builder interface.
113: * @author Taimo Peelo (taimo@araneaframework.org)
114: */
115: public static interface ResponseBuilder extends Serializable {
116: /**
117: * Returns response content with <code>suggestions</code> appropriately set.
118: * @param suggestions suggested completions that should be included in response
119: * @return appropriate response content
120: */
121: public String getResponseContent(List suggestions);
122:
123: /**
124: * Returns response content type.
125: * @return response content type
126: */
127: public String getResponseContentType();
128: }
129:
130: /**
131: * Default {@link AutoCompleteTextControl.ResponseBuilder} used when {@link AutoCompleteTextControl} does not have
132: * its {@link AutoCompleteTextControl.ResponseBuilder} set and {@link ConfigurationContext#AUTO_COMPLETE_RESPONSE_BUILDER}
133: * does not specify application-wide {@link AutoCompleteTextControl.ResponseBuilder}.
134: *
135: * @author Steven Jentson (steven@webmedia.ee)
136: */
137: public static class DefaultResponseBuilder implements
138: ResponseBuilder {
139: public String getResponseContent(List suggestions) {
140: StringBuffer xml = new StringBuffer();
141: xml.append("<ul>");
142: for (int i = 0; i < suggestions.size(); i++) {
143: xml.append("<li>");
144: xml.append(StringEscapeUtils
145: .escapeHtml((String) suggestions.get(i)));
146: xml.append("</li>");
147: }
148: xml.append("</ul>");
149: return xml.toString();
150: }
151:
152: public String getResponseContentType() {
153: return "text/xml";
154: }
155: }
156:
157: /**
158: * Returns {@link ViewModel}.
159: *
160: * @return {@link ViewModel}.
161: */
162: public Object getViewModel() {
163: return new ViewModel();
164: }
165:
166: //*********************************************************************
167: //* VIEW MODEL
168: //*********************************************************************
169: public class ViewModel extends TextControl.ViewModel {
170: private long minCompletionLength;
171:
172: public ViewModel() {
173: this .minCompletionLength = AutoCompleteTextControl.this .minCompletionLength;
174: }
175:
176: public long getMinCompletionLength() {
177: return minCompletionLength;
178: }
179: }
180: }
|