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.AbstractUIStyle;
25 import org.apache.myfaces.tobago.internal.component.AbstractUITree;
26 import org.apache.myfaces.tobago.internal.component.AbstractUITreeNode;
27 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
28 import org.apache.myfaces.tobago.internal.util.JsonUtils;
29 import org.apache.myfaces.tobago.internal.util.RenderUtils;
30 import org.apache.myfaces.tobago.model.ExpandedState;
31 import org.apache.myfaces.tobago.model.Selectable;
32 import org.apache.myfaces.tobago.model.SelectedState;
33 import org.apache.myfaces.tobago.model.TreePath;
34 import org.apache.myfaces.tobago.renderkit.RendererBase;
35 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
36 import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
37 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
38 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
39 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
40 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
41 import org.apache.myfaces.tobago.util.ComponentUtils;
42 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import javax.faces.component.UIComponent;
47 import javax.faces.context.FacesContext;
48 import java.io.IOException;
49 import java.lang.invoke.MethodHandles;
50 import java.util.ArrayList;
51 import java.util.List;
52
53 public class TreeRenderer<T extends AbstractUITree> extends RendererBase<T> {
54
55 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
56
57 protected static final String SCROLL_POSITION = "scrollPosition";
58
59 @Override
60 public void decodeInternal(final FacesContext facesContext, final T component) {
61 final String value = facesContext.getExternalContext().getRequestParameterMap().get(
62 component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + SCROLL_POSITION);
63 if (value != null) {
64 component.getState().getScrollPosition().update(value);
65 }
66 RenderUtils.decodedStateOfTreeData(facesContext, component);
67 }
68
69 @Override
70 public boolean getRendersChildren() {
71 return true;
72 }
73
74 @Override
75 public void encodeChildrenInternal(final FacesContext facesContext, final T component) throws IOException {
76 for (final UIComponent child : component.getChildren()) {
77 if (child instanceof AbstractUIStyle) {
78 child.encodeAll(facesContext);
79 }
80 }
81 }
82
83 @Override
84 public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
85
86 final TobagoResponseWriter writer = getResponseWriter(facesContext);
87 final String clientId = component.getClientId(facesContext);
88 final Markup markup = component.getMarkup();
89 final UIComponent root = ComponentUtils.findDescendant(component, AbstractUITreeNode.class);
90 if (root == null) {
91 LOG.error("Can't find the tree root. This may occur while updating a tree from Tobago 1.0 to 1.5. "
92 + "Please refer the documentation to see how to use tree tags.");
93 return;
94 }
95
96 writer.startElement(HtmlElements.TOBAGO_TREE);
97 writer.writeIdAttribute(clientId);
98 writer.writeClassAttribute(
99 component.getCustomClass(),
100 TobagoClass.TREE.createMarkup(markup));
101 HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
102 writer.writeAttribute(DataAttributes.SCROLL_PANEL, Boolean.TRUE.toString(), false);
103
104 final Selectable selectable = component.getSelectable();
105 if (selectable.isSupportedByTree()) {
106 writer.writeAttribute(DataAttributes.SELECTABLE, selectable.name(), false);
107 writer.writeAttribute(CustomAttributes.SELECTABLE, selectable.name(), false);
108 }
109
110 final SelectedState selectedState = component.getSelectedState();
111 final List<Integer> selectedValue = new ArrayList<>();
112
113 final ExpandedState expandedState = component.getExpandedState();
114 final List<Integer> expandedValue = new ArrayList<>();
115
116 final int last = component.isRowsUnlimited() ? Integer.MAX_VALUE : component.getFirst() + component.getRows();
117 for (int rowIndex = component.getFirst(); rowIndex < last; rowIndex++) {
118 component.setRowIndex(rowIndex);
119 if (!component.isRowAvailable()) {
120 break;
121 }
122
123 final TreePath path = component.getPath();
124
125 if (selectedState.isSelected(path)) {
126 selectedValue.add(rowIndex);
127 }
128
129 if (component.isFolder() && expandedState.isExpanded(path)) {
130 expandedValue.add(rowIndex);
131 }
132
133 for (final UIComponent child : component.getChildren()) {
134 if (child instanceof AbstractUIStyle) {
135
136 } else {
137 child.encodeAll(facesContext);
138 }
139 }
140 }
141 component.setRowIndex(-1);
142
143 writer.startElement(HtmlElements.INPUT);
144 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
145 final String selectedId = clientId + ComponentUtils.SUB_SEPARATOR + AbstractUITree.SUFFIX_SELECTED;
146 writer.writeNameAttribute(selectedId);
147 writer.writeIdAttribute(selectedId);
148 writer.writeClassAttribute(TobagoClass.TREE__SELECTED);
149 writer.writeAttribute(HtmlAttributes.VALUE, JsonUtils.encode(selectedValue), false);
150 writer.endElement(HtmlElements.INPUT);
151
152 writer.startElement(HtmlElements.INPUT);
153 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
154 final String expandedId = clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_EXPANDED;
155 writer.writeNameAttribute(expandedId);
156 writer.writeIdAttribute(expandedId);
157 writer.writeClassAttribute(TobagoClass.TREE__EXPANDED);
158 writer.writeAttribute(HtmlAttributes.VALUE, JsonUtils.encode(expandedValue), false);
159 writer.endElement(HtmlElements.INPUT);
160
161 writer.startElement(HtmlElements.INPUT);
162 writer.writeIdAttribute(clientId + ComponentUtils.SUB_SEPARATOR + SCROLL_POSITION);
163 writer.writeNameAttribute(clientId + ComponentUtils.SUB_SEPARATOR + SCROLL_POSITION);
164 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
165 writer.writeAttribute(HtmlAttributes.VALUE, component.getState().getScrollPosition().encode(), false);
166 writer.writeAttribute(DataAttributes.SCROLL_POSITION, Boolean.TRUE.toString(), false);
167 writer.endElement(HtmlElements.INPUT);
168
169 writer.endElement(HtmlElements.TOBAGO_TREE);
170 }
171 }