1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.primefaces.extensions.component.tristatemanycheckbox;
23
24 import java.io.IOException;
25 import java.util.*;
26 import java.util.Map.Entry;
27
28 import javax.faces.FacesException;
29 import javax.faces.component.UIComponent;
30 import javax.faces.component.UIInput;
31 import javax.faces.component.UINamingContainer;
32 import javax.faces.component.UISelectMany;
33 import javax.faces.context.FacesContext;
34 import javax.faces.context.ResponseWriter;
35 import javax.faces.convert.Converter;
36 import javax.faces.model.SelectItem;
37
38 import org.primefaces.component.selectmanycheckbox.SelectManyCheckbox;
39 import org.primefaces.extensions.util.Attrs;
40 import org.primefaces.renderkit.SelectManyRenderer;
41 import org.primefaces.util.Constants;
42 import org.primefaces.util.EscapeUtils;
43 import org.primefaces.util.HTML;
44 import org.primefaces.util.WidgetBuilder;
45
46
47
48
49
50
51
52
53 public class TriStateManyCheckboxRenderer extends SelectManyRenderer {
54
55 @Override
56 public Object getConvertedValue(final FacesContext context, final UIComponent component, final Object submittedValue) {
57
58 if (submittedValue instanceof Map) {
59 final Map<String, Object> mapSub = (Map<String, Object>) submittedValue;
60 final List<String> keyValues = new ArrayList<>(mapSub.keySet());
61 final Map<String, Object> mapSubConv = new LinkedHashMap<>();
62 final TriStateManyCheckbox checkbox = (TriStateManyCheckbox) component;
63 final Converter<?> converter = checkbox.getConverter();
64 if (converter != null) {
65 for (final String keyVal : keyValues) {
66 final Object mapVal = converter.getAsObject(context, checkbox, (String) mapSub.get(keyVal));
67 mapSubConv.put(keyVal, mapVal);
68 }
69
70 return mapSubConv;
71 }
72 else {
73 return mapSub;
74 }
75 }
76 else {
77 throw new FacesException("Value of '" + component.getClientId() + "'must be a Map instance");
78 }
79 }
80
81 @Override
82 public void decode(final FacesContext context, final UIComponent component) {
83 final TriStateManyCheckbox checkbox = (TriStateManyCheckbox) component;
84 if (!shouldDecode(checkbox)) {
85 return;
86 }
87
88 decodeBehaviors(context, checkbox);
89
90 final String submitParam = getSubmitParam(context, checkbox);
91 final Map<String, String[]> params = context.getExternalContext().getRequestParameterValuesMap();
92
93 String[] valuesArray = null;
94 if (params.containsKey(submitParam)) {
95 valuesArray = params.get(submitParam);
96 }
97
98 checkbox.setSubmittedValue(getSubmitedMap(context, checkbox, valuesArray));
99 }
100
101 @Override
102 public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
103 final TriStateManyCheckbox checkbox = (TriStateManyCheckbox) component;
104 encodeMarkup(context, checkbox);
105 encodeScript(context, checkbox);
106 }
107
108 protected void encodeMarkup(final FacesContext context, final TriStateManyCheckbox checkbox) throws IOException {
109 final ResponseWriter writer = context.getResponseWriter();
110 final String clientId = checkbox.getClientId(context);
111 final String style = checkbox.getStyle();
112 final String styleClass = getStyleClassBuilder(context)
113 .add(SelectManyCheckbox.STYLE_CLASS)
114 .add(checkbox.getStyleClass())
115 .build();
116
117 writer.startElement("table", checkbox);
118 writer.writeAttribute("id", clientId, "id");
119 writer.writeAttribute(Attrs.CLASS, styleClass, "styleClass");
120 if (style != null) {
121 writer.writeAttribute(Attrs.STYLE, style, Attrs.STYLE);
122 }
123
124 encodeSelectItems(context, checkbox);
125
126 writer.endElement("table");
127 }
128
129 protected void encodeSelectItems(final FacesContext context, final TriStateManyCheckbox checkbox) throws IOException {
130 final ResponseWriter writer = context.getResponseWriter();
131 final List<SelectItem> selectItems = getSelectItems(context, checkbox);
132 final Converter<Object> converter = checkbox.getConverter();
133 Map<String, Object> values = getValues(checkbox);
134 final Map<String, Object> submittedMap = getSubmittedFromComp(checkbox);
135 final String layout = checkbox.getLayout();
136 final boolean pageDirection = layout != null && "pageDirection".equals(layout);
137
138 if (submittedMap != null) {
139 values = submittedMap;
140 }
141
142 if (converter != null && submittedMap == null) {
143 for (final Entry<String, Object> entry : values.entrySet()) {
144 final String keyValue = converter.getAsString(context, checkbox, entry.getValue());
145 values.put(entry.getKey(), keyValue);
146 }
147 }
148
149 int idx = -1;
150 for (final SelectItem selectItem : selectItems) {
151 idx++;
152 if (pageDirection) {
153 writer.startElement("tr", null);
154 }
155
156 encodeOption(context, checkbox, values, selectItem, idx);
157 if (pageDirection) {
158 writer.endElement("tr");
159 }
160 }
161 }
162
163 protected void encodeOption(final FacesContext context, final UIInput component, final Map<String, Object> values,
164 final SelectItem option, final int idx) throws IOException {
165 final ResponseWriter writer = context.getResponseWriter();
166 final TriStateManyCheckbox checkbox = (TriStateManyCheckbox) component;
167 final String itemValueAsString = String.valueOf(option.getValue());
168 final String name = checkbox.getClientId(context);
169 final String id = name + UINamingContainer.getSeparatorChar(context) + idx;
170 final boolean disabled = option.isDisabled() || checkbox.isDisabled();
171
172 final String itemValue = (String) option.getValue();
173
174 final int valueInput = getValueForInput(component, itemValue, values);
175 if (option.isNoSelectionOption() && values != null && Constants.EMPTY_STRING.equals(itemValue)) {
176 return;
177 }
178
179 writer.startElement("td", null);
180
181 writer.startElement("div", null);
182 writer.writeAttribute(Attrs.CLASS, HTML.CHECKBOX_CLASS, null);
183
184 encodeOptionInput(context, checkbox, id, name, disabled, itemValueAsString, valueInput);
185 encodeOptionOutput(context, checkbox, valueInput, disabled);
186
187 writer.endElement("div");
188 writer.endElement("td");
189
190 writer.startElement("td", null);
191 encodeOptionLabel(context, id, option, disabled);
192 writer.endElement("td");
193 }
194
195 protected void encodeOptionInput(final FacesContext context, final TriStateManyCheckbox checkbox, final String id, final String name,
196 final boolean disabled, final String value, final int valueInput) throws IOException {
197 final ResponseWriter writer = context.getResponseWriter();
198
199 writer.startElement("div", null);
200 writer.writeAttribute(Attrs.CLASS, HTML.CHECKBOX_INPUT_WRAPPER_CLASS, null);
201
202 writer.startElement("input", null);
203 writer.writeAttribute("id", id, null);
204 writer.writeAttribute("name", name, null);
205 writer.writeAttribute("type", "text", null);
206 writer.writeAttribute("value", valueInput, null);
207 writer.writeAttribute("itemValue", value, null);
208
209 if (disabled) {
210 writer.writeAttribute("disabled", "disabled", null);
211 }
212
213 if (checkbox.getOnchange() != null) {
214 writer.writeAttribute("onchange", checkbox.getOnchange(), null);
215 }
216
217 writer.endElement("input");
218
219 writer.endElement("div");
220 }
221
222 protected void encodeOptionOutput(final FacesContext context, final TriStateManyCheckbox checkbox, final int valCheck,
223 final boolean disabled) throws IOException {
224 final ResponseWriter writer = context.getResponseWriter();
225 final String styleClass = getStyleClassBuilder(context)
226 .add(HTML.CHECKBOX_BOX_CLASS)
227 .add(valCheck == 1 || valCheck == 2, "ui-state-active")
228 .add(disabled, "ui-state-disabled")
229 .build();
230
231
232 final String stateOneIconClass = checkbox.getStateOneIcon() != null ? TriStateManyCheckbox.UI_ICON + checkbox.getStateOneIcon()
233 : Constants.EMPTY_STRING;
234 final String stateTwoIconClass = checkbox.getStateTwoIcon() != null ? TriStateManyCheckbox.UI_ICON + checkbox.getStateTwoIcon()
235 : TriStateManyCheckbox.UI_ICON + "ui-icon-check";
236 final String stataThreeIconClass = checkbox.getStateThreeIcon() != null ? TriStateManyCheckbox.UI_ICON + checkbox.getStateThreeIcon()
237 : TriStateManyCheckbox.UI_ICON + "ui-icon-closethick";
238
239 final String comma = "\",\"";
240 final String statesIconsClasses = "[\"" + stateOneIconClass + comma + stateTwoIconClass + comma + stataThreeIconClass + "\"]";
241
242 final String statesTitles = "[\"" + EscapeUtils.forJavaScript(checkbox.getStateOneTitle()) + comma
243 + EscapeUtils.forJavaScript(checkbox.getStateTwoTitle()) + comma + EscapeUtils.forJavaScript(checkbox.getStateThreeTitle()) + "\"]";
244
245 String iconClass = "ui-chkbox-icon ui-icon ui-c";
246 String activeTitle = Constants.EMPTY_STRING;
247 if (valCheck == 0) {
248 iconClass = iconClass + " " + stateOneIconClass;
249 activeTitle = checkbox.getStateOneTitle();
250 }
251 else if (valCheck == 1) {
252 iconClass = iconClass + " " + stateTwoIconClass;
253 activeTitle = checkbox.getStateTwoTitle();
254 }
255 else if (valCheck == 2) {
256 iconClass = iconClass + " " + stataThreeIconClass;
257 activeTitle = checkbox.getStateThreeTitle();
258 }
259
260 String dataTitles = Constants.EMPTY_STRING;
261 String titleAtt = Constants.EMPTY_STRING;
262
263 if (!checkbox.getStateOneTitle().isEmpty()
264 || !checkbox.getStateTwoTitle().isEmpty() || !checkbox.getStateThreeTitle().isEmpty()) {
265
266 dataTitles = "data-titlestates='" + statesTitles + "' ";
267
268 titleAtt = " title=\"" + EscapeUtils.forJavaScript(activeTitle) + "\" ";
269 }
270
271 String tabIndexTag = " tabIndex=0 ";
272 if (checkbox.getTabindex() != null) {
273 tabIndexTag = "tabIndex=" + checkbox.getTabindex() + " ";
274 }
275
276
277 writer.write("<div " + tabIndexTag + titleAtt + "class=\"" + styleClass + "\" data-iconstates='" + statesIconsClasses + "' "
278 + dataTitles + ">"
279 + "<span class=\"" + iconClass + "\"></span></div>");
280
281 }
282
283 protected void encodeScript(final FacesContext context, final TriStateManyCheckbox checkbox) throws IOException {
284 final WidgetBuilder wb = getWidgetBuilder(context);
285 wb.init("ExtTriStateManyCheckbox", checkbox);
286 encodeClientBehaviors(context, checkbox);
287 wb.finish();
288 }
289
290 protected void encodeOptionLabel(final FacesContext context, final String containerClientId,
291 final SelectItem option, final boolean disabled) throws IOException {
292 final ResponseWriter writer = context.getResponseWriter();
293
294 writer.startElement(Attrs.LABEL, null);
295 writer.writeAttribute("for", containerClientId, null);
296 if (disabled) {
297 writer.writeAttribute(Attrs.CLASS, "ui-state-disabled", null);
298 }
299
300 if (option.isEscape()) {
301 writer.writeText(option.getLabel(), null);
302 }
303 else {
304 writer.write(option.getLabel());
305 }
306
307 writer.endElement(Attrs.LABEL);
308 }
309
310 @Override
311 protected String getSubmitParam(final FacesContext context, final UISelectMany selectMany) {
312 return selectMany.getClientId(context);
313 }
314
315
316
317
318 protected int getValueForInput(final UIInput component, final String itemValue, final Map<String, Object> valueArray) {
319 try {
320 int retInt = 0;
321 if (itemValue == null || valueArray == null) {
322 return retInt;
323 }
324
325 if (valueArray.containsKey(itemValue)) {
326 retInt = Integer.parseInt((String) valueArray.get(itemValue));
327
328 return retInt % 3;
329 }
330 else {
331 return retInt;
332 }
333 }
334 catch (final NumberFormatException ex) {
335 throw new FacesException("State of '" + component.getClientId() + "' must be an integer representation");
336 }
337 }
338
339 @Override
340 protected Map getValues(final UIComponent component) {
341 final UISelectMany selectMany = (UISelectMany) component;
342 final Object value = selectMany.getValue();
343
344 if (value == null) {
345 return null;
346 }
347 else if (value instanceof Map) {
348
349 return (Map) value;
350 }
351 else {
352 throw new FacesException("Value of '" + component.getClientId() + "'must be a Map instance");
353 }
354 }
355
356 protected Map<String, Object> getSubmitedMap(final FacesContext context, final TriStateManyCheckbox checkbox, final String[] valuesArray) {
357 final List<SelectItem> selectItems = getSelectItems(context, checkbox);
358 final Map<String, Object> subValues = new LinkedHashMap<>();
359 if (valuesArray != null && valuesArray.length == selectItems.size()) {
360 int idx = -1;
361 for (final SelectItem item : selectItems) {
362 idx++;
363
364 final String keyMap = (String) item.getValue();
365 final Object valueMap = valuesArray[idx];
366 subValues.put(keyMap, valueMap);
367 }
368
369 return subValues;
370 }
371 else {
372 return null;
373 }
374 }
375
376 protected Map<String, Object> getSubmittedFromComp(final UIComponent component) {
377 final TriStateManyCheckbox checkbox = (TriStateManyCheckbox) component;
378 final Map<String, Object> ret = (Map<String, Object>) checkbox.getSubmittedValue();
379 if (ret != null) {
380 final Map<String, Object> subValues = new LinkedHashMap<>();
381
382
383 final Set<String> keys = ret.keySet();
384 final String[] tempArray = keys.toArray(new String[0]);
385
386 final int length = tempArray.length;
387 for (int i = length - 1; i >= 0; i--) {
388 final String key = tempArray[i];
389 final Object val = ret.get(key);
390 subValues.put(key, val);
391 }
392
393 return subValues;
394 }
395 else {
396 return null;
397 }
398 }
399 }