1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.renderkit.renderer;
21
22 import org.apache.myfaces.tobago.context.Markup;
23 import org.apache.myfaces.tobago.internal.component.AbstractUIData;
24 import org.apache.myfaces.tobago.internal.component.AbstractUITree;
25 import org.apache.myfaces.tobago.internal.component.AbstractUITreeListbox;
26 import org.apache.myfaces.tobago.internal.component.AbstractUITreeNode;
27 import org.apache.myfaces.tobago.internal.component.AbstractUITreeSelect;
28 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
29 import org.apache.myfaces.tobago.model.Selectable;
30 import org.apache.myfaces.tobago.model.SelectedState;
31 import org.apache.myfaces.tobago.model.TreePath;
32 import org.apache.myfaces.tobago.renderkit.RendererBase;
33 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
34 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
35 import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
36 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
37 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
38 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
39 import org.apache.myfaces.tobago.util.ComponentUtils;
40 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import javax.faces.component.UINamingContainer;
45 import javax.faces.context.FacesContext;
46 import java.io.IOException;
47 import java.lang.invoke.MethodHandles;
48 import java.util.Map;
49
50 public class TreeNodeRenderer<T extends AbstractUITreeNode> extends RendererBase<T> {
51
52 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
53
54 @Override
55 public void decodeInternal(final FacesContext facesContext, final T component) {
56
57 super.decodeInternal(facesContext, component);
58
59 if (component.isDisabled()) {
60 return;
61 }
62
63 final AbstractUIData data = ComponentUtils.findAncestor(component, AbstractUIData.class);
64 if (data instanceof AbstractUITreeListbox) {
65 final String clientId = data.getClientId(facesContext);
66 final String nodeStateId = component.nodeStateId(facesContext);
67 final Map<String, String> requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
68 final String nodeId = component.getClientId(facesContext);
69 final boolean folder = component.isFolder();
70
71
72 if (folder) {
73 final boolean expanded = Boolean.parseBoolean(requestParameterMap.get(
74 nodeId + ComponentUtils.SUB_SEPARATOR + AbstractUITree.SUFFIX_EXPANDED));
75
76
77
78
79
80 }
81
82
83 if (data.getSelectable() != Selectable.none) {
84 String selected = requestParameterMap.get(
85 clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_SELECTED);
86
87 selected = selected.replaceAll("\\[", ";");
88 selected = selected.replaceAll("]", ";");
89 selected = selected.replaceAll(",", ";");
90 final String searchString = ";" + component.getClientId(facesContext) + ";";
91 final AbstractUITreeSelect treeSelect = ComponentUtils.findDescendant(component, AbstractUITreeSelect.class);
92 if (treeSelect != null) {
93 treeSelect.setSubmittedValue(selected.contains(searchString));
94 }
95 }
96
97
98 final String marked =
99 requestParameterMap.get(clientId + ComponentUtils.SUB_SEPARATOR + AbstractUITree.SUFFIX_MARKED);
100 if (marked != null) {
101 final String searchString = clientId + UINamingContainer.getSeparatorChar(facesContext) + nodeStateId;
102 final boolean markedValue = marked.equals(searchString);
103
104
105
106
107
108 } else {
109 LOG.warn("This log message is help clarifying the occurrence of this else case.");
110 }
111 }
112 }
113
114 @Override
115 public void encodeBeginInternal(final FacesContext facesContext, final T component) throws IOException {
116
117 final AbstractUIData data = ComponentUtils.findAncestor(component, AbstractUIData.class);
118 final boolean dataRendersRowContainer = data.isRendersRowContainer();
119 final String clientId = component.getClientId(facesContext);
120 final String parentId = data.getRowParentClientId();
121 final boolean visible = data.isRowVisible();
122 final boolean folder = component.isFolder();
123 Markup markup = Markup.NULL;
124 final TreePath path = component.getPath();
125 final SelectedState selectedState = data.getSelectedState();
126 final boolean selected = data instanceof AbstractUITree && selectedState.isSelected(path);
127
128 if (selected) {
129 markup = markup.add(Markup.SELECTED);
130 }
131 if (folder) {
132 markup = markup.add(Markup.FOLDER);
133 if (data.getExpandedState().isExpanded(path)) {
134 markup = markup.add(Markup.EXPANDED);
135 }
136 }
137
138 final TobagoResponseWriter writer = getResponseWriter(facesContext);
139
140 if (data instanceof AbstractUITreeListbox) {
141 writer.startElement(HtmlElements.OPTION);
142
143
144 writer.writeAttribute(HtmlAttributes.VALUE, clientId, true);
145 writer.writeIdAttribute(clientId);
146 writer.writeAttribute(HtmlAttributes.SELECTED, selectedState.isAncestorOfSelected(path));
147 writer.writeAttribute(CustomAttributes.ROW_INDEX, data.getRowIndex());
148 } else {
149 writer.startElement(HtmlElements.TOBAGO_TREE_NODE);
150
151
152 writer.writeIdAttribute(clientId);
153
154
155 final boolean hidden = !dataRendersRowContainer && !visible;
156
157 writer.writeClassAttribute(
158 null,
159 TobagoClass.TREE_NODE.createMarkup(markup),
160 hidden ? BootstrapClass.D_NONE : null,
161 component.getCustomClass());
162 writer.writeAttribute(CustomAttributes.SELECTED, selected);
163 writer.writeAttribute(CustomAttributes.EXPANDABLE, folder);
164 writer.writeAttribute(CustomAttributes.INDEX, data.getRowIndex());
165 HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
166 if (parentId != null) {
167
168
169
170 writer.writeAttribute(DataAttributes.TREE_PARENT, parentId, false);
171 writer.writeAttribute(CustomAttributes.PARENT, parentId, false);
172 }
173 writer.writeAttribute(DataAttributes.LEVEL, data.isShowRoot() ? component.getLevel() : component.getLevel() - 1);
174 }
175 }
176
177 @Override
178 public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
179 final AbstractUIData data = ComponentUtils.findAncestor(component, AbstractUIData.class);
180 final int level = component.getLevel();
181 final boolean folder = component.isFolder();
182 final boolean expanded = folder && data.getExpandedState().isExpanded(component.getPath()) || level == 0;
183
184 final TobagoResponseWriter writer = getResponseWriter(facesContext);
185
186 if (data instanceof AbstractUITreeListbox) {
187 if (folder) {
188 writer.writeText(" \u2192");
189 }
190 writer.endElement(HtmlElements.OPTION);
191 } else {
192 writer.endElement(HtmlElements.TOBAGO_TREE_NODE);
193 }
194 }
195 }