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.dynaform;
23
24 import java.io.IOException;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.logging.Logger;
28
29 import javax.faces.component.ContextCallback;
30 import javax.faces.component.EditableValueHolder;
31 import javax.faces.component.UIComponent;
32 import javax.faces.context.FacesContext;
33 import javax.faces.context.ResponseWriter;
34
35 import org.primefaces.component.api.InputHolder;
36 import org.primefaces.component.row.Row;
37 import org.primefaces.extensions.model.dynaform.AbstractDynaFormElement;
38 import org.primefaces.extensions.model.dynaform.DynaFormControl;
39 import org.primefaces.extensions.model.dynaform.DynaFormLabel;
40 import org.primefaces.extensions.model.dynaform.DynaFormModel;
41 import org.primefaces.extensions.model.dynaform.DynaFormModelElement;
42 import org.primefaces.extensions.model.dynaform.DynaFormRow;
43 import org.primefaces.extensions.util.Attrs;
44 import org.primefaces.extensions.util.ExtLangUtils;
45 import org.primefaces.renderkit.CoreRenderer;
46 import org.primefaces.util.CompositeUtils;
47 import org.primefaces.util.Constants;
48 import org.primefaces.util.FacetUtils;
49 import org.primefaces.util.WidgetBuilder;
50
51
52
53
54
55
56
57
58 public class DynaFormRenderer extends CoreRenderer {
59
60 public static final String FACET_HEADER_REGULAR = "headerRegular";
61 public static final String FACET_FOOTER_REGULAR = "footerRegular";
62 public static final String FACET_HEADER_EXTENDED = "headerExtended";
63 public static final String FACET_FOOTER_EXTENDED = "footerExtended";
64 public static final String FACET_BUTTON_BAR = "buttonBar";
65 public static final String FACET_STATIC_TOP = "staticTop";
66 public static final String FACET_STATIC_BOTTOM = "staticBottom";
67
68 public static final String GRID_CLASS = "pe-dynaform-grid";
69 public static final String NESTED_GRID_CLASS = "pe-dynaform-nested-grid";
70 public static final String CELL_CLASS = "pe-dynaform-cell";
71 public static final String CELL_FIRST_CLASS = "pe-dynaform-cell-first";
72 public static final String CELL_LAST_CLASS = "pe-dynaform-cell-last";
73 public static final String LABEL_CLASS = "pe-dynaform-label";
74 public static final String LABEL_INVALID_CLASS = "ui-state-error ui-corner-all";
75 public static final String LABEL_INDICATOR_CLASS = "pe-dynaform-label-rfi";
76 public static final String LABEL_CONTROL_TYPE_CLASS_FORMAT = "pe-dynaform-%s-label";
77
78 public static final String FACET_BUTTON_BAR_TOP_CLASS = "pe-dynaform-buttonbar-top";
79 public static final String FACET_BUTTON_BAR_BOTTOM_CLASS = "pe-dynaform-buttonbar-bottom";
80 public static final String FACET_HEADER_CLASS = "pe-dynaform-headerfacet";
81 public static final String FACET_FOOTER_CLASS = "pe-dynaform-footerfacet";
82 public static final String FACET_STATIC_TOP_CLASS = "pe-dynaform-static-top";
83 public static final String FACET_STATIC_BOTTOM_CLASS = "pe-dynaform-static-bottom";
84 public static final String EXTENDED_ROW_CLASS = "pe-dynaform-extendedrow";
85
86 public static final String BUTTON_BAR_ROLE = "toolbar";
87 public static final String GRID_CELL_ROLE = "gridcell";
88
89 private static final String[] EMPTY_COLUMN_CLASSES = new String[] {Constants.EMPTY_STRING, Constants.EMPTY_STRING};
90
91 private static final Logger LOGGER = Logger.getLogger(DynaFormRenderer.class.getName());
92
93 @Override
94 public void encodeEnd(final FacesContext fc, final UIComponent component) throws IOException {
95 final DynaForm dynaForm = (DynaForm) component;
96
97
98 final DynaFormModel dynaFormModel = (DynaFormModel) dynaForm.getValue();
99 encodeMarkup(fc, dynaForm, dynaFormModel, false);
100 encodeScript(fc, dynaForm, dynaFormModel);
101 }
102
103 protected void encodeMarkup(final FacesContext fc, final DynaForm dynaForm, final DynaFormModel dynaFormModel, final boolean nestedGrid)
104 throws IOException {
105 final ResponseWriter writer = fc.getResponseWriter();
106
107 writer.startElement("table", dynaForm);
108
109 if (!nestedGrid) {
110 final String clientId = dynaForm.getClientId(fc);
111 writer.writeAttribute("id", clientId, "id");
112 }
113
114 String styleClass = nestedGrid ? NESTED_GRID_CLASS : GRID_CLASS;
115 styleClass += dynaForm.getStyleClass() == null ? Constants.EMPTY_STRING : " " + dynaForm.getStyleClass();
116
117 writer.writeAttribute("cellspacing", "0", "cellspacing");
118 writer.writeAttribute(Attrs.CLASS, styleClass, "styleClass");
119 if (dynaForm.getStyle() != null) {
120 writer.writeAttribute(Attrs.STYLE, dynaForm.getStyle(), Attrs.STYLE);
121 }
122
123 writer.writeAttribute("role", "grid", null);
124
125
126 preRenderLabel(fc, dynaForm, dynaFormModel);
127
128 final int totalColspan = getTotalColspan(dynaFormModel);
129 final String bbPosition = dynaForm.getButtonBarPosition();
130
131 if (!nestedGrid) {
132 if ("top".equals(bbPosition) || "both".equals(bbPosition)) {
133 encodeFacet(fc, dynaForm, FACET_BUTTON_BAR, totalColspan, FACET_BUTTON_BAR_TOP_CLASS, BUTTON_BAR_ROLE, false, true);
134 }
135
136 encodeFacet(fc, dynaForm, FACET_HEADER_REGULAR, totalColspan, FACET_HEADER_CLASS, GRID_CELL_ROLE, false, true);
137 encodeStatic(fc, dynaForm, FACET_STATIC_TOP, totalColspan, FACET_STATIC_TOP_CLASS);
138 }
139
140
141 encodeBody(fc, dynaForm, dynaFormModel.getRegularRows(), false, true);
142
143 if (!nestedGrid) {
144 encodeFacet(fc, dynaForm, FACET_FOOTER_REGULAR, totalColspan, FACET_FOOTER_CLASS, GRID_CELL_ROLE, false, true);
145 encodeFacet(fc, dynaForm, FACET_HEADER_EXTENDED, totalColspan, FACET_HEADER_CLASS, GRID_CELL_ROLE, true,
146 dynaForm.isOpenExtended());
147 }
148
149
150 encodeBody(fc, dynaForm, dynaFormModel.getExtendedRows(), true, dynaForm.isOpenExtended());
151
152 if (!nestedGrid) {
153 encodeStatic(fc, dynaForm, FACET_STATIC_BOTTOM, totalColspan, FACET_STATIC_BOTTOM_CLASS);
154 encodeFacet(fc, dynaForm, FACET_FOOTER_EXTENDED, totalColspan, FACET_FOOTER_CLASS, GRID_CELL_ROLE, true, dynaForm.isOpenExtended());
155
156 if ("bottom".equals(bbPosition) || "both".equals(bbPosition)) {
157 encodeFacet(fc, dynaForm, FACET_BUTTON_BAR, totalColspan, FACET_BUTTON_BAR_BOTTOM_CLASS, BUTTON_BAR_ROLE, false, true);
158 }
159 }
160
161 writer.endElement("table");
162 }
163
164 protected void encodeScript(final FacesContext fc, final DynaForm dynaForm, final DynaFormModel dynaFormModel) throws IOException {
165 final WidgetBuilder wb = getWidgetBuilder(fc);
166 wb.init("ExtDynaForm", dynaForm);
167 wb.attr("uuid", dynaFormModel.getUuid());
168 wb.attr("autoSubmit", dynaForm.isAutoSubmit());
169 wb.attr("isPostback", fc.isPostback());
170 wb.finish();
171 }
172
173 protected void encodeFacet(final FacesContext fc, final DynaForm dynaForm, final String name, final int totalColspan, final String styleClass,
174 final String role,
175 final boolean extended, final boolean visible) throws IOException {
176 final UIComponent facet = dynaForm.getFacet(name);
177 if (FacetUtils.shouldRenderFacet(facet)) {
178 final ResponseWriter writer = fc.getResponseWriter();
179 writer.startElement("tr", null);
180 if (extended) {
181 writer.writeAttribute(Attrs.CLASS, EXTENDED_ROW_CLASS, null);
182 }
183
184 if (!visible) {
185 writer.writeAttribute(Attrs.STYLE, "display:none;", null);
186 }
187
188 writer.writeAttribute("role", "row", null);
189 writer.startElement("td", null);
190 if (totalColspan > 1) {
191 writer.writeAttribute("colspan", totalColspan, null);
192 }
193
194 writer.writeAttribute(Attrs.CLASS, styleClass, null);
195 writer.writeAttribute("role", role, null);
196
197 facet.encodeAll(fc);
198
199 writer.endElement("td");
200 writer.endElement("tr");
201 }
202 }
203
204 protected void encodeBody(final FacesContext fc, final DynaForm dynaForm, final List<DynaFormRow> dynaFormRows, final boolean extended,
205 final boolean visible) throws IOException {
206 if (dynaFormRows == null || dynaFormRows.isEmpty()) {
207 return;
208 }
209
210 final ResponseWriter writer = fc.getResponseWriter();
211
212 final String columnClassesValue = dynaForm.getColumnClasses();
213 final String[] columnClasses = columnClassesValue == null ? EMPTY_COLUMN_CLASSES : columnClassesValue.split(",");
214 final String labelCommonClass = columnClasses[0].trim();
215 final String controlCommonClass = columnClasses.length > 1 ? columnClasses[1].trim() : EMPTY_COLUMN_CLASSES[1];
216
217 for (final DynaFormRow dynaFormRow : dynaFormRows) {
218 writer.startElement("tr", null);
219 if (extended) {
220 writer.writeAttribute(Attrs.CLASS, EXTENDED_ROW_CLASS, null);
221 }
222
223 if (!visible) {
224 writer.writeAttribute(Attrs.STYLE, "display:none;", null);
225 }
226
227 writer.writeAttribute("role", "row", null);
228
229 final List<AbstractDynaFormElement> elements = dynaFormRow.getElements();
230 final int size = elements.size();
231
232 for (int i = 0; i < size; i++) {
233 final AbstractDynaFormElement element = elements.get(i);
234
235 writer.startElement("td", null);
236 if (element.getColspan() > 1) {
237 writer.writeAttribute("colspan", element.getColspan(), null);
238 }
239
240 if (element.getRowspan() > 1) {
241 writer.writeAttribute("rowspan", element.getRowspan(), null);
242 }
243
244 String styleClass = CELL_CLASS;
245 if (i == 0 && element.getColspan() == 1) {
246 styleClass = styleClass + " " + CELL_FIRST_CLASS;
247 }
248
249 if (i == size - 1 && element.getColspan() == 1) {
250 styleClass = styleClass + " " + CELL_LAST_CLASS;
251 }
252
253 if (element instanceof DynaFormLabel) {
254 renderLabel(writer, labelCommonClass, (DynaFormLabel) element, styleClass);
255 }
256 else if (element instanceof DynaFormControl) {
257 renderControl(fc, dynaForm, writer, controlCommonClass, (DynaFormControl) element, styleClass);
258 }
259 else if (element instanceof DynaFormModelElement) {
260 renderNestedModel(fc, dynaForm, writer, (DynaFormModelElement) element, styleClass);
261 }
262
263 writer.endElement("td");
264 }
265
266 writer.endElement("tr");
267 }
268
269 dynaForm.resetData();
270 }
271
272 protected void renderNestedModel(
273 final FacesContext fc, final DynaForm dynaForm, final ResponseWriter writer, final DynaFormModelElement element, final String styleClass)
274 throws IOException {
275
276 writer.writeAttribute(Attrs.CLASS, styleClass, null);
277 writer.writeAttribute("role", GRID_CELL_ROLE, null);
278
279 encodeMarkup(fc, dynaForm, element.getModel(), true);
280 }
281
282 protected void renderControl(
283 final FacesContext fc, final DynaForm dynaForm, final ResponseWriter writer, final String controlCommonClass, final DynaFormControl element,
284 String styleClass) throws IOException {
285 dynaForm.setData(element);
286
287
288 final UIDynaFormControl cell = dynaForm.getControlCell(element.getType());
289
290 if (cell.getStyle() != null) {
291 writer.writeAttribute(Attrs.STYLE, cell.getStyle(), null);
292 }
293
294 if (cell.getStyleClass() != null) {
295 styleClass = styleClass + " " + cell.getStyleClass();
296 }
297
298 writer.writeAttribute(Attrs.CLASS, (styleClass + " " + controlCommonClass).trim(), null);
299 writer.writeAttribute("role", GRID_CELL_ROLE, null);
300
301 cell.encodeAll(fc);
302 }
303
304 protected void renderLabel(final ResponseWriter writer, final String labelCommonClass,
305 final DynaFormLabel element, final String styleClass) throws IOException {
306
307 writer.writeAttribute(Attrs.CLASS, (styleClass
308 + " " + LABEL_CLASS
309 + " " + ExtLangUtils.defaultString(element.getStyleClass())
310 + " " + labelCommonClass).trim(), null);
311 writer.writeAttribute("role", GRID_CELL_ROLE, null);
312
313 writer.startElement(Attrs.LABEL, null);
314 if (!element.isTargetValid()) {
315 writer.writeAttribute(Attrs.CLASS, LABEL_INVALID_CLASS, null);
316 }
317
318 writer.writeAttribute("for", element.getTargetClientId(), null);
319
320 if (element.getValue() != null) {
321 if (element.isEscape()) {
322 writer.writeText(element.getValue(), "value");
323 }
324 else {
325 writer.write(element.getValue());
326 }
327 }
328
329 if (element.isTargetRequired()) {
330 writer.startElement("span", null);
331 writer.writeAttribute(Attrs.CLASS, LABEL_INDICATOR_CLASS, null);
332 writer.write("*");
333 writer.endElement("span");
334 }
335
336 writer.endElement(Attrs.LABEL);
337 }
338
339 protected void encodeStatic(final FacesContext fc, final DynaForm dynaForm, final String name, final int totalColspan, final String styleClass)
340 throws IOException {
341 final UIComponent facet = dynaForm.getFacet(name);
342 if (facet == null || !facet.isRendered()) {
343 return;
344 }
345
346 final List<UIComponent> components = facet instanceof Row
347 ? Collections.singletonList(facet)
348 : facet.getChildren();
349
350 final ResponseWriter writer = fc.getResponseWriter();
351 for (final UIComponent child : components) {
352 if (!child.isRendered() || !(child instanceof Row)) {
353 continue;
354 }
355
356 writer.startElement("tr", null);
357 if (shouldWriteId(child)) {
358 writer.writeAttribute("id", child.getClientId(fc), null);
359 }
360 writer.writeAttribute("role", "row", null);
361 writer.writeAttribute(Attrs.CLASS, styleClass, null);
362
363 int i = 0;
364 for (final UIComponent column : child.getChildren()) {
365 if (!column.isRendered()) {
366 continue;
367 }
368
369 String columnClass = CELL_CLASS;
370 if (i % totalColspan == 0) {
371 columnClass = columnClass + " " + CELL_FIRST_CLASS;
372 }
373
374 if ((i + 1) % totalColspan == 0) {
375 columnClass = columnClass + " " + CELL_LAST_CLASS;
376 }
377
378 writer.startElement("td", null);
379 writer.writeAttribute("role", GRID_CELL_ROLE, null);
380 writer.writeAttribute(Attrs.CLASS, columnClass, null);
381 column.encodeAll(fc);
382 writer.endElement("td");
383 i++;
384 }
385
386 writer.endElement("tr");
387 }
388 }
389
390 protected void preRenderLabel(final FacesContext fc, final DynaForm dynaForm, final DynaFormModel model) {
391 for (final DynaFormLabel label : model.getLabels()) {
392
393 final DynaFormControl control = label.getForControl();
394 if (control != null) {
395
396 final UIDynaFormControl cell = dynaForm.getControlCell(control.getType());
397
398 if (cell.getFor() != null) {
399 dynaForm.setData(control);
400
401 final UIComponent target = cell.findComponent(cell.getFor());
402 if (target == null) {
403 LOGGER.warning("Cannot find component with identifier " + cell.getFor() + " inside UIDynaFormControl");
404
405 continue;
406 }
407
408 final String targetClientId = target instanceof InputHolder
409 ? ((InputHolder) target).getInputClientId()
410 : target.getClientId(fc);
411 label.setTargetClientId(targetClientId);
412
413 final ContextCallback callback = (context, target1) -> {
414 label.setTargetValid(((EditableValueHolder) target1).isValid());
415 label.setTargetRequired(((EditableValueHolder) target1).isRequired());
416 };
417
418 if (CompositeUtils.isComposite(target)) {
419 CompositeUtils.invokeOnDeepestEditableValueHolder(fc, target, callback);
420 }
421 else {
422 callback.invokeContextCallback(fc, target);
423 }
424
425 if (label.getValue() != null) {
426 target.getAttributes().put(Attrs.LABEL, label.getValue());
427 }
428
429 label.setStyleClass(String.format(LABEL_CONTROL_TYPE_CLASS_FORMAT, control.getType().toLowerCase()));
430 }
431 }
432 }
433
434 dynaForm.resetData();
435 }
436
437 protected int getTotalColspan(final DynaFormModel dynaFormModel) {
438
439 int totalColspan = -1;
440 for (final DynaFormRow dynaFormRow : dynaFormModel.getRegularRows()) {
441 if (dynaFormRow.getTotalColspan() > totalColspan) {
442 totalColspan = dynaFormRow.getTotalColspan();
443 }
444 }
445
446 if (dynaFormModel.getExtendedRows() != null) {
447 for (final DynaFormRow dynaFormRow : dynaFormModel.getExtendedRows()) {
448 if (dynaFormRow.getTotalColspan() > totalColspan) {
449 totalColspan = dynaFormRow.getTotalColspan();
450 }
451 }
452 }
453
454 if (totalColspan < 1) {
455 totalColspan = 1;
456 }
457
458 return totalColspan;
459 }
460
461 @Override
462 public void encodeChildren(final FacesContext context, final UIComponent component) {
463
464 }
465
466 @Override
467 public boolean getRendersChildren() {
468 return true;
469 }
470 }