View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.fuzzysearch;
23  
24  import java.io.IOException;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UISelectOne;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.faces.model.SelectItem;
33  
34  import org.primefaces.component.inputtext.InputText;
35  import org.primefaces.extensions.util.Attrs;
36  import org.primefaces.renderkit.SelectOneRenderer;
37  import org.primefaces.shaded.json.JSONArray;
38  import org.primefaces.shaded.json.JSONObject;
39  import org.primefaces.util.ComponentUtils;
40  import org.primefaces.util.HTML;
41  import org.primefaces.util.WidgetBuilder;
42  
43  /**
44   * <code>FuzzySearch</code> component.
45   *
46   * @author https://github.com/aripddev
47   * @since 8.0.1
48   */
49  public class FuzzySearchRenderer extends SelectOneRenderer {
50  
51      private static final String INPUT = "_input";
52  
53      @Override
54      public void decode(final FacesContext context, final UIComponent component) {
55          final FuzzySearch fuzzySearch = (FuzzySearch) component;
56          if (!shouldDecode(fuzzySearch)) {
57              return;
58          }
59  
60          final String clientId = fuzzySearch.getClientId(context);
61          final Map<String, String> params = context.getExternalContext().getRequestParameterMap();
62          final String submittedValue = params.get(clientId + INPUT);
63  
64          if (submittedValue != null) {
65              fuzzySearch.setSubmittedValue(submittedValue);
66          }
67  
68          decodeBehaviors(context, fuzzySearch);
69      }
70  
71      @Override
72      public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
73          final FuzzySearch fuzzySearch = (FuzzySearch) component;
74  
75          encodeMarkup(context, fuzzySearch);
76          encodeScript(context, fuzzySearch);
77      }
78  
79      protected void encodeMarkup(final FacesContext context, final FuzzySearch fuzzySearch) throws IOException {
80          final ResponseWriter writer = context.getResponseWriter();
81  
82          final String clientId = fuzzySearch.getClientId(context);
83          final List<SelectItem> selectItems = getSelectItems(context, fuzzySearch);
84          final int selectItemsSize = selectItems.size();
85          final String style = fuzzySearch.getStyle();
86          final String styleClass = getStyleClassBuilder(context)
87                      .add(FuzzySearch.STYLE_CLASS)
88                      .add(fuzzySearch.getStyleClass())
89                      .add("ui-buttonset-" + selectItemsSize)
90                      .add(!fuzzySearch.isValid(), "ui-state-error")
91                      .build();
92  
93          writer.startElement("div", fuzzySearch);
94          writer.writeAttribute("id", clientId, "id");
95          writer.writeAttribute(Attrs.CLASS, styleClass, "styleClass");
96          if (style != null) {
97              writer.writeAttribute(Attrs.STYLE, style, Attrs.STYLE);
98          }
99  
100         writer.startElement("input", fuzzySearch);
101         writer.writeAttribute("id", clientId + INPUT, null);
102         writer.writeAttribute("name", clientId + INPUT, null);
103         writer.writeAttribute("placeholder", fuzzySearch.getPlaceholder(), null);
104         writer.writeAttribute("class", createStyleClass(fuzzySearch, InputText.STYLE_CLASS), "styleClass");
105         renderPassThruAttributes(context, fuzzySearch, HTML.TAB_INDEX);
106         renderDomEvents(context, fuzzySearch, HTML.BLUR_FOCUS_EVENTS);
107         renderAccessibilityAttributes(context, fuzzySearch);
108         renderValidationMetadata(context, fuzzySearch);
109         final String valueToRender = ComponentUtils.getValueToRender(context, fuzzySearch);
110         if (valueToRender != null) {
111             writer.writeAttribute("value", valueToRender, null);
112         }
113         writer.endElement("input");
114 
115         writer.startElement("div", fuzzySearch);
116         writer.writeAttribute("id", clientId + "_fuzzysearch-search-results", null);
117 
118         if (fuzzySearch.isListItemsAtTheBeginning()) {
119             encodeSelectItems(context, fuzzySearch, selectItems);
120         }
121 
122         writer.endElement("div");
123 
124         writer.endElement("div");
125     }
126 
127     protected void encodeSelectItems(final FacesContext context, final FuzzySearch fuzzySearch, final List<SelectItem> selectItems) throws IOException {
128         for (final SelectItem selectItem : selectItems) {
129             encodeOption(context, fuzzySearch, selectItem);
130         }
131     }
132 
133     protected void encodeOption(
134                 final FacesContext context, final FuzzySearch fuzzySearch, final SelectItem option) throws IOException {
135 
136         final ResponseWriter writer = context.getResponseWriter();
137         final String itemValueAsString = getOptionAsString(context, fuzzySearch, fuzzySearch.getConverter(), option.getValue());
138 
139         final String resultStyle = fuzzySearch.getResultStyle();
140         final String resultStyleClass = getStyleClassBuilder(context)
141                     .add(FuzzySearch.ITEM_CLASS)
142                     .add(fuzzySearch.getResultStyleClass())
143                     .build();
144 
145         // results
146         writer.startElement("div", null);
147         writer.writeAttribute("class", resultStyleClass, "resultStyleClass");
148         if (resultStyle != null) {
149             writer.writeAttribute(Attrs.STYLE, resultStyle, "resultStyle");
150         }
151         writer.writeAttribute("tabindex", fuzzySearch.getTabindex(), null);
152         if (option.getDescription() != null) {
153             writer.writeAttribute("title", option.getDescription(), null);
154         }
155         writer.writeAttribute("data-item-value", itemValueAsString, null);
156 
157         if (option.isEscape()) {
158             writer.writeText(option.getLabel(), "itemLabel");
159         }
160         else {
161             writer.write(option.getLabel());
162         }
163 
164         writer.endElement("div");
165     }
166 
167     protected void encodeScript(final FacesContext context, final FuzzySearch fuzzySearch) throws IOException {
168         final WidgetBuilder wb = getWidgetBuilder(context);
169 
170         final List<SelectItem> selectItems = getSelectItems(context, fuzzySearch);
171         final JSONArray ja = new JSONArray();
172         for (final SelectItem selectItem : selectItems) {
173             ja.put(new JSONObject(selectItem));
174         }
175         final String jsonDatasource = ja.toString();
176 
177         wb.init(FuzzySearch.class.getSimpleName(), fuzzySearch)
178                     .attr("resultStyle", fuzzySearch.getResultStyle())
179                     .attr("resultStyleClass", fuzzySearch.getResultStyleClass())
180                     .attr("listItemsAtTheBeginning", fuzzySearch.isListItemsAtTheBeginning())
181                     .attr("datasource", jsonDatasource)
182                     .attr("unselectable", fuzzySearch.isUnselectable(), true)
183                     .attr("highlight", fuzzySearch.isHighlight(), true)
184                     .callback("change", "function()", fuzzySearch.getOnchange());
185 
186         encodeClientBehaviors(context, fuzzySearch);
187 
188         wb.finish();
189     }
190 
191     @Override
192     protected String getSubmitParam(final FacesContext context, final UISelectOne selectOne) {
193         return selectOne.getClientId(context);
194     }
195 
196 }