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.masterdetail;
23
24 import java.util.Map;
25
26 import javax.el.MethodExpression;
27 import javax.el.ValueExpression;
28 import javax.faces.FacesException;
29 import javax.faces.application.ResourceDependency;
30 import javax.faces.component.UIComponent;
31 import javax.faces.component.UIComponentBase;
32 import javax.faces.context.FacesContext;
33 import javax.faces.context.PartialViewContext;
34 import javax.faces.event.ComponentSystemEvent;
35 import javax.faces.event.PostRestoreStateEvent;
36
37 import org.primefaces.component.breadcrumb.BreadCrumb;
38 import org.primefaces.model.menu.DefaultMenuItem;
39 import org.primefaces.model.menu.MenuModel;
40 import org.primefaces.util.Constants;
41 import org.primefaces.util.LangUtils;
42
43
44
45
46
47
48
49
50 @ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.css")
51 public class MasterDetail extends UIComponentBase {
52
53 public static final String CONTEXT_VALUE_VALUE_EXPRESSION = "mdContextValueVE";
54 public static final String SELECTED_LEVEL_VALUE_EXPRESSION = "selectedLevelVE";
55 public static final String SELECTED_STEP_VALUE_EXPRESSION = "selectedStepVE";
56 public static final String PRESERVE_INPUTS_VALUE_EXPRESSION = "preserveInputsVE";
57 public static final String RESET_INPUTS_VALUE_EXPRESSION = "resetInputsVE";
58 public static final String CONTEXT_VALUES = "mdContextValues";
59 public static final String SELECT_DETAIL_REQUEST = "_selectDetailRequest";
60 public static final String CURRENT_LEVEL = "_currentLevel";
61 public static final String SELECTED_LEVEL = "_selectedLevel";
62 public static final String SELECTED_STEP = "_selectedStep";
63 public static final String PRESERVE_INPUTS = "_preserveInputs";
64 public static final String RESET_INPUTS = "_resetInputs";
65 public static final String CURRENT_CONTEXT_VALUE = "_curContextValue";
66 public static final String RESOLVED_CONTEXT_VALUE = "contextValue_";
67 public static final String BREADCRUMB_ID_PREFIX = "_bc";
68
69 public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.MasterDetail";
70 public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
71 private static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.MasterDetailRenderer";
72
73 private MasterDetailLevel detailLevelToProcess;
74 private MasterDetailLevel detailLevelToGo;
75 private int levelPositionToProcess = -1;
76 private int levelCount = -1;
77
78
79
80
81
82
83
84 @SuppressWarnings("java:S115")
85 protected enum PropertyKeys {
86
87 level,
88 contextValue,
89 selectLevelListener,
90 showBreadcrumb,
91 showAllBreadcrumbItems,
92 showBreadcrumbFirstLevel,
93 breadcrumbAboveHeader,
94 style,
95 styleClass
96
97 }
98
99 public MasterDetail() {
100 setRendererType(DEFAULT_RENDERER);
101 }
102
103 @Override
104 public String getFamily() {
105 return COMPONENT_FAMILY;
106 }
107
108 public int getLevel() {
109 return (Integer) getStateHelper().eval(PropertyKeys.level, 1);
110 }
111
112 public void setLevel(final int level) {
113 getStateHelper().put(PropertyKeys.level, level);
114 }
115
116 public Object getContextValue() {
117 return getStateHelper().eval(PropertyKeys.contextValue, null);
118 }
119
120 public void setContextValue(final Object contextValue) {
121 getStateHelper().put(PropertyKeys.contextValue, contextValue);
122 }
123
124 public MethodExpression getSelectLevelListener() {
125 return (MethodExpression) getStateHelper().eval(PropertyKeys.selectLevelListener, null);
126 }
127
128 public void setSelectLevelListener(final MethodExpression selectLevelListener) {
129 getStateHelper().put(PropertyKeys.selectLevelListener, selectLevelListener);
130 }
131
132 public boolean isShowBreadcrumb() {
133 return (Boolean) getStateHelper().eval(PropertyKeys.showBreadcrumb, true);
134 }
135
136 public void setShowBreadcrumb(final boolean showBreadcrumb) {
137 getStateHelper().put(PropertyKeys.showBreadcrumb, showBreadcrumb);
138 }
139
140 public boolean isShowAllBreadcrumbItems() {
141 return (Boolean) getStateHelper().eval(PropertyKeys.showAllBreadcrumbItems, false);
142 }
143
144 public void setShowBreadcrumbFirstLevel(final boolean showBreadcrumbFirstLevel) {
145 getStateHelper().put(PropertyKeys.showBreadcrumbFirstLevel, showBreadcrumbFirstLevel);
146 }
147
148 public boolean isShowBreadcrumbFirstLevel() {
149 return (Boolean) getStateHelper().eval(PropertyKeys.showBreadcrumbFirstLevel, true);
150 }
151
152 public void setShowAllBreadcrumbItems(final boolean showAllBreadcrumbItems) {
153 getStateHelper().put(PropertyKeys.showAllBreadcrumbItems, showAllBreadcrumbItems);
154 }
155
156 public boolean isBreadcrumbAboveHeader() {
157 return (Boolean) getStateHelper().eval(PropertyKeys.breadcrumbAboveHeader, true);
158 }
159
160 public void setBreadcrumbAboveHeader(final boolean breadcrumbAboveHeader) {
161 getStateHelper().put(PropertyKeys.breadcrumbAboveHeader, breadcrumbAboveHeader);
162 }
163
164 public String getStyle() {
165 return (String) getStateHelper().eval(PropertyKeys.style, null);
166 }
167
168 public void setStyle(final String style) {
169 getStateHelper().put(PropertyKeys.style, style);
170 }
171
172 public String getStyleClass() {
173 return (String) getStateHelper().eval(PropertyKeys.styleClass, null);
174 }
175
176 public void setStyleClass(final String styleClass) {
177 getStateHelper().put(PropertyKeys.styleClass, styleClass);
178 }
179
180 @Override
181 public void processEvent(final ComponentSystemEvent event) {
182 super.processEvent(event);
183
184 final FacesContext fc = FacesContext.getCurrentInstance();
185 if (!(event instanceof PostRestoreStateEvent) || !isSelectDetailRequest(fc)) {
186 return;
187 }
188
189 final PartialViewContext pvc = fc.getPartialViewContext();
190 if (pvc.getRenderIds().isEmpty()) {
191
192 pvc.getRenderIds().add(getClientId(fc));
193 }
194
195 final MasterDetailLevel mdl = getDetailLevelToProcess(fc);
196 final Object contextValue = getContextValueFromFlow(fc, mdl, true);
197 final String contextVar = mdl.getContextVar();
198 if (LangUtils.isNotBlank(contextVar) && contextValue != null) {
199 final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
200 requestMap.put(contextVar, contextValue);
201 }
202 }
203
204 @Override
205 public void processDecodes(final FacesContext fc) {
206 if (!isSelectDetailRequest(fc)) {
207 super.processDecodes(fc);
208 }
209 else {
210 getDetailLevelToProcess(fc).processDecodes(fc);
211 }
212 }
213
214 @Override
215 public void processValidators(final FacesContext fc) {
216 if (!isSelectDetailRequest(fc)) {
217 super.processValidators(fc);
218 }
219 else {
220 getDetailLevelToProcess(fc).processValidators(fc);
221 }
222 }
223
224 @Override
225 public void processUpdates(final FacesContext fc) {
226 if (!isSelectDetailRequest(fc)) {
227 super.processUpdates(fc);
228 }
229 else {
230 getDetailLevelToProcess(fc).processUpdates(fc);
231 }
232 }
233
234 public MasterDetailLevel getDetailLevelToProcess(final FacesContext fc) {
235 if (detailLevelToProcess == null) {
236 initDataForLevels(fc);
237 }
238
239 return detailLevelToProcess;
240 }
241
242 public MasterDetailLevel getDetailLevelToGo(final FacesContext fc) {
243 if (detailLevelToGo != null) {
244 return detailLevelToGo;
245 }
246
247 final String strSelectedLevel = fc.getExternalContext().getRequestParameterMap()
248 .get(getClientId(fc) + SELECTED_LEVEL);
249 final String strSelectedStep = fc.getExternalContext().getRequestParameterMap()
250 .get(getClientId(fc) + SELECTED_STEP);
251
252
253 if (strSelectedLevel != null) {
254 final int selectedLevel = Integer.parseInt(strSelectedLevel);
255 detailLevelToGo = getDetailLevelByLevel(selectedLevel);
256 if (detailLevelToGo != null) {
257 return detailLevelToGo;
258 }
259
260 throw new FacesException("MasterDetailLevel for selected level = " + selectedLevel + " not found.");
261 }
262
263 final int step;
264 if (strSelectedStep != null) {
265
266 step = Integer.parseInt(strSelectedStep);
267 }
268 else {
269
270 step = 1;
271 }
272
273 detailLevelToGo = getDetailLevelByStep(step);
274
275 return detailLevelToGo;
276 }
277
278 public MasterDetailLevel getDetailLevelByLevel(final int level) {
279 for (final UIComponent child : getChildren()) {
280 if (child instanceof MasterDetailLevel) {
281 final MasterDetailLevel mdl = (MasterDetailLevel) child;
282 if (mdl.getLevel() == level) {
283 return mdl;
284 }
285 }
286 }
287
288 return null;
289 }
290
291 public boolean isSelectDetailRequest(final FacesContext fc) {
292 return fc.getPartialViewContext().isAjaxRequest()
293 && fc.getExternalContext().getRequestParameterMap()
294 .containsKey(getClientId(fc) + SELECT_DETAIL_REQUEST);
295 }
296
297 public String getPreserveInputs(final FacesContext fc) {
298 return fc.getExternalContext().getRequestParameterMap().get(getClientId(fc) + PRESERVE_INPUTS);
299 }
300
301 public String getResetInputs(final FacesContext fc) {
302 return fc.getExternalContext().getRequestParameterMap().get(getClientId(fc) + RESET_INPUTS);
303 }
304
305 public void updateModel(final FacesContext fc, final MasterDetailLevel mdlToGo) {
306 final int levelToGo = mdlToGo.getLevel();
307 final ValueExpression levelVE = getValueExpression(PropertyKeys.level.toString());
308 if (levelVE != null) {
309
310 levelVE.setValue(fc.getELContext(), levelToGo);
311 getStateHelper().remove(PropertyKeys.level);
312 }
313
314
315 final String source = fc.getExternalContext().getRequestParameterMap()
316 .get(Constants.RequestParams.PARTIAL_SOURCE_PARAM);
317 final MasterDetailLevel mdl = getDetailLevelToProcess(fc);
318
319
320 Object contextValue = null;
321 final Map<String, Object> contextValues = (Map<String, Object>) mdl.getAttributes().get(CONTEXT_VALUES);
322 if (contextValues != null) {
323 contextValue = contextValues.get(RESOLVED_CONTEXT_VALUE + source);
324 }
325
326 if (contextValue != null) {
327
328 mdlToGo.getAttributes().put(getClientId(fc) + CURRENT_CONTEXT_VALUE, contextValue);
329
330 final ValueExpression contextValueVE = getValueExpression(PropertyKeys.contextValue.toString());
331 if (contextValueVE != null) {
332
333 contextValueVE.setValue(fc.getELContext(), contextValue);
334 getStateHelper().remove(PropertyKeys.contextValue);
335 }
336 }
337 }
338
339 public Object getContextValueFromFlow(final FacesContext fc, final MasterDetailLevel mdl,
340 final boolean includeModel) {
341
342 final Object contextValue = mdl.getAttributes().get(getClientId(fc) + MasterDetail.CURRENT_CONTEXT_VALUE);
343 if (contextValue != null) {
344 return contextValue;
345 }
346
347
348 if (includeModel) {
349 return getContextValue();
350 }
351
352 return null;
353 }
354
355 public BreadCrumb getBreadcrumb() {
356 BreadCrumb breadCrumb = null;
357 for (final UIComponent child : getChildren()) {
358 if (child instanceof BreadCrumb) {
359 breadCrumb = (BreadCrumb) child;
360
361 break;
362 }
363 }
364
365 final MenuModel model = breadCrumb != null ? breadCrumb.getModel() : null;
366
367 if (model != null && model.getElements().isEmpty()) {
368 final String clientId = getClientId();
369 final String menuItemIdPrefix = getId() + "_bcItem_";
370
371 for (final UIComponent child : getChildren()) {
372 if (child instanceof MasterDetailLevel) {
373 final int level = ((MasterDetailLevel) child).getLevel();
374
375
376 final DefaultMenuItem menuItem = new DefaultMenuItem();
377 menuItem.setId(menuItemIdPrefix + level);
378 menuItem.setAjax(true);
379 menuItem.setImmediate(true);
380 menuItem.setProcess("@none");
381 menuItem.setUpdate("@parent");
382
383
384 menuItem.setParam(clientId + MasterDetail.SELECT_DETAIL_REQUEST, true);
385 menuItem.setParam(clientId + MasterDetail.CURRENT_LEVEL, -1);
386 menuItem.setParam(clientId + MasterDetail.SELECTED_LEVEL, level);
387
388 model.getElements().add(menuItem);
389 }
390 }
391 }
392
393 return breadCrumb;
394 }
395
396 public void resetCalculatedValues() {
397 detailLevelToProcess = null;
398 detailLevelToGo = null;
399 levelPositionToProcess = -1;
400 levelCount = -1;
401 }
402
403 private void initDataForLevels(final FacesContext fc) {
404 final String strCurrentLevel = fc.getExternalContext().getRequestParameterMap()
405 .get(getClientId(fc) + CURRENT_LEVEL);
406 if (strCurrentLevel == null) {
407 throw new FacesException("Current level is missing in request.");
408 }
409
410 final int currentLevel = Integer.parseInt(strCurrentLevel);
411 int count = 0;
412
413 for (final UIComponent child : getChildren()) {
414 if (child instanceof MasterDetailLevel) {
415 final MasterDetailLevel mdl = (MasterDetailLevel) child;
416 count++;
417
418 if (detailLevelToProcess == null && mdl.getLevel() == currentLevel) {
419 detailLevelToProcess = mdl;
420 levelPositionToProcess = count;
421 }
422 }
423 }
424
425 levelCount = count;
426
427 if (detailLevelToProcess == null) {
428 throw new FacesException("Current MasterDetailLevel to process not found.");
429 }
430 }
431
432 private MasterDetailLevel getDetailLevelByStep(final int step) {
433 int levelPositionToGo = getLevelPositionToProcess() + step;
434 if (levelPositionToGo < 1) {
435 levelPositionToGo = 1;
436 }
437 else if (levelPositionToGo > getLevelCount()) {
438 levelPositionToGo = getLevelCount();
439 }
440
441 int pos = 0;
442 for (final UIComponent child : getChildren()) {
443 if (child instanceof MasterDetailLevel) {
444 final MasterDetailLevel mdl = (MasterDetailLevel) child;
445 pos++;
446
447 if (pos == levelPositionToGo) {
448 return mdl;
449 }
450 }
451 }
452
453
454 return null;
455 }
456
457 private int getLevelPositionToProcess() {
458 if (levelPositionToProcess == -1) {
459 initDataForLevels(FacesContext.getCurrentInstance());
460 }
461
462 return levelPositionToProcess;
463 }
464
465 private int getLevelCount() {
466 if (levelCount == -1) {
467 initDataForLevels(FacesContext.getCurrentInstance());
468 }
469
470 return levelCount;
471 }
472
473 @Override
474 public Object saveState(final FacesContext context) {
475
476 detailLevelToGo = null;
477 detailLevelToProcess = null;
478 levelCount = -1;
479 levelPositionToProcess = -1;
480
481 return super.saveState(context);
482 }
483 }