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 import java.util.Arrays; 041 import java.util.Collections; 042 import java.util.Comparator; 043 import java.util.Iterator; 044 import java.util.List; 045 046 /* 047 * User: weber 048 * Date: Mar 7, 2005 049 * Time: 4:01:27 PM 050 */ 051 public class Sorter extends MethodBinding { 052 053 private static final Log LOG = LogFactory.getLog(Sorter.class); 054 055 private Comparator comparator; 056 057 public Object invoke(FacesContext facesContext, Object[] aobj) 058 throws EvaluationException { 059 if (aobj[0] instanceof SortActionEvent) { 060 SortActionEvent sortEvent = (SortActionEvent) aobj[0]; 061 if (LOG.isDebugEnabled()) { 062 LOG.debug("sorterId = " + sortEvent.getComponent().getId()); 063 } 064 UIColumn column = sortEvent.getColumn(); 065 UIData data = sortEvent.getSheet(); 066 067 Object value = data.getValue(); 068 if (value instanceof DataModel) { 069 value = ((DataModel) value).getWrappedData(); 070 } 071 SheetState sheetState = data.getSheetState(facesContext); 072 073 Comparator actualComparator = null; 074 075 if (value instanceof List || value instanceof Object[]) { 076 String sortProperty; 077 078 try { 079 080 UIComponent child = getFirstSortableChild(column.getChildren()); 081 if (child != null) { 082 ValueBinding valueBinding = child.getValueBinding("value"); 083 String var = data.getVar(); 084 085 if (valueBinding != null) { 086 if (isSimpleProperty(valueBinding.getExpressionString())) { 087 String expressionString = valueBinding.getExpressionString(); 088 if (expressionString.startsWith("#{") 089 && expressionString.endsWith("}")) { 090 expressionString = 091 expressionString.substring(2, 092 expressionString.length() - 1); 093 } 094 sortProperty = expressionString.substring(var.length() + 1); 095 096 actualComparator = new BeanComparator( 097 sortProperty, comparator, !sheetState.isAscending()); 098 099 if (LOG.isDebugEnabled()) { 100 LOG.debug("Sort property is " + sortProperty); 101 } 102 } else { 103 actualComparator = new ValueBindingComparator(facesContext, var, 104 valueBinding, !sheetState.isAscending(), comparator); 105 } 106 } 107 108 } else { 109 LOG.error("No sorting performed. Value is not instanceof List or Object[]!"); 110 unsetSortableAttribute(column); 111 return null; 112 } 113 } catch (Exception e) { 114 LOG.error("Error while extracting sortMethod :" + e.getMessage(), e); 115 if (column != null) { 116 unsetSortableAttribute(column); 117 } 118 return null; 119 } 120 121 // TODO: locale / comparator parameter? 122 // don't compare numbers with Collator.getInstance() comparator 123 // Comparator comparator = Collator.getInstance(); 124 // comparator = new RowComparator(ascending, method); 125 126 if (value instanceof List) { 127 Collections.sort((List) value, actualComparator); 128 } else { // value is instanceof Object[] 129 Arrays.sort((Object[]) value, actualComparator); 130 } 131 132 } else { // DataModel?, ResultSet, Result or Object 133 LOG.warn("Sorting not supported for type " 134 + (value != null ? value.getClass().toString() : "null")); 135 } 136 } 137 return null; 138 } 139 140 private boolean isSimpleProperty(String expressionString) { 141 return expressionString.matches("^#\\{(\\w+(\\.\\w)*)\\}$"); 142 } 143 144 private void unsetSortableAttribute(UIColumn uiColumn) { 145 LOG.warn("removing attribute sortable from column " + uiColumn.getId()); 146 uiColumn.getAttributes().put(ATTR_SORTABLE, Boolean.FALSE); 147 } 148 149 private UIComponent getFirstSortableChild(List children) { 150 UIComponent child = null; 151 152 for (Iterator iter = children.iterator(); iter.hasNext();) { 153 child = (UIComponent) iter.next(); 154 if (child instanceof UICommand 155 || child instanceof javax.faces.component.UIPanel) { 156 child = getFirstSortableChild(child.getChildren()); 157 } 158 if (child instanceof UISelectMany 159 || child instanceof UISelectOne 160 || child instanceof UISelectBoolean) { 161 continue; 162 } else if (child instanceof UIInput 163 && TobagoConstants.RENDERER_TYPE_HIDDEN.equals(child.getRendererType())) { 164 continue; 165 } else if (child instanceof UIOutput) { 166 break; 167 } 168 } 169 return child; 170 } 171 172 public Class getType(FacesContext facescontext) 173 throws MethodNotFoundException { 174 return String.class; 175 } 176 177 public Comparator getComparator() { 178 return comparator; 179 } 180 181 public void setComparator(Comparator comparator) { 182 this.comparator = comparator; 183 } 184 } 185