001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.myfaces.tobago.component; 021 022 import org.apache.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 import org.apache.commons.lang.StringUtils; 025 import org.apache.myfaces.tobago.TobagoConstants; 026 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SORTABLE; 027 import org.apache.myfaces.tobago.event.SortActionEvent; 028 import org.apache.myfaces.tobago.model.SheetState; 029 import org.apache.myfaces.tobago.util.BeanComparator; 030 import org.apache.myfaces.tobago.util.ValueBindingComparator; 031 032 import javax.faces.component.UIColumn; 033 import javax.faces.component.UIComponent; 034 import javax.faces.component.UIInput; 035 import javax.faces.component.UIOutput; 036 import javax.faces.component.UISelectBoolean; 037 import javax.faces.context.FacesContext; 038 import javax.faces.el.EvaluationException; 039 import javax.faces.el.MethodBinding; 040 import javax.faces.el.MethodNotFoundException; 041 import javax.faces.el.ValueBinding; 042 import javax.faces.model.DataModel; 043 044 import java.util.ArrayList; 045 import java.util.Arrays; 046 import java.util.Collections; 047 import java.util.Comparator; 048 import java.util.Iterator; 049 import java.util.List; 050 051 /* 052 * User: weber 053 * Date: Mar 7, 2005 054 * Time: 4:01:27 PM 055 */ 056 public class Sorter extends MethodBinding { 057 058 private static final Log LOG = LogFactory.getLog(Sorter.class); 059 060 private Comparator comparator; 061 062 public Object invoke(FacesContext facesContext, Object[] aobj) 063 throws EvaluationException { 064 if (aobj[0] instanceof SortActionEvent) { 065 SortActionEvent sortEvent = (SortActionEvent) aobj[0]; 066 if (LOG.isDebugEnabled()) { 067 LOG.debug("sorterId = " + sortEvent.getComponent().getId()); 068 } 069 UIColumn column = sortEvent.getColumn(); 070 UIData data = sortEvent.getSheet(); 071 072 Object value = data.getValue(); 073 if (value instanceof DataModel) { 074 value = ((DataModel) value).getWrappedData(); 075 } 076 SheetState sheetState = data.getSheetState(facesContext); 077 078 Comparator actualComparator = null; 079 080 if (value instanceof List || value instanceof Object[]) { 081 String sortProperty; 082 083 try { 084 085 UIComponent child = getFirstSortableChild(column.getChildren()); 086 if (child != null) { 087 String attributeName = child instanceof UICommand ? TobagoConstants.ATTR_LABEL:TobagoConstants.ATTR_VALUE; 088 ValueBinding valueBinding = child.getValueBinding(attributeName); 089 090 091 if (valueBinding != null) { 092 String var = data.getVar(); 093 if (isSimpleProperty(valueBinding.getExpressionString())) { 094 String expressionString = valueBinding.getExpressionString(); 095 if (expressionString.startsWith("#{") 096 && expressionString.endsWith("}")) { 097 expressionString = 098 expressionString.substring(2, 099 expressionString.length() - 1); 100 } 101 sortProperty = expressionString.substring(var.length() + 1); 102 103 actualComparator = new BeanComparator( 104 sortProperty, comparator, !sheetState.isAscending()); 105 106 if (LOG.isDebugEnabled()) { 107 LOG.debug("Sort property is " + sortProperty); 108 } 109 } else { 110 actualComparator = new ValueBindingComparator(facesContext, var, 111 valueBinding, !sheetState.isAscending(), comparator); 112 } 113 } 114 115 } else { 116 LOG.error("No sorting performed. Value is not instanceof List or Object[]!"); 117 unsetSortableAttribute(column); 118 return null; 119 } 120 } catch (Exception e) { 121 LOG.error("Error while extracting sortMethod :" + e.getMessage(), e); 122 if (column != null) { 123 unsetSortableAttribute(column); 124 } 125 return null; 126 } 127 128 // TODO: locale / comparator parameter? 129 // don't compare numbers with Collator.getInstance() comparator 130 // Comparator comparator = Collator.getInstance(); 131 // comparator = new RowComparator(ascending, method); 132 133 // memorize selected rows 134 List<Object> selectedDataRows = null; 135 if (sheetState.getSelectedRows() != null && sheetState.getSelectedRows().size() > 0) { 136 selectedDataRows = new ArrayList<Object>(sheetState.getSelectedRows().size()); 137 Object dataRow; 138 for (Integer index : sheetState.getSelectedRows()) { 139 if (value instanceof List) { 140 dataRow = ((List) value).get(index); 141 } else { 142 dataRow = ((Object[]) value)[index]; 143 } 144 selectedDataRows.add(dataRow); 145 } 146 } 147 148 // do sorting 149 if (value instanceof List) { 150 Collections.sort((List) value, actualComparator); 151 } else { // value is instanceof Object[] 152 Arrays.sort((Object[]) value, actualComparator); 153 } 154 155 // restore selected rows 156 if (selectedDataRows != null) { 157 sheetState.getSelectedRows().clear(); 158 for (Object dataRow : selectedDataRows) { 159 int index = -1; 160 if (value instanceof List) { 161 for (int i = 0; i < ((List) value).size() && index < 0; i++) { 162 if (dataRow == ((List) value).get(i)) { 163 index = i; 164 } 165 } 166 } else { 167 for (int i = 0; i < ((Object[]) value).length && index < 0; i++) { 168 if (dataRow == ((Object[]) value)[i]) { 169 index = i; 170 } 171 } 172 } 173 if (index >= 0) { 174 sheetState.getSelectedRows().add(index); 175 } 176 } 177 } 178 179 } else { // DataModel?, ResultSet, Result or Object 180 LOG.warn("Sorting not supported for type " 181 + (value != null ? value.getClass().toString() : "null")); 182 } 183 } 184 return null; 185 } 186 187 // XXX needs to be tested 188 // XXX was based on ^#\{(\w+(\.\w)*)\}$ which is wrong, because there is a + missing after the last \w 189 boolean isSimpleProperty(String expressionString) { 190 if (expressionString.startsWith("#{") && expressionString.endsWith("}")) { 191 String inner = expressionString.substring(2, expressionString.length() - 1); 192 String[] parts = StringUtils.split(inner, "."); 193 for (String part : parts) { 194 if (!StringUtils.isAlpha(part)) { 195 return false; 196 } 197 } 198 return true; 199 } 200 return false; 201 } 202 203 private void unsetSortableAttribute(UIColumn uiColumn) { 204 LOG.warn("removing attribute sortable from column " + uiColumn.getId()); 205 uiColumn.getAttributes().put(ATTR_SORTABLE, Boolean.FALSE); 206 } 207 208 private UIComponent getFirstSortableChild(List children) { 209 UIComponent child = null; 210 211 for (Iterator iter = children.iterator(); iter.hasNext();) { 212 child = (UIComponent) iter.next(); 213 if (child instanceof UISelectMany 214 || child instanceof UISelectOne 215 || child instanceof UISelectBoolean 216 || (child instanceof UICommand && child.getChildren().isEmpty()) 217 || (child instanceof UIInput && TobagoConstants.RENDERER_TYPE_HIDDEN.equals(child.getRendererType()))) { 218 continue; 219 // look for a better component if any 220 } 221 if (child instanceof UIOutput) { 222 break; 223 } 224 if (child instanceof javax.faces.component.UICommand 225 || child instanceof javax.faces.component.UIPanel) { 226 child = getFirstSortableChild(child.getChildren()); 227 if (child instanceof UIOutput) { 228 break; 229 } 230 } 231 } 232 return child; 233 } 234 235 public Class getType(FacesContext facescontext) 236 throws MethodNotFoundException { 237 return String.class; 238 } 239 240 public Comparator getComparator() { 241 return comparator; 242 } 243 244 public void setComparator(Comparator comparator) { 245 this.comparator = comparator; 246 } 247 } 248