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 sortable component found!");
110                removeSortableAttribute(column);
111                return null;
112              }
113            } catch (Exception e) {
114              LOG.error("Error while extracting sortMethod :" + e.getMessage(), e);
115              if (column != null) {
116                removeSortableAttribute(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 removeSortableAttribute(UIColumn uiColumn) {
145        LOG.warn("removing attribute sortable from column " + uiColumn.getId());
146        uiColumn.getAttributes().remove(ATTR_SORTABLE);
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