You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/02/27 12:48:51 UTC

[myfaces-tobago] branch tobago-4.x updated: TOBAGO-1972: Sheet sorting: can't sort "Apples with Pears"

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch tobago-4.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/tobago-4.x by this push:
     new e15c90a  TOBAGO-1972: Sheet sorting: can't sort "Apples with Pears"
e15c90a is described below

commit e15c90a4276cea37771082dedef92179b6e85ef0
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Wed Feb 27 13:48:40 2019 +0100

    TOBAGO-1972: Sheet sorting: can't sort "Apples with Pears"
    
    * refactoring: Sorter -> SortingUtils
    * adding unit test
---
 .../apache/myfaces/tobago/component/Sorter.java    | 192 ++-------------------
 .../tobago/internal/component/AbstractUISheet.java |   4 +-
 .../util/SortingUtils.java}                        |  47 ++---
 .../myfaces/tobago/component/SorterUnitTest.java   |  57 ------
 .../apache/myfaces/tobago/internal/util/Apple.java |  27 +++
 .../apache/myfaces/tobago/internal/util/Fruit.java |  20 +++
 .../apache/myfaces/tobago/internal/util/Pear.java  |  27 +++
 .../tobago/internal/util/SortingUtilsUnitTest.java | 119 +++++++++++++
 .../tobago/util/BeanComparatorUnitTest.java        |  60 +------
 9 files changed, 222 insertions(+), 331 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java
index 53dae76..e6dc3b8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java
@@ -20,36 +20,17 @@
 package org.apache.myfaces.tobago.component;
 
 import org.apache.myfaces.tobago.event.SortActionEvent;
-import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
-import org.apache.myfaces.tobago.model.SheetState;
-import org.apache.myfaces.tobago.util.MessageUtils;
-import org.apache.myfaces.tobago.util.ValueExpressionComparator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.myfaces.tobago.internal.util.SortingUtils;
 
-import javax.el.ValueExpression;
-import javax.faces.application.FacesMessage;
-import javax.faces.component.UIColumn;
-import javax.faces.component.UICommand;
-import javax.faces.component.UIComponent;
-import javax.faces.component.UIInput;
-import javax.faces.component.UIOutput;
-import javax.faces.component.UISelectBoolean;
-import javax.faces.component.UISelectMany;
-import javax.faces.component.UISelectOne;
-import javax.faces.context.FacesContext;
-import javax.faces.model.DataModel;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
 
+/**
+ * @deprecated since 4.4.0. Please use {@link SortingUtils}
+ */
+@Deprecated
 public class Sorter {
 
-  private static final Logger LOG = LoggerFactory.getLogger(Sorter.class);
-
   private Comparator comparator;
 
   /**
@@ -57,165 +38,15 @@ public class Sorter {
    */
   @Deprecated
   public void perform(final SortActionEvent sortEvent) {
-    final AbstractUISheet data = (AbstractUISheet) sortEvent.getComponent();
-    perform(data);
+    SortingUtils.sort((AbstractUISheet) sortEvent.getComponent(), comparator);
   }
 
+  /**
+   * @deprecated since 4.4.0. Please use {@link SortingUtils#sort}
+   */
+  @Deprecated
   public void perform(final AbstractUISheet sheet) {
-    final FacesContext facesContext = FacesContext.getCurrentInstance();
-    Object data = sheet.getValue();
-    if (data instanceof DataModel) {
-      data = ((DataModel) data).getWrappedData();
-    }
-    final SheetState sheetState = sheet.getSheetState(facesContext);
-
-    final String sortedColumnId = sheetState.getSortedColumnId();
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("sorterId = '{}'", sortedColumnId);
-    }
-
-    boolean success = false;
-    if (sortedColumnId != null) {
-      final UIColumn column = (UIColumn) sheet.findComponent(sortedColumnId);
-      if (column != null) {
-        success = perform(facesContext, sheet, data, column, sheetState);
-      } else {
-        LOG.error("No column to sort found, sorterId = '{}'!", sortedColumnId);
-        addNotSortableMessage(facesContext, null);
-      }
-    } else {
-      LOG.error("No sorterId!");
-      addNotSortableMessage(facesContext, null);
-    }
-
-    if (!success) {
-      sheetState.resetSortState();
-    }
-  }
-
-  private boolean perform(
-      final FacesContext facesContext, final AbstractUISheet sheet, final Object data, final UIColumn column,
-      final SheetState sheetState) {
-
-    final Comparator actualComparator;
-
-    if (data instanceof List || data instanceof Object[]) {
-      try {
-        final UIComponent child = getFirstSortableChild(column.getChildren());
-        if (child != null) {
-          final boolean descending = !sheetState.isAscending();
-          final String attribute = (child instanceof AbstractUICommand ? Attributes.label : Attributes.value).getName();
-          final ValueExpression expression = child.getValueExpression(attribute);
-          if (expression != null) {
-            final String var = sheet.getVar();
-            if (var == null) {
-                LOG.error("No sorting performed. Property var of sheet is not set!");
-                addNotSortableMessage(facesContext, column);
-                return false;
-            }
-            actualComparator = new ValueExpressionComparator(facesContext, var, expression, descending, comparator);
-          } else {
-            LOG.error("No sorting performed, because no expression found for "
-                    + "attribute '{}' in component '{}' with id='{}'! You may check the type of the component!",
-                attribute, child.getClass().getName(), child.getClientId());
-            addNotSortableMessage(facesContext, column);
-            return false;
-          }
-        } else {
-          LOG.error("No sorting performed. Value is not instanceof List or Object[]!");
-          addNotSortableMessage(facesContext, column);
-          return false;
-        }
-      } catch (final Exception e) {
-        LOG.error("Error while extracting sortMethod :" + e.getMessage(), e);
-        addNotSortableMessage(facesContext, column);
-        return false;
-      }
-
-      // memorize selected rows
-      List<Object> selectedDataRows = null;
-      if (sheetState.getSelectedRows().size() > 0) {
-        selectedDataRows = new ArrayList<>(sheetState.getSelectedRows().size());
-        Object dataRow;
-        for (final Integer index : sheetState.getSelectedRows()) {
-          if (data instanceof List) {
-            dataRow = ((List) data).get(index);
-          } else {
-            dataRow = ((Object[]) data)[index];
-          }
-          selectedDataRows.add(dataRow);
-        }
-      }
-
-      // do sorting
-      if (data instanceof List) {
-        Collections.sort((List) data, actualComparator);
-      } else { // value is instanceof Object[]
-        Arrays.sort((Object[]) data, actualComparator);
-      }
-
-      // restore selected rows
-      if (selectedDataRows != null) {
-        sheetState.getSelectedRows().clear();
-        for (final Object dataRow : selectedDataRows) {
-          int index = -1;
-          if (data instanceof List) {
-            for (int i = 0; i < ((List) data).size() && index < 0; i++) {
-              if (dataRow == ((List) data).get(i)) {
-                index = i;
-              }
-            }
-          } else {
-            for (int i = 0; i < ((Object[]) data).length && index < 0; i++) {
-              if (dataRow == ((Object[]) data)[i]) {
-                index = i;
-              }
-            }
-          }
-          if (index >= 0) {
-            sheetState.getSelectedRows().add(index);
-          }
-        }
-      }
-
-    } else {  // DataModel?, ResultSet, Result or Object
-      LOG.warn("Sorting not supported for type '{}'.", data != null ? data.getClass().toString() : "null");
-      addNotSortableMessage(facesContext, column);
-      return false;
-    }
-    return true;
-  }
-
-  private void addNotSortableMessage(final FacesContext facesContext, final UIColumn column) {
-    MessageUtils.addMessage(facesContext, column, FacesMessage.SEVERITY_WARN,
-        AbstractUISheet.NOT_SORTABLE_MESSAGE_ID, new Object[]{MessageUtils.getLabel(facesContext, column)});
-  }
-
-  private UIComponent getFirstSortableChild(final List<UIComponent> children) {
-    UIComponent result = null;
-
-    for (UIComponent child : children) {
-      result = child;
-      if (child instanceof UISelectMany
-          || child instanceof UISelectOne
-          || child instanceof UISelectBoolean
-          || (child instanceof AbstractUICommand && child.getChildren().isEmpty())
-          || (child instanceof UIInput && RendererTypes.HIDDEN.equals(child.getRendererType()))) {
-        continue;
-        // look for a better component if any
-      }
-      if (child instanceof UIOutput) {
-        break;
-      }
-      if (child instanceof UICommand
-          || child instanceof javax.faces.component.UIPanel) {
-        child = getFirstSortableChild(child.getChildren());
-        if (child instanceof UIOutput) {
-          break;
-        }
-      }
-    }
-    return result;
+    SortingUtils.sort(sheet, comparator);
   }
 
   public Comparator getComparator() {
@@ -226,4 +57,3 @@ public class Sorter {
     this.comparator = comparator;
   }
 }
-
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
index 48be4f4..8980994 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
@@ -20,7 +20,6 @@
 package org.apache.myfaces.tobago.internal.component;
 
 import org.apache.myfaces.tobago.component.Attributes;
-import org.apache.myfaces.tobago.component.Sorter;
 import org.apache.myfaces.tobago.component.Visual;
 import org.apache.myfaces.tobago.event.PageActionEvent;
 import org.apache.myfaces.tobago.event.SheetStateChangeEvent;
@@ -30,6 +29,7 @@ import org.apache.myfaces.tobago.event.SortActionEvent;
 import org.apache.myfaces.tobago.event.SortActionSource;
 import org.apache.myfaces.tobago.internal.layout.Grid;
 import org.apache.myfaces.tobago.internal.layout.OriginCell;
+import org.apache.myfaces.tobago.internal.util.SortingUtils;
 import org.apache.myfaces.tobago.layout.Measure;
 import org.apache.myfaces.tobago.layout.MeasureList;
 import org.apache.myfaces.tobago.model.ExpandedState;
@@ -443,7 +443,7 @@ public abstract class AbstractUISheet extends AbstractUIData
           LOG.warn("Sorting not possible!", e);
         }
       } else {
-        new Sorter().perform(this);
+        SortingUtils.sort(this, null);
       }
       sheetState.setToBeSorted(false);
     }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/SortingUtils.java
similarity index 85%
copy from tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java
copy to tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/SortingUtils.java
index 53dae76..3de48f9 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Sorter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/SortingUtils.java
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-package org.apache.myfaces.tobago.component;
+package org.apache.myfaces.tobago.internal.util;
 
-import org.apache.myfaces.tobago.event.SortActionEvent;
+import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
 import org.apache.myfaces.tobago.model.SheetState;
@@ -46,22 +47,14 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-public class Sorter {
+public class SortingUtils {
 
-  private static final Logger LOG = LoggerFactory.getLogger(Sorter.class);
+  private static final Logger LOG = LoggerFactory.getLogger(SortingUtils.class);
 
-  private Comparator comparator;
-
-  /**
-   * @deprecated since 2.0.7, please use {@link #perform(org.apache.myfaces.tobago.internal.component.AbstractUISheet)}
-   */
-  @Deprecated
-  public void perform(final SortActionEvent sortEvent) {
-    final AbstractUISheet data = (AbstractUISheet) sortEvent.getComponent();
-    perform(data);
+  private SortingUtils() {
   }
 
-  public void perform(final AbstractUISheet sheet) {
+  public static void sort(final AbstractUISheet sheet, final Comparator comparator) {
     final FacesContext facesContext = FacesContext.getCurrentInstance();
     Object data = sheet.getValue();
     if (data instanceof DataModel) {
@@ -70,22 +63,19 @@ public class Sorter {
     final SheetState sheetState = sheet.getSheetState(facesContext);
 
     final String sortedColumnId = sheetState.getSortedColumnId();
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("sorterId = '{}'", sortedColumnId);
-    }
+    LOG.debug("sorterId = '{}'", sortedColumnId);
 
     boolean success = false;
     if (sortedColumnId != null) {
       final UIColumn column = (UIColumn) sheet.findComponent(sortedColumnId);
       if (column != null) {
-        success = perform(facesContext, sheet, data, column, sheetState);
+        success = sort(facesContext, sheet, data, column, sheetState, comparator);
       } else {
         LOG.error("No column to sort found, sorterId = '{}'!", sortedColumnId);
         addNotSortableMessage(facesContext, null);
       }
     } else {
-      LOG.error("No sorterId!");
-      addNotSortableMessage(facesContext, null);
+      LOG.debug("No sorterId!");
     }
 
     if (!success) {
@@ -93,9 +83,9 @@ public class Sorter {
     }
   }
 
-  private boolean perform(
+  private static boolean sort(
       final FacesContext facesContext, final AbstractUISheet sheet, final Object data, final UIColumn column,
-      final SheetState sheetState) {
+      final SheetState sheetState, Comparator comparator) {
 
     final Comparator actualComparator;
 
@@ -186,12 +176,12 @@ public class Sorter {
     return true;
   }
 
-  private void addNotSortableMessage(final FacesContext facesContext, final UIColumn column) {
+  private static void addNotSortableMessage(final FacesContext facesContext, final UIColumn column) {
     MessageUtils.addMessage(facesContext, column, FacesMessage.SEVERITY_WARN,
         AbstractUISheet.NOT_SORTABLE_MESSAGE_ID, new Object[]{MessageUtils.getLabel(facesContext, column)});
   }
 
-  private UIComponent getFirstSortableChild(final List<UIComponent> children) {
+  private static UIComponent getFirstSortableChild(final List<UIComponent> children) {
     UIComponent result = null;
 
     for (UIComponent child : children) {
@@ -217,13 +207,4 @@ public class Sorter {
     }
     return result;
   }
-
-  public Comparator getComparator() {
-    return comparator;
-  }
-
-  public void setComparator(final Comparator comparator) {
-    this.comparator = comparator;
-  }
 }
-
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/component/SorterUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/component/SorterUnitTest.java
deleted file mode 100644
index bc25c27..0000000
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/component/SorterUnitTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.myfaces.tobago.component;
-
-import org.apache.myfaces.test.el.MockValueExpression;
-import org.apache.myfaces.tobago.event.SortActionEvent;
-import org.apache.myfaces.tobago.internal.config.AbstractTobagoTestBase;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SorterUnitTest extends AbstractTobagoTestBase {
-
-    @Test
-    public void testSorter() {
-        final UISheet sheet = new UISheet();
-        final UIColumn column = new UIColumn();
-        sheet.getChildren().add(column);
-
-        final Sorter sorter = new Sorter();
-        final SortActionEvent sortActionEvent = new SortActionEvent(sheet, column);
-        sorter.perform(sortActionEvent);
-
-        final List list = new ArrayList();
-        sheet.setValue(list);
-        sorter.perform(sortActionEvent);
-
-        final UILink link = new UILink();
-        column.getChildren().add(link);
-
-        sorter.perform(sortActionEvent);
-
-        link.setValueExpression(Attributes.label.getName(),
-                new MockValueExpression("var.test", String.class));
-
-        sorter.perform(sortActionEvent);
-
-    }
-}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Apple.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Apple.java
new file mode 100644
index 0000000..77da572
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Apple.java
@@ -0,0 +1,27 @@
+package org.apache.myfaces.tobago.internal.util;
+
+public class Apple implements Fruit {
+
+  public static final Apple SCHOENER_AUS_BOSKOOP = new Apple("Schöner aus Boskoop");
+  public static final Apple GOLDEN_DELICIOUS = new Apple("Golden Delicious");
+
+  private String name;
+
+  public Apple(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String toString() {
+    return getName();
+  }
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Fruit.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Fruit.java
new file mode 100644
index 0000000..e375b83
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Fruit.java
@@ -0,0 +1,20 @@
+package org.apache.myfaces.tobago.internal.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public interface Fruit extends Serializable {
+
+  static List<Fruit> getFreshFruits() {
+      final List<Fruit> list = new ArrayList<>();
+      list.add(Apple.GOLDEN_DELICIOUS);
+      list.add(Apple.SCHOENER_AUS_BOSKOOP);
+      list.add(Pear.WILLIAMS_CHRIST);
+      list.add(Pear.KOESTLICHE_AUS_CHARNEUX);
+      return list;
+  }
+
+  String getName();
+  void setName(String name);
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Pear.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Pear.java
new file mode 100644
index 0000000..c010e55
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/Pear.java
@@ -0,0 +1,27 @@
+package org.apache.myfaces.tobago.internal.util;
+
+public class Pear implements Fruit {
+
+  public static final Pear WILLIAMS_CHRIST =new Pear("Williams Christ");
+  public static final Pear KOESTLICHE_AUS_CHARNEUX =new Pear("Köstliche aus Charneux");
+
+  private String name;
+
+  public Pear(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public String toString() {
+    return getName();
+  }
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/SortingUtilsUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/SortingUtilsUnitTest.java
new file mode 100644
index 0000000..1e66598
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/SortingUtilsUnitTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.util;
+
+import org.apache.myfaces.test.el.MockValueExpression;
+import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.component.UIColumn;
+import org.apache.myfaces.tobago.component.UILink;
+import org.apache.myfaces.tobago.component.UIOut;
+import org.apache.myfaces.tobago.component.UISheet;
+import org.apache.myfaces.tobago.internal.config.AbstractTobagoTestBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.List;
+
+public class SortingUtilsUnitTest extends AbstractTobagoTestBase {
+
+    @Test
+    public void testSheetValueNull() {
+        final UISheet sheet = new UISheet();
+        final UIColumn column = new UIColumn();
+        sheet.getChildren().add(column);
+        SortingUtils.sort(sheet, null);
+    }
+
+    @Test
+    public void testNoChild() {
+        final List<Fruit> list = Fruit.getFreshFruits();
+
+        final UISheet sheet = new UISheet();
+        sheet.getState().setSortedColumnId("id");
+        final UIColumn column = new UIColumn();
+        column.setId("id");
+        sheet.getChildren().add(column);
+        sheet.setValue(list);
+        SortingUtils.sort(sheet, null);
+
+        // not sorted
+        Assert.assertEquals(Apple.GOLDEN_DELICIOUS, list.get(0));
+        Assert.assertEquals(Apple.SCHOENER_AUS_BOSKOOP, list.get(1));
+        Assert.assertEquals(Pear.WILLIAMS_CHRIST, list.get(2));
+        Assert.assertEquals(Pear.KOESTLICHE_AUS_CHARNEUX, list.get(3));
+    }
+
+    @Test
+    public void testUIOut() {
+        final List<Fruit> list = Fruit.getFreshFruits();
+        final UISheet sheet = new UISheet();
+        sheet.getState().setSortedColumnId("id");
+        sheet.setVar("var");
+        final UIColumn column = new UIColumn();
+        column.setId("id");
+        sheet.getChildren().add(column);
+        sheet.setValue(list);
+        final UIOut out = new UIOut();
+        column.getChildren().add(out);
+        out.setValueExpression(Attributes.value.getName(),
+            new MockValueExpression("#{var.name}", String.class));
+        Assert.assertNotNull(out.getValueExpression(Attributes.value.getName()));
+
+        SortingUtils.sort(sheet, null);
+
+        Assert.assertEquals(Apple.GOLDEN_DELICIOUS, list.get(0));
+        Assert.assertEquals(Pear.KOESTLICHE_AUS_CHARNEUX, list.get(1));
+        Assert.assertEquals(Apple.SCHOENER_AUS_BOSKOOP, list.get(2));
+        Assert.assertEquals(Pear.WILLIAMS_CHRIST, list.get(3));
+
+        sheet.getState().setAscending(false);
+        SortingUtils.sort(sheet, null);
+
+        Assert.assertEquals(Pear.WILLIAMS_CHRIST, list.get(0));
+        Assert.assertEquals(Apple.SCHOENER_AUS_BOSKOOP, list.get(1));
+        Assert.assertEquals(Pear.KOESTLICHE_AUS_CHARNEUX, list.get(2));
+        Assert.assertEquals(Apple.GOLDEN_DELICIOUS, list.get(3));
+    }
+
+    @Test
+    public void testUILink() {
+        final List<Fruit> list = Fruit.getFreshFruits();
+        final UISheet sheet = new UISheet();
+        sheet.getState().setSortedColumnId("id");
+        final UIColumn column = new UIColumn();
+        column.setId("id");
+        sheet.getChildren().add(column);
+        sheet.setValue(list);
+        final UILink link = new UILink();
+        column.getChildren().add(link);
+        link.setValueExpression(Attributes.label.getName(),
+            new MockValueExpression("#{var.name}", String.class));
+
+        Assert.assertNotNull(link.getValueExpression(Attributes.label.getName()));
+
+        SortingUtils.sort(sheet, null);
+
+        Assert.assertEquals(Apple.GOLDEN_DELICIOUS, list.get(0));
+        Assert.assertEquals(Apple.SCHOENER_AUS_BOSKOOP, list.get(1));
+        Assert.assertEquals(Pear.WILLIAMS_CHRIST, list.get(2));
+        Assert.assertEquals(Pear.KOESTLICHE_AUS_CHARNEUX, list.get(3));
+    }
+
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/util/BeanComparatorUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/util/BeanComparatorUnitTest.java
index 038a397..8f79ed3 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/util/BeanComparatorUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/util/BeanComparatorUnitTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.myfaces.tobago.util;
 
+import org.apache.myfaces.tobago.internal.util.Fruit;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -33,11 +34,7 @@ public class BeanComparatorUnitTest {
 
   @Test
   public void testComparingInstancesOfDifferentClasses() {
-    final List<Fruit> original = new ArrayList<>();
-    original.add(new Apple("Golden Delicious"));
-    original.add(new Apple("Schöner aus Boskoop"));
-    original.add(new Pear("Williams Christ"));
-    original.add(new Pear("Köstliche aus Charneux"));
+    final List<Fruit> original = Fruit.getFreshFruits();
 
     final BeanComparator ascendingComparator = new BeanComparator("name", null, false);
     final List<Fruit> ascending = new ArrayList<>(original);
@@ -57,57 +54,4 @@ public class BeanComparatorUnitTest {
     Assert.assertEquals("#2", original.get(3), descending.get(2));
     Assert.assertEquals("#3", original.get(0), descending.get(3));
   }
-
-  public interface Fruit {
-
-    String getName();
-
-    void setName(String name);
-  }
-
-  public static class Apple implements Fruit {
-
-    private String name;
-
-    public Apple(String name) {
-      this.name = name;
-    }
-
-    @Override
-    public String getName() {
-      return name;
-    }
-
-    public void setName(String name) {
-      this.name = name;
-    }
-
-    @Override
-    public String toString() {
-      return getName();
-    }
-  }
-
-  public static class Pear implements Fruit {
-
-    private String name;
-
-    public Pear(String name) {
-      this.name = name;
-    }
-
-    @Override
-    public String getName() {
-      return name;
-    }
-
-    public void setName(String name) {
-      this.name = name;
-    }
-
-    @Override
-    public String toString() {
-      return getName();
-    }
-  }
 }