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.timepicker;
23
24 import java.io.IOException;
25 import java.text.ParseException;
26 import java.text.SimpleDateFormat;
27 import java.time.LocalDateTime;
28 import java.time.LocalTime;
29 import java.time.format.DateTimeFormatter;
30
31 import javax.el.ValueExpression;
32 import javax.faces.component.UIComponent;
33 import javax.faces.context.FacesContext;
34 import javax.faces.context.ResponseWriter;
35 import javax.faces.convert.Converter;
36 import javax.faces.convert.ConverterException;
37
38 import org.primefaces.extensions.util.Attrs;
39 import org.primefaces.extensions.util.MessageFactory;
40 import org.primefaces.renderkit.InputRenderer;
41 import org.primefaces.util.Constants;
42 import org.primefaces.util.HTML;
43 import org.primefaces.util.LangUtils;
44 import org.primefaces.util.WidgetBuilder;
45
46
47
48
49
50
51
52
53 public class TimePickerRenderer extends InputRenderer {
54
55 private static final String BUTTON = "button";
56
57 @Override
58 public void decode(final FacesContext fc, final UIComponent component) {
59 final TimePicker timepicker = (TimePicker) component;
60
61 if (!shouldDecode(timepicker)) {
62 return;
63 }
64
65 final String param = timepicker.getClientId(fc) + "_input";
66 final String submittedValue = fc.getExternalContext().getRequestParameterMap().get(param);
67
68 if (submittedValue != null) {
69 timepicker.setSubmittedValue(submittedValue);
70 }
71
72 decodeBehaviors(fc, timepicker);
73 }
74
75 @Override
76 public void encodeEnd(final FacesContext fc, final UIComponent component) throws IOException {
77 final TimePicker timepicker = (TimePicker) component;
78 final String value = getValueAsString(fc, timepicker);
79
80 encodeMarkup(fc, timepicker, value);
81 encodeScript(fc, timepicker, value);
82 }
83
84 @Override
85 public Object getConvertedValue(final FacesContext fc, final UIComponent component, final Object submittedValue) {
86 final String value = (String) submittedValue;
87 if (LangUtils.isBlank(value)) {
88 return null;
89 }
90
91 final TimePicker timepicker = (TimePicker) component;
92 final Converter<?> converter = timepicker.getConverter();
93
94
95 if (converter != null) {
96 return converter.getAsObject(fc, timepicker, value);
97 }
98
99
100 try {
101 final Class<?> type = resolveDateType(fc, timepicker);
102 if (type == LocalTime.class) {
103 final DateTimeFormatter formatter = getDateTimeFormatter(timepicker);
104 return LocalTime.parse(value, formatter);
105 }
106 else {
107 final SimpleDateFormat formatter = getSimpleDateFormat(timepicker);
108 return formatter.parse(value);
109 }
110 }
111 catch (final ParseException e) {
112 throw new ConverterException(
113 MessageFactory.getMessage(timepicker.calculateLocale(),
114 TimePicker.TIME_MESSAGE_KEY,
115 value,
116 getDateTimeFormatter(timepicker).format(LocalDateTime.now()),
117 MessageFactory.getLabel(fc, component)),
118 e);
119 }
120 catch (final Exception e) {
121 throw new ConverterException(e.getMessage(), e);
122 }
123 }
124
125 protected void encodeMarkup(final FacesContext fc, final TimePicker timepicker, final String value)
126 throws IOException {
127 final ResponseWriter writer = fc.getResponseWriter();
128 final String clientId = timepicker.getClientId(fc);
129 final String inputId = clientId + "_input";
130
131 writer.startElement("span", timepicker);
132 writer.writeAttribute("id", clientId, null);
133
134 String containerClass = TimePicker.CONTAINER_CLASS;
135 if (timepicker.isSpinner()) {
136 containerClass += " ui-spinner";
137 }
138 if (timepicker.isShowOnButton()) {
139 containerClass += " ui-inputgroup";
140 }
141 writer.writeAttribute(Attrs.CLASS, containerClass, null);
142
143 if (timepicker.isInline()) {
144
145 writer.startElement("div", null);
146 writer.writeAttribute("id", clientId + "_inline", null);
147 writer.endElement("div");
148 }
149
150 writer.startElement("input", null);
151 writer.writeAttribute("id", inputId, null);
152 writer.writeAttribute("name", inputId, null);
153 writer.writeAttribute("type", timepicker.isInline() ? "hidden" : "text", null);
154 if (timepicker.getSize() > 0) {
155 writer.writeAttribute("size", timepicker.getSize(), null);
156 }
157 writer.writeAttribute("autocomplete", "off", null);
158
159 if (timepicker.isReadonlyInput()) {
160 writer.writeAttribute("readonly", "readonly", null);
161 }
162
163 if (LangUtils.isNotBlank(value)) {
164 writer.writeAttribute("value", value, null);
165 }
166
167 if (!timepicker.isInline()) {
168 final String styleClass = getStyleClassBuilder(fc)
169 .add(TimePicker.INPUT_CLASS)
170 .add(timepicker.getStyleClass())
171 .add(timepicker.isSpinner(), "ui-spinner-input")
172 .add(timepicker.isShowOnButton(), "ui-inputtext")
173 .add(!timepicker.isValid(), "ui-state-error")
174 .build();
175
176 writer.writeAttribute(Attrs.CLASS, styleClass, null);
177
178 if (timepicker.getStyle() != null) {
179 writer.writeAttribute(Attrs.STYLE, timepicker.getStyle(), null);
180 }
181 }
182
183 renderAccessibilityAttributes(fc, timepicker);
184 renderPassThruAttributes(fc, timepicker, HTML.INPUT_TEXT_ATTRS_WITHOUT_EVENTS);
185 renderDomEvents(fc, timepicker, HTML.INPUT_TEXT_EVENTS);
186 renderValidationMetadata(fc, timepicker);
187
188 writer.endElement("input");
189
190 if (timepicker.isSpinner()) {
191 final boolean disabled = timepicker.isDisabled() || timepicker.isReadonly();
192 encodeSpinnerButton(fc, TimePicker.UP_BUTTON_CLASS, TimePicker.UP_ICON_CLASS, disabled);
193 encodeSpinnerButton(fc, TimePicker.DOWN_BUTTON_CLASS, TimePicker.DOWN_ICON_CLASS, disabled);
194 }
195
196 if (timepicker.isShowOnButton()) {
197 writer.startElement(BUTTON, null);
198 writer.writeAttribute(Attrs.CLASS, TimePicker.BUTTON_TRIGGER_CLASS, null);
199 writer.writeAttribute("type", BUTTON, null);
200 writer.writeAttribute("role", BUTTON, null);
201
202 writer.startElement("span", null);
203 writer.writeAttribute(Attrs.CLASS, TimePicker.BUTTON_TRIGGER_ICON_CLASS, null);
204 writer.endElement("span");
205
206 writer.startElement("span", null);
207 writer.writeAttribute(Attrs.CLASS, TimePicker.BUTTON_TRIGGER_TEXT_CLASS, null);
208 writer.write("ui-button");
209 writer.endElement("span");
210
211 writer.endElement(BUTTON);
212 }
213
214 writer.endElement("span");
215 }
216
217 protected void encodeScript(final FacesContext fc, final TimePicker timepicker, final String value)
218 throws IOException {
219 final String clientId = timepicker.getClientId(fc);
220
221 final WidgetBuilder wb = getWidgetBuilder(fc);
222 wb.init("ExtTimePicker", timepicker);
223 wb.attr("timeSeparator", timepicker.getTimeSeparator());
224 wb.attr("myPosition", timepicker.getDialogPosition());
225 wb.attr("atPosition", timepicker.getInputPosition());
226 wb.attr("showPeriod", timepicker.isShowPeriod());
227 wb.attr("showPeriodLabels", timepicker.isShowPeriod());
228 wb.attr("modeInline", timepicker.isInline());
229 wb.attr("modeSpinner", timepicker.isSpinner());
230 wb.nativeAttr("hours", "{starts:" + timepicker.getStartHours() + ",ends:" + timepicker.getEndHours() + "}");
231 wb.nativeAttr("minutes", "{starts:" + timepicker.getStartMinutes() + ",ends:" + timepicker.getEndMinutes()
232 + ",interval:" + timepicker.getIntervalMinutes() + "}");
233 wb.attr("rows", timepicker.getRows());
234 wb.attr("showHours", timepicker.isShowHours());
235 wb.attr("showMinutes", timepicker.isShowMinutes());
236 wb.attr("showCloseButton", timepicker.isShowCloseButton());
237 wb.attr("showNowButton", timepicker.isShowNowButton());
238 wb.attr("showDeselectButton", timepicker.isShowDeselectButton());
239
240 if (timepicker.getOnHourShow() != null) {
241 wb.nativeAttr("onHourShow", timepicker.getOnHourShow());
242 }
243
244 if (timepicker.getOnMinuteShow() != null) {
245 wb.nativeAttr("onMinuteShow", timepicker.getOnMinuteShow());
246 }
247
248 if (timepicker.isShowOnButton()) {
249 wb.attr("showOn", timepicker.getShowOn());
250 wb.selectorAttr(BUTTON, "#" + clientId + " .pe-timepicker-trigger");
251 }
252
253 wb.attr("locale", timepicker.calculateLocale().toString());
254 wb.attr("disabled", timepicker.isDisabled() || timepicker.isReadonly());
255
256 if (LangUtils.isBlank(value)) {
257 wb.attr("defaultTime", Constants.EMPTY_STRING);
258 }
259 else if (timepicker.isInline()) {
260 wb.attr("defaultTime", value);
261 }
262
263 if (timepicker.getMinHour() != null || timepicker.getMinMinute() != null) {
264 wb.nativeAttr("minTime", "{hour:" + timepicker.getMinHour()
265 + ",minute:" + timepicker.getMinMinute() + "}");
266 }
267
268 if (timepicker.getMaxHour() != null || timepicker.getMaxMinute() != null) {
269 wb.nativeAttr("maxTime", "{hour:" + timepicker.getMaxHour()
270 + ",minute:" + timepicker.getMaxMinute() + "}");
271 }
272
273 encodeClientBehaviors(fc, timepicker);
274
275 wb.finish();
276 }
277
278 protected void encodeSpinnerButton(final FacesContext fc, String styleClass, final String iconClass,
279 final boolean disabled)
280 throws IOException {
281 final ResponseWriter writer = fc.getResponseWriter();
282 styleClass = disabled ? styleClass + " ui-state-disabled" : styleClass;
283
284 writer.startElement("a", null);
285
286 writer.writeAttribute(Attrs.CLASS, styleClass, null);
287 writer.startElement("span", null);
288 writer.writeAttribute(Attrs.CLASS, "ui-button-text", null);
289 writer.startElement("span", null);
290 writer.writeAttribute(Attrs.CLASS, iconClass, null);
291 writer.endElement("span");
292 writer.endElement("span");
293
294 writer.endElement("a");
295 }
296
297 protected String getValueAsString(final FacesContext fc, final TimePicker timepicker) {
298 final Object submittedValue = timepicker.getSubmittedValue();
299 if (submittedValue != null) {
300 return submittedValue.toString();
301 }
302
303 final Object value = timepicker.getValue();
304 if (value == null) {
305 return null;
306 }
307 else {
308 if (timepicker.getConverter() != null) {
309
310 return timepicker.getConverter().getAsString(fc, timepicker, value);
311 }
312 else {
313
314 if (value instanceof LocalTime) {
315 final DateTimeFormatter formatter = getDateTimeFormatter(timepicker);
316 return formatter.format((LocalTime) value);
317 }
318 else {
319 final SimpleDateFormat formatter = getSimpleDateFormat(timepicker);
320 return formatter.format(value);
321 }
322 }
323 }
324 }
325
326 protected String getPattern(final TimePicker timepicker) {
327 if (!timepicker.isShowMinutes()) {
328 return timepicker.getTimePatternWithoutMinutes();
329 }
330 else if (!timepicker.isShowHours()) {
331 return timepicker.getTimePatternWithoutHours();
332 }
333 else if (timepicker.isShowPeriod()) {
334 return timepicker.getTimePattern12();
335 }
336 else {
337 return timepicker.getTimePattern24();
338 }
339 }
340
341 protected DateTimeFormatter getDateTimeFormatter(final TimePicker timepicker) {
342 return DateTimeFormatter.ofPattern(getPattern(timepicker), timepicker.calculateLocale());
343 }
344
345 protected SimpleDateFormat getSimpleDateFormat(final TimePicker timepicker) {
346 return new SimpleDateFormat(getPattern(timepicker), timepicker.calculateLocale());
347 }
348
349 protected Class<?> resolveDateType(final FacesContext context, final TimePicker timePicker) {
350 final ValueExpression ve = timePicker.getValueExpression("value");
351
352 if (ve == null) {
353 return null;
354 }
355
356 Class<?> type = ve.getType(context.getELContext());
357
358
359 if (type == null) {
360 type = LocalTime.class;
361 }
362
363 return type;
364 }
365 }