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.base;
23
24 import java.util.Map;
25
26 import javax.faces.FacesException;
27 import javax.faces.application.Application;
28 import javax.faces.application.FacesMessage;
29 import javax.faces.component.ContextCallback;
30 import javax.faces.component.EditableValueHolder;
31 import javax.faces.component.NamingContainer;
32 import javax.faces.component.UIComponent;
33 import javax.faces.component.UIComponentBase;
34 import javax.faces.component.UINamingContainer;
35 import javax.faces.component.UIViewRoot;
36 import javax.faces.component.UniqueIdVendor;
37 import javax.faces.component.visit.VisitCallback;
38 import javax.faces.component.visit.VisitContext;
39 import javax.faces.component.visit.VisitResult;
40 import javax.faces.context.FacesContext;
41 import javax.faces.event.FacesEvent;
42 import javax.faces.event.PhaseId;
43 import javax.faces.event.PostValidateEvent;
44 import javax.faces.event.PreValidateEvent;
45 import javax.faces.render.Renderer;
46
47 import org.primefaces.component.api.UITabPanel;
48 import org.primefaces.extensions.event.EventDataWrapper;
49 import org.primefaces.extensions.model.common.KeyData;
50 import org.primefaces.extensions.util.Attrs;
51 import org.primefaces.extensions.util.SavedEditableValueState;
52 import org.primefaces.util.ComponentTraversalUtils;
53
54
55
56
57
58
59
60
61 public abstract class AbstractDynamicData extends UIComponentBase implements NamingContainer, UniqueIdVendor {
62
63 protected KeyData data;
64 private String clientId = null;
65 private final StringBuilder idBuilder = new StringBuilder();
66 private Boolean isNested = null;
67
68
69
70
71
72
73
74 @SuppressWarnings("java:S115")
75 protected enum PropertyKeys {
76
77
78 saved,
79 lastId,
80 var,
81 varContainerId,
82 value;
83
84
85 private final String toString;
86
87 PropertyKeys() {
88 toString = null;
89 }
90
91 @Override
92 public String toString() {
93 return toString != null ? toString : super.toString();
94 }
95 }
96
97 public String getVar() {
98 return (String) getStateHelper().get(PropertyKeys.var);
99 }
100
101 public void setVar(final String var) {
102 getStateHelper().put(PropertyKeys.var, var);
103 }
104
105 public String getVarContainerId() {
106 return (String) getStateHelper().get(PropertyKeys.varContainerId);
107 }
108
109 public void setVarContainerId(final String varContainerId) {
110 getStateHelper().put(PropertyKeys.varContainerId, varContainerId);
111 }
112
113 public Object getValue() {
114 return getStateHelper().eval(PropertyKeys.value, null);
115 }
116
117 public void setValue(final Object value) {
118 getStateHelper().put(PropertyKeys.value, value);
119 }
120
121
122
123
124
125
126
127 protected abstract KeyData findData(String key);
128
129
130
131
132
133
134
135 protected abstract void processChildren(FacesContext context, PhaseId phaseId);
136
137
138
139
140
141
142
143
144
145 protected abstract boolean visitChildren(VisitContext context, VisitCallback callback);
146
147
148
149
150
151
152
153
154
155 protected abstract boolean invokeOnChildren(FacesContext context, String clientId, ContextCallback callback);
156
157 public void setData(final String key) {
158 saveDescendantState();
159
160 data = findData(key);
161 exposeVar();
162
163 restoreDescendantState();
164 }
165
166 public void setData(final KeyData keyData) {
167 saveDescendantState();
168
169 data = keyData;
170 exposeVar();
171
172 restoreDescendantState();
173 }
174
175 public void resetData() {
176 saveDescendantState();
177
178 data = null;
179 exposeVar();
180 }
181
182 public KeyData getData() {
183 return data;
184 }
185
186 @Override
187 public String getClientId(final FacesContext context) {
188 if (clientId != null) {
189 return clientId;
190 }
191
192 String id = getId();
193 if (id == null) {
194 final UniqueIdVendor parentUniqueIdVendor = ComponentTraversalUtils.closestUniqueIdVendor(this);
195
196 if (parentUniqueIdVendor == null) {
197 final UIViewRoot viewRoot = context.getViewRoot();
198
199 if (viewRoot != null) {
200 id = viewRoot.createUniqueId(context, null);
201 }
202 else {
203 throw new FacesException("Cannot create clientId for " + this.getClass().getCanonicalName());
204 }
205 }
206 else {
207 id = parentUniqueIdVendor.createUniqueId(context, null);
208 }
209
210 setId(id);
211 }
212
213 final UIComponent namingContainer = ComponentTraversalUtils.closestNamingContainer(this);
214 if (namingContainer != null) {
215 final String containerClientId = namingContainer.getContainerClientId(context);
216
217 if (containerClientId != null) {
218 clientId = idBuilder.append(containerClientId).append(UINamingContainer.getSeparatorChar(context))
219 .append(id)
220 .toString();
221 idBuilder.setLength(0);
222 }
223 else {
224 clientId = id;
225 }
226 }
227 else {
228 clientId = id;
229 }
230
231 final Renderer renderer = getRenderer(context);
232 if (renderer != null) {
233 clientId = renderer.convertClientId(context, clientId);
234 }
235
236 return clientId;
237 }
238
239 @Override
240 public void setId(final String id) {
241 super.setId(id);
242
243 clientId = null;
244 }
245
246 @Override
247 public String getContainerClientId(final FacesContext context) {
248 final String id = this.getClientId(context);
249
250 final KeyData keyData = getData();
251 final String key = keyData != null ? keyData.getKey() : null;
252
253 if (key == null) {
254 return id;
255 }
256 else {
257 final String containerClientId = idBuilder.append(id).append(UINamingContainer.getSeparatorChar(context))
258 .append(key).toString();
259 idBuilder.setLength(0);
260
261 return containerClientId;
262 }
263 }
264
265 @Override
266 public void processDecodes(final FacesContext context) {
267 if (!isRendered()) {
268 return;
269 }
270
271 pushComponentToEL(context, this);
272 preDecode(context);
273 processFacets(context, PhaseId.APPLY_REQUEST_VALUES, this);
274 processChildren(context, PhaseId.APPLY_REQUEST_VALUES);
275
276 try {
277 decode(context);
278 }
279 catch (final RuntimeException e) {
280 context.renderResponse();
281 throw e;
282 }
283 finally {
284 popComponentFromEL(context);
285 }
286 }
287
288 @Override
289 public void processValidators(final FacesContext context) {
290 if (!isRendered()) {
291 return;
292 }
293
294 pushComponentToEL(context, this);
295
296 final Application app = context.getApplication();
297 app.publishEvent(context, PreValidateEvent.class, this);
298
299 processFacets(context, PhaseId.PROCESS_VALIDATIONS, this);
300 processChildren(context, PhaseId.PROCESS_VALIDATIONS);
301
302 app.publishEvent(context, PostValidateEvent.class, this);
303 popComponentFromEL(context);
304 }
305
306 @Override
307 public void processUpdates(final FacesContext context) {
308 if (!isRendered()) {
309 return;
310 }
311
312 pushComponentToEL(context, this);
313 processFacets(context, PhaseId.UPDATE_MODEL_VALUES, this);
314 processChildren(context, PhaseId.UPDATE_MODEL_VALUES);
315 popComponentFromEL(context);
316 }
317
318 protected void preDecode(final FacesContext context) {
319 final Map<String, SavedEditableValueState> saved = (Map<String, SavedEditableValueState>) getStateHelper()
320 .get(PropertyKeys.saved);
321 if (null == saved) {
322 getStateHelper().remove(PropertyKeys.saved);
323 }
324 else if (!keepSaved(context)) {
325 for (final SavedEditableValueState saveState : saved.values()) {
326 saveState.reset();
327 }
328 }
329 }
330
331 private boolean keepSaved(final FacesContext context) {
332 return contextHasErrorMessages(context) || isNestedWithinIterator();
333 }
334
335 private boolean contextHasErrorMessages(final FacesContext context) {
336 final FacesMessage.Severity sev = context.getMaximumSeverity();
337 return sev != null && FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0;
338 }
339
340 protected Boolean isNestedWithinIterator() {
341 if (isNested == null) {
342 UIComponent parent = this;
343 while (null != (parent = parent.getParent())) {
344 if (parent instanceof javax.faces.component.UIData ||
345 parent.getClass().getName().endsWith("UIRepeat") ||
346 parent instanceof UITabPanel && ((UITabPanel) parent).isRepeating() ||
347 parent instanceof AbstractDynamicData) {
348 isNested = Boolean.TRUE;
349 break;
350 }
351 }
352 if (isNested == null) {
353 isNested = Boolean.FALSE;
354 }
355 }
356 return isNested;
357 }
358
359 @Override
360 public void queueEvent(final FacesEvent event) {
361 super.queueEvent(new EventDataWrapper(this, event, getData()));
362 }
363
364 @Override
365 public void broadcast(final FacesEvent event) {
366 if (!(event instanceof EventDataWrapper)) {
367 super.broadcast(event);
368
369 return;
370 }
371
372 final FacesContext context = FacesContext.getCurrentInstance();
373 final KeyData oldData = getData();
374 final EventDataWrapper eventDataWrapper = (EventDataWrapper) event;
375 final FacesEvent originalEvent = eventDataWrapper.getFacesEvent();
376 final UIComponent originalSource = (UIComponent) originalEvent.getSource();
377 setData(eventDataWrapper.getData());
378
379 UIComponent compositeParent = null;
380 try {
381 if (!UIComponent.isCompositeComponent(originalSource)) {
382 compositeParent = getCompositeComponentParent(originalSource);
383 }
384
385 if (compositeParent != null) {
386 compositeParent.pushComponentToEL(context, null);
387 }
388
389 originalSource.pushComponentToEL(context, null);
390 originalSource.broadcast(originalEvent);
391 }
392 finally {
393 originalSource.popComponentFromEL(context);
394 if (compositeParent != null) {
395 compositeParent.popComponentFromEL(context);
396 }
397 }
398
399 setData(oldData);
400 }
401
402 @Override
403 public boolean visitTree(final VisitContext context, final VisitCallback callback) {
404 if (!isVisitable(context)) {
405 return false;
406 }
407
408 final FacesContext fc = context.getFacesContext();
409 final KeyData oldData = getData();
410 resetData();
411
412 pushComponentToEL(fc, null);
413
414 try {
415 final VisitResult result = context.invokeVisitCallback(this, callback);
416
417 if (result == VisitResult.COMPLETE) {
418 return true;
419 }
420
421 if (result == VisitResult.ACCEPT && !context.getSubtreeIdsToVisit(this).isEmpty()) {
422 if (getFacetCount() > 0) {
423 for (final UIComponent facet : getFacets().values()) {
424 if (facet.visitTree(context, callback)) {
425 return true;
426 }
427 }
428 }
429
430 if (visitChildren(context, callback)) {
431 return true;
432 }
433 }
434 }
435 finally {
436 popComponentFromEL(fc);
437 setData(oldData);
438 }
439
440 return false;
441 }
442
443 @Override
444 public boolean invokeOnComponent(final FacesContext context, final String clientId, final ContextCallback callback) {
445 final KeyData oldData = getData();
446 resetData();
447
448 try {
449 if (clientId.equals(super.getClientId(context))) {
450 pushComponentToEL(context, getCompositeComponentParent(this));
451 callback.invokeContextCallback(context, this);
452
453 return true;
454 }
455
456 if (getFacetCount() > 0) {
457 for (final UIComponent c : getFacets().values()) {
458 if (clientId.equals(c.getClientId(context))) {
459 callback.invokeContextCallback(context, c);
460
461 return true;
462 }
463 }
464 }
465
466
467 if (!clientId.startsWith(this.getClientId(context))) {
468 return false;
469 }
470
471 return invokeOnChildren(context, clientId, callback);
472 }
473 catch (final FacesException fe) {
474 throw fe;
475 }
476 catch (final Exception e) {
477 throw new FacesException(e);
478 }
479 finally {
480 popComponentFromEL(context);
481 setData(oldData);
482 }
483 }
484
485 protected void processFacets(final FacesContext context, final PhaseId phaseId, final UIComponent component) {
486 resetData();
487
488 if (component.getFacetCount() > 0) {
489 for (final UIComponent facet : component.getFacets().values()) {
490 if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
491 facet.processDecodes(context);
492 }
493 else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
494 facet.processValidators(context);
495 }
496 else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
497 facet.processUpdates(context);
498 }
499 else {
500 throw new IllegalArgumentException();
501 }
502 }
503 }
504 }
505
506 @Override
507 public String createUniqueId(final FacesContext context, final String seed) {
508 final Integer i = (Integer) getStateHelper().get(PropertyKeys.lastId);
509 int lastId = i != null ? i : 0;
510 getStateHelper().put(PropertyKeys.lastId, ++lastId);
511
512 return UIViewRoot.UNIQUE_ID_PREFIX + (seed == null ? lastId : seed);
513 }
514
515 protected void exposeVar() {
516 final FacesContext fc = FacesContext.getCurrentInstance();
517 final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
518
519 final String var = getVar();
520 if (var != null) {
521 final KeyData keyData = getData();
522 if (keyData == null) {
523 requestMap.remove(var);
524 }
525 else {
526 requestMap.put(var, keyData.getData());
527 }
528 }
529
530 final String varContainerId = getVarContainerId();
531 if (varContainerId != null) {
532 final String containerClientId = getContainerClientId(fc);
533 if (containerClientId == null) {
534 requestMap.remove(varContainerId);
535 }
536 else {
537 requestMap.put(varContainerId, containerClientId);
538 }
539 }
540 }
541
542 protected void saveDescendantState() {
543 for (final UIComponent child : getChildren()) {
544 saveDescendantState(FacesContext.getCurrentInstance(), child);
545 }
546 }
547
548 protected void saveDescendantState(final FacesContext context, final UIComponent component) {
549
550 component.setId(component.getId());
551
552 final Map<String, SavedEditableValueState> saved = (Map<String, SavedEditableValueState>) getStateHelper()
553 .get(PropertyKeys.saved);
554
555 if (component instanceof EditableValueHolder) {
556 final EditableValueHolder input = (EditableValueHolder) component;
557 SavedEditableValueState state = null;
558 final String id = component.getClientId(context);
559
560 if (saved == null) {
561 state = new SavedEditableValueState();
562 getStateHelper().put(PropertyKeys.saved, id, state);
563 }
564
565 if (state == null) {
566 state = saved.get(clientId);
567
568 if (state == null) {
569 state = new SavedEditableValueState();
570 getStateHelper().put(PropertyKeys.saved, id, state);
571 }
572 }
573
574 state.setValue(input.getLocalValue());
575 state.setValid(input.isValid());
576 state.setSubmittedValue(input.getSubmittedValue());
577 state.setLocalValueSet(input.isLocalValueSet());
578 state.setLabelValue(((UIComponent) input).getAttributes().get(Attrs.LABEL));
579
580
581
582
583
584
585 }
586
587 for (final UIComponent child : component.getChildren()) {
588 saveDescendantState(context, child);
589 }
590
591 if (component.getFacetCount() > 0) {
592 for (final UIComponent facet : component.getFacets().values()) {
593 saveDescendantState(context, facet);
594 }
595 }
596 }
597
598 protected void restoreDescendantState() {
599 for (final UIComponent child : getChildren()) {
600 restoreDescendantState(FacesContext.getCurrentInstance(), child);
601 }
602 }
603
604 protected void restoreDescendantState(final FacesContext context, final UIComponent component) {
605
606 component.setId(component.getId());
607
608 final Map<String, SavedEditableValueState> saved = (Map<String, SavedEditableValueState>) getStateHelper()
609 .get(PropertyKeys.saved);
610
611 if (component instanceof EditableValueHolder) {
612 final EditableValueHolder input = (EditableValueHolder) component;
613 final String id = component.getClientId(context);
614
615 SavedEditableValueState state = saved.get(id);
616 if (state == null) {
617 state = new SavedEditableValueState();
618 }
619
620 input.setValue(state.getValue());
621 input.setValid(state.isValid());
622 input.setSubmittedValue(state.getSubmittedValue());
623 input.setLocalValueSet(state.isLocalValueSet());
624 if (state.getLabelValue() != null) {
625 ((UIComponent) input).getAttributes().put(Attrs.LABEL, state.getLabelValue());
626 }
627
628
629
630
631
632
633 }
634
635 for (final UIComponent child : component.getChildren()) {
636 restoreDescendantState(context, child);
637 }
638
639 if (component.getFacetCount() > 0) {
640 for (final UIComponent facet : component.getFacets().values()) {
641 restoreDescendantState(context, facet);
642 }
643 }
644 }
645
646 @Override
647 public Object saveState(FacesContext context) {
648
649 data = null;
650 clientId = null;
651 isNested = null;
652
653 return super.saveState(context);
654 }
655 }