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.io.IOException;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.faces.FacesException;
29 import javax.faces.component.EditableValueHolder;
30 import javax.faces.component.UIComponent;
31 import javax.faces.component.visit.VisitContext;
32 import javax.faces.context.FacesContext;
33 import javax.faces.context.ResponseWriter;
34 import javax.faces.convert.Converter;
35 import javax.faces.render.Renderer;
36
37 import org.primefaces.component.breadcrumb.BreadCrumb;
38 import org.primefaces.extensions.util.Attrs;
39 import org.primefaces.extensions.util.ExtLangUtils;
40 import org.primefaces.model.menu.DefaultMenuItem;
41 import org.primefaces.model.menu.MenuElement;
42 import org.primefaces.model.menu.MenuItem;
43 import org.primefaces.renderkit.CoreRenderer;
44 import org.primefaces.util.ComponentUtils;
45 import org.primefaces.util.FacetUtils;
46 import org.primefaces.util.FastStringWriter;
47 import org.primefaces.util.LangUtils;
48
49
50
51
52
53
54
55
56 public class MasterDetailRenderer extends CoreRenderer {
57
58 private static final String FACET_HEADER = "header";
59 private static final String FACET_FOOTER = "footer";
60 private static final String FACET_LABEL = Attrs.LABEL;
61
62 @Override
63 public void encodeEnd(final FacesContext fc, final UIComponent component) throws IOException {
64 final MasterDetail masterDetail = (MasterDetail) component;
65 final MasterDetailLevel mdl;
66
67 if (masterDetail.isSelectDetailRequest(fc)) {
68
69 final MasterDetailLevel mdlToProcess = masterDetail.getDetailLevelToProcess(fc);
70
71 if (fc.isValidationFailed()) {
72 mdl = mdlToProcess;
73 }
74 else {
75 mdl = getDetailLevelToEncode(fc, masterDetail, mdlToProcess, masterDetail.getDetailLevelToGo(fc));
76
77
78 final MasterDetailLevelVisitCallback visitCallback = new MasterDetailLevelVisitCallback();
79 mdlToProcess.visitTree(VisitContext.createVisitContext(fc), visitCallback);
80
81 final String preserveInputs = masterDetail.getPreserveInputs(fc);
82 final String resetInputs = masterDetail.getResetInputs(fc);
83 final String[] piIds = preserveInputs != null ? preserveInputs.split("[\\s,]+") : null;
84 final String[] riIds = resetInputs != null ? resetInputs.split("[\\s,]+") : null;
85 final boolean preserveAll = ExtLangUtils.contains(piIds, "@all");
86 final boolean resetAll = ExtLangUtils.contains(riIds, "@all");
87
88 final List<EditableValueHolder> editableValueHolders = visitCallback.getEditableValueHolders();
89 for (final EditableValueHolder editableValueHolder : editableValueHolders) {
90 final String clientId = ((UIComponent) editableValueHolder).getClientId(fc);
91 if (resetAll || ExtLangUtils.contains(riIds, clientId)) {
92 editableValueHolder.resetValue();
93 }
94 else if (preserveAll || ExtLangUtils.contains(piIds, clientId)) {
95 editableValueHolder.setValue(getConvertedSubmittedValue(fc, editableValueHolder));
96 }
97 else {
98
99 editableValueHolder.resetValue();
100 }
101 }
102 }
103
104 masterDetail.updateModel(fc, mdl);
105 }
106 else {
107
108 mdl = masterDetail.getDetailLevelByLevel(masterDetail.getLevel());
109 }
110
111 if (mdl == null) {
112 throw new FacesException(
113 "MasterDetailLevel [Level=" + masterDetail.getLevel() +
114 "] must be nested inside a MasterDetail component!");
115 }
116
117
118 encodeMarkup(fc, masterDetail, mdl);
119
120
121 masterDetail.resetCalculatedValues();
122 }
123
124 protected MasterDetailLevel getDetailLevelToEncode(final FacesContext fc, final MasterDetail masterDetail,
125 final MasterDetailLevel mdlToProcess,
126 final MasterDetailLevel mdlToGo) {
127 if (masterDetail.getSelectLevelListener() != null) {
128 final SelectLevelEvent selectLevelEvent = new SelectLevelEvent(masterDetail, mdlToProcess.getLevel(),
129 mdlToGo.getLevel());
130 final int levelToEncode = (Integer) masterDetail.getSelectLevelListener()
131 .invoke(fc.getELContext(), new Object[] {selectLevelEvent});
132 if (levelToEncode != mdlToGo.getLevel()) {
133
134 return masterDetail.getDetailLevelByLevel(levelToEncode);
135 }
136 }
137
138 return mdlToGo;
139 }
140
141 protected void encodeMarkup(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl)
142 throws IOException {
143 if (mdl == null) {
144 throw new FacesException("MasterDetailLevel must be nested inside a MasterDetail component!");
145 }
146 final ResponseWriter writer = fc.getResponseWriter();
147 final String clientId = masterDetail.getClientId(fc);
148 final String styleClass = masterDetail.getStyleClass() == null ? "pe-master-detail" : "pe-master-detail " + masterDetail.getStyleClass();
149
150 writer.startElement("div", masterDetail);
151 writer.writeAttribute("id", clientId, "id");
152 writer.writeAttribute(Attrs.CLASS, styleClass, "styleClass");
153 if (masterDetail.getStyle() != null) {
154 writer.writeAttribute(Attrs.STYLE, masterDetail.getStyle(), Attrs.STYLE);
155 }
156
157 if (masterDetail.isShowBreadcrumb()) {
158 if (masterDetail.isBreadcrumbAboveHeader()) {
159
160 renderBreadcrumb(fc, masterDetail, mdl);
161 encodeFacet(fc, masterDetail, FACET_HEADER);
162 }
163 else {
164
165 encodeFacet(fc, masterDetail, FACET_HEADER);
166 renderBreadcrumb(fc, masterDetail, mdl);
167 }
168 }
169 else {
170
171 encodeFacet(fc, masterDetail, FACET_HEADER);
172 }
173
174
175 writer.startElement("div", null);
176 writer.writeAttribute("id", clientId + "_detaillevel", "id");
177 writer.writeAttribute(Attrs.CLASS, "pe-master-detail-level", null);
178
179
180 Object contextValue = null;
181 final String contextVar = mdl.getContextVar();
182 if (LangUtils.isNotBlank(contextVar)) {
183 contextValue = masterDetail.getContextValueFromFlow(fc, mdl, true);
184 }
185
186 if (contextValue != null) {
187 final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
188 requestMap.put(contextVar, contextValue);
189 }
190
191
192 mdl.encodeAll(fc);
193
194 if (contextValue != null) {
195 fc.getExternalContext().getRequestMap().remove(contextVar);
196 }
197
198 writer.endElement("div");
199
200
201 encodeFacet(fc, masterDetail, FACET_FOOTER);
202 writer.endElement("div");
203 }
204
205 protected void renderBreadcrumb(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl)
206 throws IOException {
207
208 final BreadCrumb breadcrumb = masterDetail.getBreadcrumb();
209
210
211 updateBreadcrumb(fc, breadcrumb, masterDetail, mdl);
212
213 if (!masterDetail.isShowBreadcrumbFirstLevel()) {
214 final int levelToRender = mdl.getLevel();
215 if (levelToRender == 1) {
216 breadcrumb.setStyleClass("ui-helper-hidden");
217 }
218 else {
219 breadcrumb.setStyleClass("");
220 }
221 }
222
223
224 breadcrumb.encodeAll(fc);
225 }
226
227 protected void encodeFacet(final FacesContext fc, final UIComponent component, final String name)
228 throws IOException {
229 final UIComponent facet = component.getFacet(name);
230 if (FacetUtils.shouldRenderFacet(facet)) {
231 facet.encodeAll(fc);
232 }
233 }
234
235 protected void updateBreadcrumb(final FacesContext fc, final BreadCrumb breadcrumb, final MasterDetail masterDetail,
236 final MasterDetailLevel mdlToRender) throws IOException {
237 boolean lastMdlFound = false;
238 final int levelToRender = mdlToRender.getLevel();
239 final boolean isShowAllBreadcrumbItems = masterDetail.isShowAllBreadcrumbItems();
240
241 for (final UIComponent child : masterDetail.getChildren()) {
242 if (child instanceof MasterDetailLevel) {
243 final MasterDetailLevel mdl = (MasterDetailLevel) child;
244 final DefaultMenuItem menuItem = getMenuItemByLevel(breadcrumb, masterDetail, mdl);
245 if (menuItem == null) {
246
247 return;
248 }
249
250 if (!child.isRendered()) {
251 menuItem.setRendered(false);
252 if (!lastMdlFound) {
253 lastMdlFound = mdl.getLevel() == mdlToRender.getLevel();
254 }
255
256 continue;
257 }
258
259 if (lastMdlFound && !isShowAllBreadcrumbItems) {
260 menuItem.setRendered(false);
261 }
262 else {
263 menuItem.setRendered(true);
264
265 final Object contextValue = masterDetail.getContextValueFromFlow(fc, mdl,
266 mdl.getLevel() == mdlToRender.getLevel());
267 final String contextVar = mdl.getContextVar();
268 final boolean putContext = LangUtils.isNotBlank(contextVar) && contextValue != null;
269
270 if (putContext) {
271 final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
272 requestMap.put(contextVar, contextValue);
273 }
274
275 final UIComponent facet = mdl.getFacet(FACET_LABEL);
276 if (FacetUtils.shouldRenderFacet(facet)) {
277
278 final ResponseWriter writer = fc.getResponseWriter();
279 final FastStringWriter fsw = new FastStringWriter();
280 final ResponseWriter clonedWriter = writer.cloneWithWriter(fsw);
281 fc.setResponseWriter(clonedWriter);
282
283
284 facet.encodeAll(fc);
285
286
287 fc.setResponseWriter(writer);
288
289
290 menuItem.setValue(ExtLangUtils.unescapeXml(fsw.toString()));
291 }
292 else {
293
294 menuItem.setValue(mdl.getLevelLabel());
295 }
296
297 if (isShowAllBreadcrumbItems && lastMdlFound) {
298 menuItem.setDisabled(true);
299 }
300 else {
301 menuItem.setDisabled(mdl.isLevelDisabled());
302 }
303
304 if (putContext) {
305 fc.getExternalContext().getRequestMap().remove(contextVar);
306 }
307
308 if (!menuItem.isDisabled()) {
309
310 updateUIParameter(menuItem, masterDetail.getClientId(fc) + MasterDetail.CURRENT_LEVEL,
311 levelToRender);
312 }
313 }
314
315 if (!lastMdlFound) {
316 lastMdlFound = mdl.getLevel() == mdlToRender.getLevel();
317 }
318 }
319 }
320 }
321
322 protected DefaultMenuItem getMenuItemByLevel(final BreadCrumb breadcrumb, final MasterDetail masterDetail,
323 final MasterDetailLevel mdl) {
324 final String menuItemId = masterDetail.getId() + "_bcItem_" + mdl.getLevel();
325 for (final MenuElement child : breadcrumb.getModel().getElements()) {
326 if (menuItemId.equals(child.getId())) {
327 return (DefaultMenuItem) child;
328 }
329 }
330
331 return null;
332 }
333
334 protected void updateUIParameter(final MenuItem menuItem, final String name, final Object value) {
335 final Map<String, List<String>> params = menuItem.getParams();
336 if (params == null) {
337 return;
338 }
339
340 for (final String key : params.keySet()) {
341 if (key.equals(name)) {
342 params.remove(key);
343 menuItem.setParam(name, value);
344
345 break;
346 }
347 }
348 }
349
350 @Override
351 public void encodeChildren(final FacesContext fc, final UIComponent component) {
352
353 }
354
355 @Override
356 public boolean getRendersChildren() {
357 return true;
358 }
359
360 public static Object getConvertedSubmittedValue(final FacesContext fc, final EditableValueHolder evh) {
361 final Object submittedValue = evh.getSubmittedValue();
362 if (submittedValue == null) {
363 return null;
364 }
365
366 try {
367 final UIComponent component = (UIComponent) evh;
368 final Renderer renderer = getRenderer(fc, component);
369 if (renderer != null) {
370
371 return renderer.getConvertedValue(fc, component, submittedValue);
372 }
373 else if (submittedValue instanceof String) {
374
375
376 final Converter converter = ComponentUtils.getConverter(fc, component);
377 if (converter != null) {
378 return converter.getAsObject(fc, component, (String) submittedValue);
379 }
380 }
381 }
382 catch (final Exception e) {
383
384 return null;
385 }
386
387 return submittedValue;
388 }
389
390 public static Renderer getRenderer(final FacesContext fc, final UIComponent component) {
391 final String rendererType = component.getRendererType();
392 if (rendererType != null) {
393 return fc.getRenderKit().getRenderer(component.getFamily(), rendererType);
394 }
395
396 return null;
397 }
398 }