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