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