View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
21  
22  import org.apache.commons.lang.StringUtils;
23  import org.apache.myfaces.tobago.component.Attributes;
24  import org.apache.myfaces.tobago.component.UIIn;
25  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
26  import org.apache.myfaces.tobago.internal.component.AbstractUIInput;
27  import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
28  import org.apache.myfaces.tobago.model.AutoSuggestExtensionItem;
29  import org.apache.myfaces.tobago.model.AutoSuggestItem;
30  import org.apache.myfaces.tobago.model.AutoSuggestItems;
31  import org.apache.myfaces.tobago.renderkit.InputRendererBase;
32  import org.apache.myfaces.tobago.renderkit.css.Classes;
33  import org.apache.myfaces.tobago.renderkit.css.Style;
34  import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
35  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
36  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
37  import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
38  import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
39  import org.apache.myfaces.tobago.util.ComponentUtils;
40  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import javax.faces.component.UIComponent;
45  import javax.faces.context.FacesContext;
46  import javax.faces.el.MethodBinding;
47  import javax.faces.validator.LengthValidator;
48  import javax.faces.validator.Validator;
49  import java.io.IOException;
50  import java.util.ArrayList;
51  import java.util.Collections;
52  import java.util.List;
53  
54  public class InRenderer extends InputRendererBase {
55  
56    private static final Logger LOG = LoggerFactory.getLogger(InRenderer.class);
57  
58    @Override
59    public void decode(FacesContext facesContext, UIComponent component) {
60      super.decode(facesContext, component);
61      String clientId = component.getClientId(facesContext);
62      if (clientId.equals(FacesContextUtils.getActionId(facesContext))) {
63        // this is a inputSuggest request -> render response
64        facesContext.renderResponse();
65      }
66    }
67  
68    @Override
69    public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
70      if (!(component instanceof AbstractUIInput)) {
71        LOG.error("Wrong type: Need " + AbstractUIInput.class.getName() + ", but was " + component.getClass().getName());
72        return;
73      }
74  
75      String clientId = component.getClientId(facesContext);
76      if (clientId.equals(FacesContextUtils.getActionId(facesContext))) {
77        // this is a inputSuggest
78        encodeAjax(facesContext, component);
79      } else {
80  
81        AbstractUIInput input = (AbstractUIInput) component;
82  
83        String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, input);
84  
85        String currentValue = getCurrentValue(facesContext, input);
86        if (LOG.isDebugEnabled()) {
87          LOG.debug("currentValue = '" + currentValue + "'");
88        }
89        String type = ComponentUtils.getBooleanAttribute(input,
90            Attributes.PASSWORD) ? HtmlInputTypes.PASSWORD : HtmlInputTypes.TEXT;
91  
92        // Todo: check for valid binding
93        boolean renderAjaxSuggest = false;
94        if (input instanceof UIIn) {
95          renderAjaxSuggest = ((UIIn) input).getSuggestMethod() != null;
96        }
97        String id = input.getClientId(facesContext);
98        TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
99        writer.startElement(HtmlElements.INPUT, input);
100       writer.writeAttribute(HtmlAttributes.TYPE, type, false);
101       writer.writeNameAttribute(id);
102       writer.writeIdAttribute(id);
103       HtmlRendererUtils.writeDataAttributes(facesContext, writer, input);
104       if (currentValue != null) {
105         writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
106       }
107       if (title != null) {
108         writer.writeAttribute(HtmlAttributes.TITLE, title, true);
109       }
110       int maxLength = 0;
111       String pattern = null;
112       for (Validator validator : input.getValidators()) {
113         if (validator instanceof LengthValidator) {
114           LengthValidator lengthValidator = (LengthValidator) validator;
115           maxLength = lengthValidator.getMaximum();
116         }
117         /*if (validator instanceof RegexValidator) {
118           RegexValidator regexValidator = (RegexValidator) validator;
119           pattern = regexValidator.getPattern();
120         }*/
121       }
122       if (maxLength > 0) {
123         writer.writeAttribute(HtmlAttributes.MAXLENGTH, maxLength);
124       }
125       if (pattern != null) {
126         writer.writeAttribute(HtmlAttributes.PATTERN, pattern, false);
127       }
128       writer.writeAttribute(HtmlAttributes.READONLY, ComponentUtils.getBooleanAttribute(input, Attributes.READONLY));
129       writer.writeAttribute(HtmlAttributes.DISABLED, ComponentUtils.getBooleanAttribute(input, Attributes.DISABLED));
130       Integer tabIndex = input.getTabIndex();
131       if (tabIndex != null) {
132         writer.writeAttribute(HtmlAttributes.TABINDEX, tabIndex);
133       }
134       Style style = new Style(facesContext, input);
135       writer.writeStyleAttribute(style);
136 
137       if (renderAjaxSuggest || (input instanceof UIIn && !((UIIn) input).isAutocomplete())) {
138         writer.writeAttribute(HtmlAttributes.AUTOCOMPLETE, "off", false);
139       }
140 
141       HtmlRendererUtils.renderDojoDndItem(component, writer, true);
142       writer.writeClassAttribute(Classes.create(input));
143       /*if (component instanceof AbstractUIInput) {
144        String onchange = HtmlUtils.generateOnchange((AbstractUIInput) component, facesContext);
145        if (onchange != null) {
146          // TODO: create and use utility method to write attributes without quoting
147      //      writer.writeAttribute(HtmlAttributes.ONCHANGE, onchange, null);
148        }
149      } */
150       boolean required = ComponentUtils.getBooleanAttribute(input, Attributes.REQUIRED);
151       writer.writeAttribute(HtmlAttributes.REQUIRED, required);
152       writer.writeAttribute(DataAttributes.SUGGEST, renderAjaxSuggest);
153       if (renderAjaxSuggest) {
154         UIIn in = (UIIn) input;
155         writer.writeAttribute(DataAttributes.SUGGEST_MIN_CHARS, in.getSuggestMinChars());
156         writer.writeAttribute(DataAttributes.SUGGEST_DELAY, in.getSuggestDelay());
157       }
158 
159       HtmlRendererUtils.renderFocus(id, input.isFocus(), ComponentUtils.isError(input), facesContext, writer);
160       writeAdditionalAttributes(facesContext, writer, input);
161       HtmlRendererUtils.renderCommandFacet(input, facesContext, writer);
162       writer.endElement(HtmlElements.INPUT);
163     }
164   }
165 
166   protected void writeAdditionalAttributes(
167       FacesContext facesContext, TobagoResponseWriter writer, AbstractUIInput input) throws IOException {
168   }
169 
170   private void encodeAjax(FacesContext facesContext, UIComponent component) throws IOException {
171     if (!(component instanceof UIIn)) {
172       LOG.error("Wrong type: Need " + UIIn.class.getName() + ", but was " + component.getClass().getName());
173       return;
174     }
175 
176     final UIIn input = (UIIn) component;
177     final MethodBinding methodBinding = input.getSuggestMethod();
178     final AutoSuggestItems items
179             = createAutoSuggestItems(methodBinding.invoke(facesContext, new Object[]{input}));
180     final List<AutoSuggestItem> suggestItems = items.getItems();
181     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
182 
183     writer.startJavascript();
184 
185     writer.write("Tobago.ajaxComponents['");
186     writer.write(component.getClientId(facesContext));
187     writer.write("'].suggestions = {items: [");
188 
189     for (int i = 0; i < suggestItems.size() && i < items.getMaxSuggestedCount(); i++) {
190       AutoSuggestItem suggestItem = suggestItems.get(i);
191       if (i > 0) {
192         writer.write(", ");
193       }
194       writer.write("{label: '");
195       String label = suggestItem.getLabel();
196       label = StringUtils.replace(label, "\\", "\\\\");
197       label = StringUtils.replace(label, "\"", "\\\"");
198       writer.write(label);
199       writer.write("', value: '");
200       String value = suggestItem.getLabel();
201       value = StringUtils.replace(value, "\\", "\\\\");
202       value = StringUtils.replace(value, "\"", "\\\"");
203       writer.write(value);
204       writer.write("'");
205       if (suggestItem.getExtensionItems() != null) {
206         writer.write(", values: [");
207         for (int j = 0; j < suggestItem.getExtensionItems().size(); j++) {
208           AutoSuggestExtensionItem item = suggestItem.getExtensionItems().get(j);
209           if (j > 0) {
210             writer.write(", ");
211           }
212           writer.write("{id: '");
213           writer.write(item.getId());
214           writer.write("', value: '");
215           writer.write(item.getValue());
216           writer.write("'}");
217         }
218         writer.write("]");
219       }
220       if (suggestItem.getNextFocusId() != null) {
221         writer.write(", nextFocusId: '");
222         writer.write(suggestItem.getNextFocusId());
223         writer.write("'");
224       }
225 
226       writer.write("}");
227 
228     }
229 
230     writer.write("]");
231     if (items.getNextFocusId() != null) {
232       writer.write(", nextFocusId: \"");
233       writer.write(items.getNextFocusId());
234       writer.write("\"");
235     }
236 
237     if (suggestItems.size() > items.getMaxSuggestedCount()) {
238       writer.write(", moreElements: \"");
239       writer.write(
240           ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "tobago.in.inputSuggest.moreElements"));
241       writer.write("\"");
242     }
243 
244     writer.write("};");
245     writer.endJavascript();
246   }
247 
248   private AutoSuggestItems createAutoSuggestItems(Object object) {
249     if (object instanceof AutoSuggestItems) {
250       return (AutoSuggestItems) object;
251     }
252     AutoSuggestItems autoSuggestItems = new AutoSuggestItems();
253     if (object instanceof List && !((List) object).isEmpty()) {
254       if (((List) object).get(0) instanceof AutoSuggestItem) {
255         //noinspection unchecked
256         autoSuggestItems.setItems((List<AutoSuggestItem>) object);
257       } else if (((List) object).get(0) instanceof String) {
258         List<AutoSuggestItem> items = new ArrayList<AutoSuggestItem>(((List) object).size());
259         for (int i = 0; i < ((List) object).size(); i++) {
260           AutoSuggestItem item = new AutoSuggestItem();
261           item.setLabel((String) ((List) object).get(i));
262           item.setValue((String) ((List) object).get(i));
263           items.add(item);
264         }
265         autoSuggestItems.setItems(items);
266       } else {
267         throw new IllegalArgumentException("Cant create AutoSuggestItems from " + object);
268       }
269     } else {
270       autoSuggestItems.setItems(Collections.<AutoSuggestItem>emptyList());
271     }
272     return autoSuggestItems;
273   }
274 }