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.util;
21
22 import org.apache.myfaces.tobago.component.Attributes;
23 import org.apache.myfaces.tobago.component.RendererTypes;
24 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
25 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
26 import org.apache.myfaces.tobago.model.SheetState;
27 import org.apache.myfaces.tobago.util.MessageUtils;
28 import org.apache.myfaces.tobago.util.ValueExpressionComparator;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import javax.el.ValueExpression;
33 import javax.faces.application.FacesMessage;
34 import javax.faces.component.UIColumn;
35 import javax.faces.component.UICommand;
36 import javax.faces.component.UIComponent;
37 import javax.faces.component.UIInput;
38 import javax.faces.component.UIOutput;
39 import javax.faces.component.UISelectBoolean;
40 import javax.faces.component.UISelectMany;
41 import javax.faces.component.UISelectOne;
42 import javax.faces.context.FacesContext;
43 import javax.faces.model.DataModel;
44 import java.lang.invoke.MethodHandles;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.List;
50
51 public class SortingUtils {
52
53 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
54
55 private SortingUtils() {
56 }
57
58 public static void sort(final AbstractUISheet sheet, final Comparator comparator) {
59 final FacesContext facesContext = FacesContext.getCurrentInstance();
60 Object data = sheet.getValue();
61 if (data instanceof DataModel) {
62 data = ((DataModel) data).getWrappedData();
63 }
64 final SheetState sheetState = sheet.getSheetState(facesContext);
65
66 final String sortedColumnId = sheetState.getSortedColumnId();
67 LOG.debug("sorterId = '{}'", sortedColumnId);
68
69 boolean success = false;
70 if (sortedColumnId != null) {
71 final UIColumn column = (UIColumn) sheet.findComponent(sortedColumnId);
72 if (column != null) {
73 success = sort(facesContext, sheet, data, column, sheetState, comparator);
74 } else {
75 LOG.error("No column to sort found, sorterId = '{}'!", sortedColumnId);
76 addNotSortableMessage(facesContext, null);
77 }
78 } else {
79 LOG.debug("No sorterId!");
80 }
81
82 if (!success) {
83 sheetState.resetSortState();
84 }
85 }
86
87 private static boolean sort(
88 final FacesContext facesContext, final AbstractUISheet sheet, final Object data, final UIColumn column,
89 final SheetState sheetState, Comparator comparator) {
90
91 final Comparator actualComparator;
92
93 if (data instanceof List || data instanceof Object[]) {
94 try {
95 final UIComponent child = getFirstSortableChild(column.getChildren());
96 if (child != null) {
97 final boolean descending = !sheetState.isAscending();
98 final String attribute = (child instanceof AbstractUICommand ? Attributes.label : Attributes.value).getName();
99 final ValueExpression expression = child.getValueExpression(attribute);
100 if (expression != null) {
101 final String var = sheet.getVar();
102 if (var == null) {
103 LOG.error("No sorting performed. Property var of sheet is not set!");
104 addNotSortableMessage(facesContext, column);
105 return false;
106 }
107 actualComparator = new ValueExpressionComparator(facesContext, var, expression, descending, comparator);
108 } else {
109 LOG.error("No sorting performed, because no expression found for "
110 + "attribute '{}' in component '{}' with id='{}'! You may check the type of the component!",
111 attribute, child.getClass().getName(), child.getClientId());
112 addNotSortableMessage(facesContext, column);
113 return false;
114 }
115 } else {
116 LOG.error("No sorting performed. Value is not instanceof List or Object[]!");
117 addNotSortableMessage(facesContext, column);
118 return false;
119 }
120 } catch (final Exception e) {
121 LOG.error("Error while extracting sortMethod :" + e.getMessage(), e);
122 addNotSortableMessage(facesContext, column);
123 return false;
124 }
125
126
127 List<Object> selectedDataRows = null;
128 if (sheetState.getSelectedRows().size() > 0) {
129 selectedDataRows = new ArrayList<>(sheetState.getSelectedRows().size());
130 Object dataRow;
131 for (final Integer index : sheetState.getSelectedRows()) {
132 if (data instanceof List) {
133 dataRow = ((List) data).get(index);
134 } else {
135 dataRow = ((Object[]) data)[index];
136 }
137 selectedDataRows.add(dataRow);
138 }
139 }
140
141
142 if (data instanceof List) {
143 Collections.sort((List) data, actualComparator);
144 } else {
145 Arrays.sort((Object[]) data, actualComparator);
146 }
147
148
149 if (selectedDataRows != null) {
150 sheetState.getSelectedRows().clear();
151 for (final Object dataRow : selectedDataRows) {
152 int index = -1;
153 if (data instanceof List) {
154 for (int i = 0; i < ((List) data).size() && index < 0; i++) {
155 if (dataRow == ((List) data).get(i)) {
156 index = i;
157 }
158 }
159 } else {
160 for (int i = 0; i < ((Object[]) data).length && index < 0; i++) {
161 if (dataRow == ((Object[]) data)[i]) {
162 index = i;
163 }
164 }
165 }
166 if (index >= 0) {
167 sheetState.getSelectedRows().add(index);
168 }
169 }
170 }
171
172 } else {
173 LOG.warn("Sorting not supported for type '{}'.", data != null ? data.getClass().toString() : "null");
174 addNotSortableMessage(facesContext, column);
175 return false;
176 }
177 return true;
178 }
179
180 private static void addNotSortableMessage(final FacesContext facesContext, final UIColumn column) {
181 if (column != null) {
182 final String label = MessageUtils.getLabel(facesContext, column);
183 facesContext.addMessage(column.getClientId(facesContext),
184 MessageUtils.getMessage(
185 facesContext, FacesMessage.SEVERITY_WARN, AbstractUISheet.NOT_SORTABLE_COL_MESSAGE_ID, label));
186 } else {
187 facesContext.addMessage(null,
188 MessageUtils.getMessage(
189 facesContext, FacesMessage.SEVERITY_WARN, AbstractUISheet.NOT_SORTABLE_MESSAGE_ID));
190 }
191 }
192
193 private static UIComponent getFirstSortableChild(final List<UIComponent> children) {
194 UIComponent result = null;
195
196 for (UIComponent child : children) {
197 result = child;
198 if (child instanceof UISelectMany
199 || child instanceof UISelectOne
200 || child instanceof UISelectBoolean
201 || (child instanceof AbstractUICommand && child.getChildren().isEmpty())
202 || (child instanceof UIInput && RendererTypes.HIDDEN.equals(child.getRendererType()))) {
203 continue;
204
205 }
206 if (child instanceof UIOutput) {
207 break;
208 }
209 if (child instanceof UICommand
210 || child instanceof javax.faces.component.UIPanel) {
211 child = getFirstSortableChild(child.getChildren());
212 if (child instanceof UIOutput) {
213 break;
214 }
215 }
216 }
217 return result;
218 }
219 }