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.component;
21
22 import org.apache.myfaces.tobago.component.Visual;
23 import org.apache.myfaces.tobago.model.ExpandedState;
24 import org.apache.myfaces.tobago.model.Selectable;
25 import org.apache.myfaces.tobago.model.SelectedState;
26 import org.apache.myfaces.tobago.model.TreeDataModel;
27 import org.apache.myfaces.tobago.model.TreeNodeDataModel;
28 import org.apache.myfaces.tobago.model.TreePath;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import javax.faces.FacesException;
33 import javax.faces.component.ContextCallback;
34 import javax.faces.component.UIComponent;
35 import javax.faces.component.visit.VisitCallback;
36 import javax.faces.component.visit.VisitContext;
37 import javax.faces.context.FacesContext;
38 import javax.faces.model.DataModel;
39 import javax.swing.tree.TreeNode;
40 import java.io.IOException;
41 import java.lang.invoke.MethodHandles;
42 import java.util.List;
43
44
45
46
47 public abstract class AbstractUIData extends javax.faces.component.UIData implements Visual {
48
49 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
50
51
52
53
54 @Deprecated
55 public static final String SUFFIX_MARKED = "marked";
56 public static final String SUFFIX_SELECTED = "selected";
57 public static final String SUFFIX_EXPANDED = "expanded";
58
59
60
61
62 private boolean initialized;
63
64
65
66
67 private TreeDataModel dataModel;
68
69 public boolean isTreeModel() {
70 init();
71 return dataModel != null;
72 }
73
74 public TreeDataModel getTreeDataModel() {
75 if (isTreeModel()) {
76 return dataModel;
77 } else {
78 LOG.warn("Not a tree model");
79 return null;
80 }
81 }
82
83 @Override
84 protected DataModel getDataModel() {
85 init();
86
87 if (dataModel != null) {
88 return dataModel;
89 } else {
90 return super.getDataModel();
91 }
92 }
93
94 private void init() {
95 if (!initialized) {
96 final Object value = getValue();
97 final boolean showRoot = isShowRoot();
98 createTreeDataModel(value, showRoot);
99
100 initialized = true;
101 }
102 }
103
104
105
106
107 @Deprecated
108 public Selectable getSelectableAsEnum() {
109 return getSelectable();
110 }
111
112 public abstract Selectable getSelectable();
113
114
115
116
117
118
119
120
121
122
123 protected void createTreeDataModel(final Object value, final boolean showRoot) {
124
125 if (value instanceof TreeNode) {
126 dataModel = new TreeNodeDataModel((TreeNode) value, showRoot, getExpandedState());
127 }
128 }
129
130 @Override
131 public void encodeBegin(final FacesContext context) throws IOException {
132 initialized = false;
133 init();
134 if (dataModel != null) {
135 dataModel.reset();
136 }
137
138 if (getFirst() >= getRowCount()) {
139 if (getRowCount() > 0) {
140 LOG.warn("Illegal paging state detected, first='{}' >= rowCount='{}'. Setting first to 0. "
141 + "This might happen because the data model has changed. "
142 + "You may want to manipulate the sheet state in your application after manipulating the model "
143 + "(e. g. filtering) to avoid this warning.", getFirst(), getRowCount());
144 }
145 setFirst(0);
146 }
147
148 super.encodeBegin(context);
149 }
150
151 public abstract ExpandedState getExpandedState();
152
153 public abstract SelectedState getSelectedState();
154
155 public boolean isRowVisible() {
156 init();
157 if (dataModel != null) {
158 return dataModel.isRowVisible();
159 } else {
160 return super.getDataModel().isRowAvailable();
161 }
162 }
163
164 public String getRowClientId() {
165 init();
166 return dataModel != null ? dataModel.getRowClientId() : null;
167 }
168
169 public String getRowParentClientId() {
170 init();
171 return dataModel != null ? dataModel.getRowParentClientId() : null;
172 }
173
174 public abstract boolean isShowRoot();
175
176 public boolean isShowRootJunction() {
177 return false;
178 }
179
180
181
182
183 public boolean isRowsUnlimited() {
184 return getRows() == 0;
185 }
186
187
188
189
190
191
192
193 public boolean isRendersRowContainer() {
194 return false;
195 }
196
197 @Override
198 public boolean invokeOnComponent(
199 final FacesContext facesContext, final String clientId, final ContextCallback callback)
200 throws FacesException {
201
202 final int oldRowIndex = getRowIndex();
203 try {
204 final String sheetId = getClientId(facesContext);
205 if (clientId.startsWith(sheetId)) {
206 String idRemainder = clientId.substring(sheetId.length());
207 if (LOG.isDebugEnabled()) {
208 LOG.debug("idRemainder = '" + idRemainder + "'");
209 }
210 if (idRemainder.matches("^:\\d+:.*")) {
211 idRemainder = idRemainder.substring(1);
212 final int idx = idRemainder.indexOf(":");
213 try {
214 final int rowIndex = Integer.parseInt(idRemainder.substring(0, idx));
215 if (LOG.isDebugEnabled()) {
216 LOG.debug("set rowIndex = '" + rowIndex + "'");
217 }
218 setRowIndex(rowIndex);
219 } catch (final NumberFormatException e) {
220 LOG.warn("idRemainder = '" + idRemainder + "'", e);
221 }
222 } else {
223 if (LOG.isDebugEnabled()) {
224 LOG.debug("no match for '^:\\d+:.*'");
225 }
226 }
227 }
228
229 return super.invokeOnComponent(facesContext, clientId, callback);
230
231 } finally {
232
233 setRowIndex(oldRowIndex);
234 }
235 }
236
237
238
239
240 public TreePath getPath() {
241 if (isTreeModel()) {
242 return ((TreeDataModel) getDataModel()).getPath();
243 } else {
244 LOG.warn("Not a tree model");
245 return null;
246 }
247 }
248
249
250
251
252 public boolean isFolder() {
253 if (isTreeModel()) {
254 return ((TreeDataModel) getDataModel()).isFolder();
255 } else {
256 LOG.warn("Not a tree model");
257 return false;
258 }
259 }
260
261 public List<Integer> getRowIndicesOfChildren() {
262 if (isTreeModel()) {
263 return dataModel.getRowIndicesOfChildren();
264 } else {
265 LOG.warn("Not a tree model");
266 return null;
267 }
268 }
269
270
271
272
273 @Override
274 public boolean visitTree(final VisitContext context, final VisitCallback callback) {
275
276 if (super.visitTree(context, callback)) {
277 return true;
278 }
279
280
281 final int oldRowIndex = getRowIndex();
282
283 setRowIndex(-1);
284
285 pushComponentToEL(context.getFacesContext(), this);
286
287 try {
288
289 int rowsToProcess = getRows();
290
291 if (rowsToProcess == 0) {
292 rowsToProcess = getRowCount();
293 }
294 int rowIndex = getFirst();
295 for (int rowsProcessed = 0; rowsProcessed < rowsToProcess; rowsProcessed++, rowIndex++) {
296 setRowIndex(rowIndex);
297 if (!isRowAvailable()) {
298 return false;
299 }
300
301 for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
302 final UIComponent child = getChildren().get(i);
303 if (child instanceof AbstractUIRow) {
304 if (child.visitTree(context, callback)) {
305 return true;
306 }
307
308 }
309 }
310 }
311 } finally {
312
313 popComponentFromEL(context.getFacesContext());
314 setRowIndex(oldRowIndex);
315 }
316
317
318 return false;
319 }
320
321 }