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.component;
21
22 import org.apache.myfaces.tobago.event.SortActionEvent;
23 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
24 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
25 import org.apache.myfaces.tobago.internal.util.StringUtils;
26 import org.apache.myfaces.tobago.model.SheetState;
27 import org.apache.myfaces.tobago.util.BeanComparator;
28 import org.apache.myfaces.tobago.util.MessageUtils;
29 import org.apache.myfaces.tobago.util.ValueExpressionComparator;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import javax.el.ValueExpression;
34 import javax.faces.application.FacesMessage;
35 import javax.faces.component.UIColumn;
36 import javax.faces.component.UICommand;
37 import javax.faces.component.UIComponent;
38 import javax.faces.component.UIInput;
39 import javax.faces.component.UIOutput;
40 import javax.faces.component.UISelectBoolean;
41 import javax.faces.component.UISelectMany;
42 import javax.faces.component.UISelectOne;
43 import javax.faces.context.FacesContext;
44 import javax.faces.model.DataModel;
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 Sorter {
52
53 private static final Logger LOG = LoggerFactory.getLogger(Sorter.class);
54
55 private Comparator comparator;
56
57
58
59
60 @Deprecated
61 public void perform(final SortActionEvent sortEvent) {
62 final AbstractUISheet data = (AbstractUISheet) sortEvent.getComponent();
63 perform(data);
64 }
65
66 public void perform(final AbstractUISheet sheet) {
67 final FacesContext facesContext = FacesContext.getCurrentInstance();
68 Object data = sheet.getValue();
69 if (data instanceof DataModel) {
70 data = ((DataModel) data).getWrappedData();
71 }
72 final SheetState sheetState = sheet.getSheetState(facesContext);
73
74 final String sortedColumnId = sheetState.getSortedColumnId();
75 if (LOG.isDebugEnabled()) {
76 LOG.debug("sorterId = '{}'", sortedColumnId);
77 }
78
79 boolean success = false;
80 if (sortedColumnId != null) {
81 final UIColumn column = (UIColumn) sheet.findComponent(sortedColumnId);
82 if (column != null) {
83 success = perform(facesContext, sheet, data, column, sheetState);
84 } else {
85 LOG.error("No column to sort found, sorterId = '{}'!", sortedColumnId);
86 addNotSortableMessage(facesContext, null);
87 }
88 } else {
89 LOG.error("No sorterId!");
90 addNotSortableMessage(facesContext, null);
91 }
92
93 if (!success) {
94 sheetState.resetSortState();
95 }
96 }
97
98 private boolean perform(
99 final FacesContext facesContext, final AbstractUISheet sheet, final Object data, final UIColumn column,
100 final SheetState sheetState) {
101
102 final Comparator actualComparator;
103
104 if (data instanceof List || data instanceof Object[]) {
105 final String sortProperty;
106
107 try {
108 final UIComponent child = getFirstSortableChild(column.getChildren());
109 if (child != null) {
110
111 final Attributes attribute = child instanceof AbstractUICommand ? Attributes.label : Attributes.value;
112 if (child.getValueExpression(attribute.getName()) != null) {
113 final String var = sheet.getVar();
114 if (var == null) {
115 LOG.error("No sorting performed. Property var of sheet is not set!");
116 addNotSortableMessage(facesContext, column);
117 return false;
118 }
119 String expressionString = child.getValueExpression(attribute.getName()).getExpressionString();
120 if (isSimpleProperty(expressionString)) {
121 if (expressionString.startsWith("#{")
122 && expressionString.endsWith("}")) {
123 expressionString =
124 expressionString.substring(2,
125 expressionString.length() - 1);
126 }
127 sortProperty = expressionString.substring(var.length() + 1);
128
129 actualComparator = new BeanComparator(
130 sortProperty, comparator, !sheetState.isAscending());
131
132 if (LOG.isDebugEnabled()) {
133 LOG.debug("Sort property is {}", sortProperty);
134 }
135 } else {
136
137 final boolean descending = !sheetState.isAscending();
138 final ValueExpression expression = child.getValueExpression("value");
139 actualComparator = new ValueExpressionComparator(facesContext, var, expression, descending, comparator);
140 }
141 } else {
142 LOG.error("No sorting performed, because no expression found for "
143 + "attribute '{}' in component '{}' with id='{}'! You may check the type of the component!",
144 attribute.getName(), child.getClass().getName(), child.getClientId());
145 addNotSortableMessage(facesContext, column);
146 return false;
147 }
148 } else {
149 LOG.error("No sorting performed. Value is not instanceof List or Object[]!");
150 addNotSortableMessage(facesContext, column);
151 return false;
152 }
153 } catch (final Exception e) {
154 LOG.error("Error while extracting sortMethod :" + e.getMessage(), e);
155 addNotSortableMessage(facesContext, column);
156 return false;
157 }
158
159
160
161
162
163
164
165 List<Object> selectedDataRows = null;
166 if (sheetState.getSelectedRows().size() > 0) {
167 selectedDataRows = new ArrayList<>(sheetState.getSelectedRows().size());
168 Object dataRow;
169 for (final Integer index : sheetState.getSelectedRows()) {
170 if (data instanceof List) {
171 dataRow = ((List) data).get(index);
172 } else {
173 dataRow = ((Object[]) data)[index];
174 }
175 selectedDataRows.add(dataRow);
176 }
177 }
178
179
180 if (data instanceof List) {
181 Collections.sort((List) data, actualComparator);
182 } else {
183 Arrays.sort((Object[]) data, actualComparator);
184 }
185
186
187 if (selectedDataRows != null) {
188 sheetState.getSelectedRows().clear();
189 for (final Object dataRow : selectedDataRows) {
190 int index = -1;
191 if (data instanceof List) {
192 for (int i = 0; i < ((List) data).size() && index < 0; i++) {
193 if (dataRow == ((List) data).get(i)) {
194 index = i;
195 }
196 }
197 } else {
198 for (int i = 0; i < ((Object[]) data).length && index < 0; i++) {
199 if (dataRow == ((Object[]) data)[i]) {
200 index = i;
201 }
202 }
203 }
204 if (index >= 0) {
205 sheetState.getSelectedRows().add(index);
206 }
207 }
208 }
209
210 } else {
211 LOG.warn("Sorting not supported for type "
212 + (data != null ? data.getClass().toString() : "null"));
213 }
214 return true;
215 }
216
217
218
219 boolean isSimpleProperty(final String expressionString) {
220 if (expressionString.startsWith("#{") && expressionString.endsWith("}")) {
221 final String inner = expressionString.substring(2, expressionString.length() - 1);
222 final String[] parts = StringUtils.split(inner, '.');
223 for (final String part : parts) {
224 if (!StringUtils.isAlpha(part)) {
225 return false;
226 }
227 }
228 return true;
229 }
230 return false;
231 }
232
233 private void addNotSortableMessage(final FacesContext facesContext, final UIColumn column) {
234 MessageUtils.addMessage(facesContext, column, FacesMessage.SEVERITY_WARN,
235 AbstractUISheet.NOT_SORTABLE_MESSAGE_ID, new Object[]{MessageUtils.getLabel(facesContext, column)});
236 }
237
238 private UIComponent getFirstSortableChild(final List<UIComponent> children) {
239 UIComponent result = null;
240
241 for (UIComponent child : children) {
242 result = child;
243 if (child instanceof UISelectMany
244 || child instanceof UISelectOne
245 || child instanceof UISelectBoolean
246 || (child instanceof AbstractUICommand && child.getChildren().isEmpty())
247 || (child instanceof UIInput && RendererTypes.HIDDEN.equals(child.getRendererType()))) {
248 continue;
249
250 }
251 if (child instanceof UIOutput) {
252 break;
253 }
254 if (child instanceof UICommand
255 || child instanceof javax.faces.component.UIPanel) {
256 child = getFirstSortableChild(child.getChildren());
257 if (child instanceof UIOutput) {
258 break;
259 }
260 }
261 }
262 return result;
263 }
264
265 public Comparator getComparator() {
266 return comparator;
267 }
268
269 public void setComparator(final Comparator comparator) {
270 this.comparator = comparator;
271 }
272 }
273