Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix #76, #77, #78, #79

- add sortArray and sortList to SortContext for backwards support (marked deprecated)
- cleaned up SortContext for consistency and performance
- add HTMLContext for HTML-based pagination support for legacy support
- updated BeanAnalzyer to attempt to fix bridge method issues
- add missing messages in type checker properties
  • Loading branch information...
commit d81feffad7c7c92f9dd11ef0681d2de2f2e6068f 1 parent c787619
@nicholashagen authored
View
4 tea/src/main/java/org/teatrove/tea/compiler/TypeChecker.java
@@ -1550,7 +1550,7 @@ protected Expression checkForTypeExpression(Expression expr) {
}
// otherwise, fail with unknown variable
- error("variable.not.declared", expr);
+ error("variable.not.declared", name, expr);
return null;
}
}
@@ -1619,7 +1619,7 @@ else if (expr instanceof Lookup) {
}
// otherwise, fail with unknown variable
- error("variable.not.declared", expr);
+ error("variable.not.declared", name, expr);
return null;
}
}
View
73 tea/src/main/java/org/teatrove/tea/util/BeanAnalyzer.java
@@ -69,19 +69,6 @@
createPropertiesCache() {
return Collections.synchronizedMap(new IdentityMap(32768));
}
-
- /**
- * Test program.
- */
- public static void main(String[] args) throws Exception {
- Map<String, PropertyDescriptor> map = getAllProperties(new GenericType(Class.forName(args[0])));
- Iterator<String> keys = map.keySet().iterator();
- while (keys.hasNext()) {
- String key = (String)keys.next();
- PropertyDescriptor desc = (PropertyDescriptor)map.get(key);
- System.out.println(key + " = " + desc);
- }
- }
/**
* A function that returns a Map of all the available properties on
@@ -157,7 +144,59 @@ public static void main(String[] args) throws Exception {
for (int i=0; i<length; i++) {
PropertyDescriptor desc = pdArray[i];
// This check will discard standard pure indexed properties.
- if (desc.getPropertyType() != null) {
+ Class<?> propertyType = desc.getPropertyType();
+ if (propertyType != null) {
+ boolean modified = false;
+
+ // check if read method is bridge and lookup real method
+ Method readMethod = desc.getReadMethod();
+ if (readMethod != null && readMethod.isBridge()) {
+ try {
+ readMethod = clazz.getMethod(
+ readMethod.getName()
+ );
+
+ modified = true;
+ propertyType = readMethod.getReturnType();
+ }
+ catch (NoSuchMethodException nsme) {
+ // ignore and use existing method
+ }
+ catch (Exception e) {
+ throw new IntrospectionException(e.toString());
+ }
+ }
+
+ // check if write method is bridge and lookup real method
+ Method writeMethod = desc.getWriteMethod();
+ if (writeMethod != null && writeMethod.isBridge()) {
+ try {
+ writeMethod = clazz.getMethod(
+ writeMethod.getName(), propertyType
+ );
+
+ modified = true;
+ }
+ catch (NoSuchMethodException nsme) {
+ // ignore and use existing method
+ }
+ catch (Exception e) {
+ throw new IntrospectionException(e.toString());
+ }
+ }
+
+ // overwrite the methods if they were modified...note that
+ // we must set writeMethod to null first since invoking the
+ // setter will attempt to vaildate the types are the same
+ // which may not be the case if the read method was setup
+ // incorrectly with its bridge type
+ if (modified) {
+ desc.setWriteMethod(null);
+ desc.setReadMethod(readMethod);
+ desc.setWriteMethod(writeMethod);
+ }
+
+ // save the property
properties.put(desc.getName(), desc);
}
}
@@ -326,14 +365,14 @@ else if (String.class.isAssignableFrom(clazz)) {
properties.put(KEYED_PROPERTY_NAME, keyed);
int size = keyedMethods.size();
- keyed.setKeyedReadMethods
- ((Method[])keyedMethods.toArray(new Method[size]));
+ keyed.setKeyedReadMethods(
+ keyedMethods.toArray(new Method[size]));
}
// Filter out properties with names that contain '$' characters.
Iterator<String> it = properties.keySet().iterator();
while (it.hasNext()) {
- String propertyName = (String)it.next();
+ String propertyName = it.next();
if (propertyName.indexOf('$') >= 0) {
it.remove();
}
View
2  tea/src/main/resources/org/teatrove/tea/compiler/resources/TypeChecker.properties
@@ -17,6 +17,8 @@ variable.declared=\
Variable ''{0}'' already declared
variable.declared.here=\
Variable ''{0}'' declared here
+variable.not.declared=\
+ Variable ''{0}'' not declared
variable.undefined=\
Internal error: variable ''{0}'' undefined
variable.primitive.uninitialized=\
View
9 tea/src/test/java/org/teatrove/tea/templates/VarargsTest.java
@@ -29,6 +29,7 @@ public void testSpread() throws Exception {
assertEquals("11", executeTest(TEST_SOURCE_11));
assertEquals("44", executeTest(TEST_SOURCE_12));
assertEquals("11", executeTest(TEST_SOURCE_13));
+ assertEquals("java.lang.Integer", executeTest(TEST_SOURCE_14));
}
protected String executeTest(String source) throws Exception {
@@ -37,6 +38,11 @@ protected String executeTest(String source) throws Exception {
}
public static class VarargsContext {
+
+ public Number getRandom() {
+ return Integer.valueOf(1);
+ }
+
public String doSomething(String test, Object... blah) {
return "1";
}
@@ -124,4 +130,7 @@ public VarargsContext getContext() {
protected static final String TEST_SOURCE_13 =
"doSomething('test', 5.2, 2.3, 2.9, 'test');" +
"getContext()?.doSomething('test', 5.2, 2.3, 2.9, 'test');"; // 1
+
+ protected static final String TEST_SOURCE_14 =
+ "a = getRandom(); b = getRandom(); c = a + b; c.class.name";
}
View
49 tea/src/test/java/org/teatrove/tea/util/BeanAnalyzerTest.java
@@ -0,0 +1,49 @@
+package org.teatrove.tea.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+
+import org.junit.Test;
+import org.teatrove.trove.generics.GenericType;
+
+public class BeanAnalyzerTest {
+
+ @Test
+ public void testCovariants() throws Exception {
+ Map<String, PropertyDescriptor> properties =
+ BeanAnalyzer.getAllProperties(new GenericType(Int.class));
+ PropertyDescriptor value = properties.get("value");
+ assertEquals(Integer.class, value.getPropertyType());
+ assertEquals(Integer.class, value.getReadMethod().getReturnType());
+ }
+
+ @Test
+ public void testGenerics() throws Exception {
+ Map<String, PropertyDescriptor> properties =
+ BeanAnalyzer.getAllProperties(new GenericType(IntGenerics.class));
+ PropertyDescriptor value = properties.get("value");
+ assertEquals(Integer.class, value.getPropertyType());
+ assertEquals(Integer.class, value.getReadMethod().getReturnType());
+ assertEquals(Integer.class, value.getWriteMethod().getParameterTypes()[0]);
+ }
+
+ public static class Base {
+ public Number getValue() { return null; }
+ }
+
+ public static class Int extends Base {
+ public Integer getValue() { return null; }
+ }
+
+ public static class BaseGenerics<T extends Number> {
+ public T getValue() { return null; }
+ public void setValue(T value) { }
+ }
+
+ public static class IntGenerics extends BaseGenerics<Integer> {
+ public Integer getValue() { return null; }
+ public void setValue(Integer value) { }
+ }
+}
View
19 teaapps/src/main/java/org/teatrove/teaapps/contexts/ArrayContext.java
@@ -24,7 +24,24 @@
* @author Nicholas Hagen
*/
public class ArrayContext {
-
+
+ /**
+ * Clone the given array into a new instance of the same type and
+ * dimensions. The values are directly copied by memory, so this is not
+ * a deep clone operation. However, manipulation of the contents of the
+ * array will not impact the given array.
+ *
+ * @param array The array to clone
+ *
+ * @return The new cloned array
+ */
+ public Object[] cloneArray(Object[] array) {
+ Class<?> clazz = array.getClass().getComponentType();
+ Object newArray = Array.newInstance(clazz, array.length);
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ return (Object[]) newArray;
+ }
+
/**
* Determine whether the provided object is an array or not. This returns
* <code>true</code> if the object is non-<code>null</code> and the class
View
197 teaapps/src/main/java/org/teatrove/teaapps/contexts/HTMLContext.java
@@ -0,0 +1,197 @@
+package org.teatrove.teaapps.contexts;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper context that provides the ability to manipulate and trim strings based
+ * on HTML tags. This is a useful context in generating stories or short text
+ * snippets that may contain HTML to help adjust the pagination, truncation,
+ * etc.
+ */
+public class HTMLContext {
+
+ protected static final Pattern HTML_TAGS =
+ Pattern.compile("<(.|\n)+?>", Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Get the character count of the given text excluding any HTML tags. This
+ * will first strip any HTML tags from the given string and then return the
+ * number of characters in the result. For example, the input string
+ * <code>&lt;em&gt;Hello&lt;/em&gt; World</code> would return 11 characters
+ * as the <code>em</code> tags would be stripped.
+ *
+ * @param text The text to evaluate
+ *
+ * @return The number of characters in the string excluding HTML tags
+ */
+ public int getCharCount(String text) {
+ String result = text.trim();
+ Matcher matcher = HTML_TAGS.matcher(result);
+ result = matcher.replaceAll("");
+ return result.length();
+ }
+
+ /**
+ * Paginate the given text input so that each page contains no more than the
+ * given maximum number of characters. The length of each page is based on
+ * the text only ignoring any HTML tags.
+ *
+ * @param text The text to paginate against
+ * @param maxChars The maximum characters per page
+ * @param splitValue The split characters for splitting on page breaks
+ * @param HTMLBalanceTags Set of tags to ensure are opened/closed properly
+ *
+ * @return The array of strings per page
+ *
+ * @see #getCharCount(String)
+ */
+ public String[] getPagination(String text, int maxChars, String splitValue,
+ String[] HTMLBalanceTags) {
+ int count;
+ int lastSplitCount;
+ String tempHolder = " ";
+ List<String> list = new ArrayList<String>();
+ boolean secondRunThrough = false;
+
+ // if the story will not be broken up because it's too small,
+ // just return it without doing all the logic.
+ if ((getCharCount(text) <= maxChars)) {
+ list.add(text);
+ return list.toArray(new String[list.size()]);
+ }
+
+ //JVM 1.4 version
+ //String[] splits = text.split(splitValue);
+ //Will need an HTMLBalance function if implemented.
+
+ //JVM 1.3 version
+ String[] splits = split(text, splitValue, HTMLBalanceTags);
+
+ int len = splits.length;
+ for (int i = 0; i < len;i++) {
+ //Check to see if last page would not be at least half filled.
+ // If so, add to previous page.
+ if (i == len-2){
+ lastSplitCount = getCharCount(splits[i+1]);
+ if (lastSplitCount <= maxChars/2) {
+ splits[i] = splits[i]+" "+splits[i+1];
+ list.add(tempHolder+splits[i]);
+ return list.toArray(new String[list.size()]);
+ }
+ }
+
+ if (secondRunThrough){
+ count = getCharCount(tempHolder+splits[i]);
+ } else{
+ count = getCharCount(splits[i]);
+ }
+
+ if (count >= maxChars || i+1 == len) {
+ list.add(tempHolder+splits[i]);
+ secondRunThrough = false;
+ tempHolder = " ";
+ }
+ else{
+ tempHolder = tempHolder+splits[i];
+ secondRunThrough = true;
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Split the given string around the given split value. This will also
+ * ensure any of the given HTML tags are properly closed.
+ *
+ * @param text The text value to split
+ * @param splitValue The values to be split on
+ * @param HTMLBalanceTags The HTML tags to ensure are closed properly
+ *
+ * @return The array of string values per the splitting
+ */
+ public String[] split(String text, String splitValue,
+ String[] HTMLBalanceTags) {
+
+ String restOfText = text;
+ int nextBreakIndex = 0;
+ List<String> list = new ArrayList<String>();
+ int splitValueLength = splitValue.length();
+
+ while (restOfText.length() > 0) {
+ nextBreakIndex = restOfText.indexOf(splitValue);
+ if (nextBreakIndex < 0) {
+ list.add(restOfText);
+ break;
+ }
+
+ list.add(restOfText.substring(0, nextBreakIndex+splitValueLength));
+ restOfText = restOfText.substring(nextBreakIndex+splitValueLength);
+ }
+
+ // This code makes sure that no ending HTML </> tags are left out
+ // causing malfomed pages.
+ if (HTMLBalanceTags.length > 0) {
+ List<String> balancedList = new ArrayList<String>();
+ for (int t = 0; t < HTMLBalanceTags.length; t++) {
+ // first tag pass through
+ if (t < 1) {
+ balancedList = getBalancedList(HTMLBalanceTags[t], list);
+ }
+ else if (balancedList.size() > 1) {
+ // after the first pass through keep trying on each
+ // subsequent tag.
+ balancedList =
+ getBalancedList(HTMLBalanceTags[t], balancedList);
+ }
+ }
+
+ return balancedList.toArray(new String[balancedList.size()]);
+ }
+ else {
+ return list.toArray(new String[list.size()]);
+ }
+ }
+
+ protected List<String> getBalancedList(String tag, List<String> list) {
+ int e = -1;
+ int len = list.size();
+ List<String> balancedList = new ArrayList<String>();
+ for (int i = 0; i < len; i++) {
+ String temp = list.get(i);
+ //see if the element has the begin tag starting from right to left.
+ if ((e = temp.lastIndexOf(tag)) > -1) {
+ //If so, check to see if there is a matching end tag starting
+ //at the index above.
+ String endTag = "</" + tag.substring(1, tag.length()) + ">";
+ if (temp.indexOf(endTag, e) < 0) {
+ //if no matching end tag, keep looping through the
+ //arrayElement and add it.
+ int ii = 1;
+ while (temp.indexOf(endTag) < 0) {
+ temp = temp+" "+list.get(i+1);
+ list.remove(1);
+ len = len-ii;
+ ++ii;
+ }
+
+ balancedList.add(temp);
+ }
+ else {
+ balancedList.add(temp);
+ }
+ }
+ else {
+ if (!temp.equals("</p><p>")) {
+ balancedList.add(temp);
+ }
+ }
+ }
+
+ return balancedList;
+ }
+
+}
View
32 teaapps/src/main/java/org/teatrove/teaapps/contexts/ListContext.java
@@ -286,6 +286,38 @@ public int size(List<?> list) {
}
/**
+ * Create a new {@link ArrayList} instance containing all elements of the
+ * given list. This is not a deep clone operation, so the underlying
+ * elements are copied by memory and not cloned.
+ *
+ * @param list The list to copy
+ *
+ * @return The newly created list
+ */
+ public <T> List<T> cloneList(List<T> list) {
+ return new ArrayList<T>(list);
+ }
+
+ /**
+ * Create a new {@link ArrayList} instance adding all values of the given
+ * lists together. The second list will essentially be added after the
+ * first list. Note that any duplicates between lists will remain as
+ * duplicates.
+ *
+ * @param list1 The first list to merge
+ * @param list2 The second list to merge
+ *
+ * @return The newly created list containing the merged lists
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public List<?> mergeLists(List<?> list1, List<?> list2) {
+ List list = new ArrayList(list1.size() + list2.size());
+ list.addAll(list1);
+ list.addAll(list2);
+ return list;
+ }
+
+ /**
* If the elements of the list are arrays, get the max length of each needed
* dimension, otherwise return an int[] containing one element, the length
* of the list.
View
574 teaapps/src/main/java/org/teatrove/teaapps/contexts/SortContext.java
@@ -16,21 +16,40 @@
package org.teatrove.teaapps.contexts;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
import org.teatrove.trove.util.BeanComparator;
/**
- * Tea context that provides the ability to sort arrays.
+ * Tea context that provides the ability to sort arrays and lists.
*/
public class SortContext {
+ private static final Comparator<String> ISTRING_COMPARATOR_ASC =
+ new StringComparator(true);
+
+ private static final Comparator<String> ISTRING_COMPARATOR_DESC =
+ Collections.reverseOrder(ISTRING_COMPARATOR_ASC);
+
+ private static final Comparator<Object> TOSTRING_COMPARATOR_ASC =
+ new ToStringComparator();
+
+ private static final Comparator<Object> TOSTRING_COMPARATOR_DESC =
+ Collections.reverseOrder(TOSTRING_COMPARATOR_ASC);
+
/**
* Sort the given array by using the given property to evaluate against
* each element in the array comparing the resulting values. For example,
* if the object array contained User instances that had a lastName
* property, then you could sort the array by last name via:
- * <code>sort(users, 'lastName', false)</code>.
+ * <code>sort(users, 'lastName', false)</code>. Note that the determination
+ * of the array type is based purely on the first non-<code>null</code>
+ * element in the array and all other instances in the array are expected to
+ * conform to the same type. Use
+ * {@link #sort(Object[], Class, String, boolean)} to explicitly provide the
+ * underlying array type.
*
* @param array The array to sort
* @param property The name of the property on the elements to sort against
@@ -44,6 +63,73 @@ public void sort(Object[] array, String property, boolean reverse) {
}
/**
+ * Sort the given list by using the given property to evaluate against
+ * each element in the list comparing the resulting values. For example,
+ * if the object list contained User instances that had a lastName
+ * property, then you could sort the list by last name via:
+ * <code>sort(users, 'lastName', false)</code>. Note that the determination
+ * of the list type is based purely on the first non-<code>null</code>
+ * element in the list and all other instances in the list are expected to
+ * conform to the same type. Use
+ * {@link #sort(List, Class, String, boolean)} to explicitly provide the
+ * underlying type.
+ *
+ * @param list The list to sort
+ * @param property The name of the property on the elements to sort against
+ * @param reverse The state of whether to reverse the order
+ */
+ public void sort(List<?> list, String property, boolean reverse) {
+ Class<?> type = getObjectClass(list);
+ if (type != null) {
+ sort(list, type, property, reverse);
+ }
+ }
+
+ /**
+ * Sort the given array by using the given property to evaluate against
+ * each element in the array comparing the resulting values. For example,
+ * if the object array contained User instances that had a lastName
+ * property, then you could sort the array by last name via:
+ * <code>sort(users, 'lastName', false)</code>. This ensures that each
+ * element in the array is of the given type and uses the given type to
+ * perform the property lookup and evaluation.
+ *
+ * @param array The array to sort
+ * @param arrayType The type of elements in the array
+ * @param property The name of the property on the elements to sort against
+ * @param reverse The state of whether to reverse the order
+ */
+ @SuppressWarnings("unchecked")
+ public void sort(Object[] array, Class<?> arrayType,
+ String property, boolean reverse) {
+ BeanComparator comparator =
+ newBeanComparator(arrayType, property, reverse);
+ Arrays.sort(array, comparator);
+ }
+
+ /**
+ * Sort the given list by using the given property to evaluate against
+ * each element in the list comparing the resulting values. For example,
+ * if the object list contained User instances that had a lastName
+ * property, then you could sort the list by last name via:
+ * <code>sort(users, 'lastName', false)</code>. This ensures that each
+ * element in the list is of the given type and uses the given type to
+ * perform the property lookup and evaluation.
+ *
+ * @param list The list to sort
+ * @param type The type of elements in the array
+ * @param property The name of the property on the elements to sort against
+ * @param reverse The state of whether to reverse the order
+ */
+ @SuppressWarnings("unchecked")
+ public void sort(List<?> list, Class<?> type,
+ String property, boolean reverse) {
+ BeanComparator comparator =
+ newBeanComparator(type, property, reverse);
+ Collections.sort(list, comparator);
+ }
+
+ /**
* Sort the given array by using the given properties to evaluate against
* each element in the array comparing the resulting values. If multiple
* values match the first property, then the second property is used,
@@ -53,13 +139,18 @@ public void sort(Object[] array, String property, boolean reverse) {
* <code>sort(users, #('lastName', 'firstName'), #(false, false))</code>.
* The array of reverse flags correspond to each property so that you could
* sort one property ascendingly and another descendingly. The two arrays
- * <em>must</em> be the same length.
+ * <em>must</em> be the same length. Note that the determination
+ * of the array type is based purely on the first non-<code>null</code>
+ * element in the array and all other instances in the array are expected to
+ * conform to the same type. Use
+ * {@link #sort(Object[], Class, String[], boolean[])} to explicitly provide
+ * the underlying array type.
*
* @param array The array to sort
* @param properties The name of the properties on the elements to sort with
* @param reverse The states of whether to reverse the orders
*/
- public void sort(Object[] array, String[] properties, boolean[] reverse) {
+ public void sort(Object[] array, String[] properties, boolean[] reverse) {
Class<?> arrayType = getObjectClass(array);
if (arrayType != null) {
sort(array, arrayType, properties, reverse);
@@ -67,7 +158,89 @@ public void sort(Object[] array, String[] properties, boolean[] reverse) {
}
/**
- * Sort the given array of strings. If the ignoreCase flag is
+ * Sort the given list by using the given properties to evaluate against
+ * each element in the list comparing the resulting values. If multiple
+ * values match the first property, then the second property is used,
+ * and so on. For example, if the object list contained User instances that
+ * had a lastName and firstName property, then you could sort the list by
+ * last name followed by first name via:
+ * <code>sort(users, #('lastName', 'firstName'), #(false, false))</code>.
+ * The array of reverse flags correspond to each property so that you could
+ * sort one property ascendingly and another descendingly. The two arrays
+ * <em>must</em> be the same length. Note that the determination
+ * of the list type is based purely on the first non-<code>null</code>
+ * element in the list and all other instances in the list are expected to
+ * conform to the same type. Use
+ * {@link #sort(List, Class, String[], boolean[])} to explicitly provide
+ * the underlying list type.
+ *
+ * @param list The list to sort
+ * @param properties The name of the properties on the elements to sort with
+ * @param reverse The states of whether to reverse the orders
+ */
+ public void sort(List<?> list, String[] properties, boolean[] reverse) {
+ Class<?> type = getObjectClass(list);
+ if (type != null) {
+ sort(list, type, properties, reverse);
+ }
+ }
+
+ /**
+ * Sort the given array by using the given properties to evaluate against
+ * each element in the array comparing the resulting values. If multiple
+ * values match the first property, then the second property is used,
+ * and so on. For example, if the object array contained User instances that
+ * had a lastName and firstName property, then you could sort the array by
+ * last name followed by first name via:
+ * <code>sort(users, #('lastName', 'firstName'), #(false, false))</code>.
+ * The array of reverse flags correspond to each property so that you could
+ * sort one property ascendingly and another descendingly. The two arrays
+ * <em>must</em> be the same length. This also ensures that each element in
+ * the array is of the given type and uses the given type to perform the
+ * property lookup and evaluation.
+ *
+ * @param array The array to sort
+ * @param arrayType The type of elements in the array
+ * @param properties The name of the properties on the elements to sort with
+ * @param reverse The states of whether to reverse the orders
+ */
+ @SuppressWarnings("unchecked")
+ public void sort(Object[] array, Class<?> arrayType,
+ String[] properties, boolean[] reverse) {
+ BeanComparator comparator =
+ newBeanComparator(arrayType, properties, reverse);
+ Arrays.sort(array, comparator);
+ }
+
+ /**
+ * Sort the given list by using the given properties to evaluate against
+ * each element in the list comparing the resulting values. If multiple
+ * values match the first property, then the second property is used,
+ * and so on. For example, if the object list contained User instances that
+ * had a lastName and firstName property, then you could sort the list by
+ * last name followed by first name via:
+ * <code>sort(users, #('lastName', 'firstName'), #(false, false))</code>.
+ * The array of reverse flags correspond to each property so that you could
+ * sort one property ascendingly and another descendingly. The two arrays
+ * <em>must</em> be the same length. This also ensures that each element in
+ * the list is of the given type and uses the given type to perform the
+ * property lookup and evaluation.
+ *
+ * @param array The array to sort
+ * @param type The type of elements in the array
+ * @param properties The name of the properties on the elements to sort with
+ * @param reverse The states of whether to reverse the orders
+ */
+ @SuppressWarnings("unchecked")
+ public void sort(List<?> list, Class<?> type,
+ String[] properties, boolean[] reverse) {
+ BeanComparator comparator =
+ newBeanComparator(type, properties, reverse);
+ Collections.sort(list, comparator);
+ }
+
+ /**
+ * Sort the given array of strings naturally. If the ignoreCase flag is
* <code>true</code>, then case will not be used during sorting so that
* 'abc' and 'ABC' are equivalent.
*
@@ -76,40 +249,74 @@ public void sort(Object[] array, String[] properties, boolean[] reverse) {
* @param ignoreCase The state of whether to ignore the casing of strings
*/
public void sort(String[] array, boolean reverse, boolean ignoreCase) {
- StringComparator comparator = new StringComparator(reverse, ignoreCase);
- Arrays.sort(array, comparator);
+ if (ignoreCase) {
+ if (reverse) { Arrays.sort(array, ISTRING_COMPARATOR_DESC); }
+ else { Arrays.sort(array, ISTRING_COMPARATOR_ASC); }
+ }
+ else {
+ if (reverse) { Arrays.sort(array, Collections.reverseOrder()); }
+ else { Arrays.sort(array); }
+ }
}
/**
- * Sort the given array according to the natural ordering of the array. If
- * the array contains string values, a standard string comparator is used.
- * If the array contains values that implement {@link Comparable}, then the
- * natural ordering of the implementation is used. Otherwise, the array is
- * not sorted.
+ * Sort the given array according to either the natural ordering of the
+ * array or using a generic string comparison by converting each element to
+ * its string format via {@link Object#toString() toString}.
*
* @param array The array to sort
- * @param sortAscending The flag of whether to sort ascending or not
+ * @param reverse The state of whether to reverse the sort
*/
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public void sort(Object[] array, boolean sortAscending) {
- Class<?> arrayType = getObjectClass(array);
- if (arrayType != null) {
- Comparator comparator = null;
- if (String.class.equals(arrayType)) {
- comparator = new StringComparator(sortAscending, false);
- } else if (Comparable.class.isAssignableFrom(arrayType)) {
- comparator = new GenericComparator(sortAscending);
- }
- if (comparator != null) {
- Arrays.sort(array, comparator);
- } else {
- System.err.println("Sorting arrays of type " +
- arrayType.getName() + " is not supported, " +
- "must implement Comparable."
- );
- }
- } else {
- System.err.println("Could not determine type of array to sort.");
+ public void sort(Object[] array, boolean reverse) {
+
+ // handle purely comparable arrays
+ if (array instanceof Comparable[]) {
+ if (reverse) {
+ Arrays.sort(array, Collections.reverseOrder());
+ }
+ else {
+ Arrays.sort(array);
+ }
+ }
+
+ // otherwise, use general toString comparator
+ else {
+ if (reverse) {
+ Arrays.sort(array, TOSTRING_COMPARATOR_DESC);
+ }
+ else {
+ Arrays.sort(array, TOSTRING_COMPARATOR_ASC);
+ }
+ }
+ }
+
+ /**
+ * Sort the given list according to either the natural ordering of the
+ * elements in the list or using a generic string comparison by converting
+ * each element to its string format via {@link Object#toString() toString}.
+ * If all elements in the list are {@link Comparable comparable}, then the
+ * natural ordering will be used. Otherwise, the generic comparison will be
+ * performed.
+ *
+ * @param list The list to sort
+ * @param reverse The state of whether to reverse the sort
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <T> void sort(List<T> list, boolean reverse) {
+ Class<?> type = getObjectClass(list);
+ if (Comparable.class.isAssignableFrom(type)) {
+ if (reverse) {
+ Collections.sort(list, Collections.reverseOrder());
+ }
+ else { Collections.sort((List) list); }
+ }
+ else {
+ if (reverse) {
+ Collections.sort(list, TOSTRING_COMPARATOR_DESC);
+ }
+ else {
+ Collections.sort(list, TOSTRING_COMPARATOR_ASC);
+ }
}
}
@@ -125,6 +332,17 @@ public void sortAscending(Object[] array) {
}
/**
+ * Sort the given list naturally.
+ *
+ * @param list The list to sort
+ *
+ * @see Collections#sort(List)
+ */
+ public <T extends Comparable<? super T>> void sortAscending(List<T> list) {
+ Collections.sort(list);
+ }
+
+ /**
* Sort the given array naturally.
*
* @param array The array to sort
@@ -189,33 +407,44 @@ public void sortAscending(short[] array) {
public void sortAscending(long[] array) {
Arrays.sort(array);
}
+
+ private static BeanComparator newBeanComparator(Class<?> type) {
+ BeanComparator comparator = BeanComparator.forClass(type);
+ comparator.using(TOSTRING_COMPARATOR_ASC);
+ return comparator;
+ }
- @SuppressWarnings("unchecked")
- private void sort(Object[] array, Class<?> arrayType,
- String property, boolean reverse) {
- BeanComparator comparator = BeanComparator.forClass(arrayType);
+ private static BeanComparator
+ newBeanComparator(Class<?> type, String property, boolean reverse) {
+
+ BeanComparator comparator = newBeanComparator(type);
+
if (property != null && !property.equals("")) {
comparator = comparator.orderBy(property);
- }
- if (reverse) {
+ }
+
+ if (reverse) {
comparator = comparator.reverse();
}
- Arrays.sort(array, comparator);
- }
-
- @SuppressWarnings("unchecked")
- private void sort(Object[] array, Class<?> arrayType,
- String[] properties, boolean[] reverse) {
- BeanComparator comparator = BeanComparator.forClass(arrayType);
+
+ return comparator;
+ }
+
+ private static BeanComparator
+ newBeanComparator(Class<?> type, String[] properties, boolean[] reverse) {
+
+ BeanComparator comparator = newBeanComparator(type);
+
for (int i = 0; i < properties.length; i++) {
comparator = comparator.orderBy(properties[i]);
- if (reverse[i] == true) {
+ if (reverse[i]) {
comparator = comparator.reverse();
}
}
- Arrays.sort(array, comparator);
- }
+ return comparator;
+ }
+
private Class<?> getObjectClass(Object[] array) {
Class<?> result = null;
if (array != null) {
@@ -229,63 +458,212 @@ private void sort(Object[] array, Class<?> arrayType,
return result;
}
- @SuppressWarnings("rawtypes")
- public class GenericComparator implements Comparator<Comparable> {
-
- protected boolean sortAscending = true;
-
- public GenericComparator(boolean sortAscending) {
- this.sortAscending = sortAscending;
- }
-
- @SuppressWarnings("unchecked")
- public int compare(Comparable k1, Comparable k2) {
- if (k1 != null) {
- if (k2 != null) {
- int result = k1.compareTo(k2);
- return (sortAscending) ? result: -result;
- } else {
- return (sortAscending) ? 1: -1;
- }
- } else if (k2 != null) {
- return (sortAscending) ? -1: 1;
- }
- return 0;
- }
+ private Class<?> getObjectClass(List<?> list) {
+ Class<?> result = null;
+ if (list != null) {
+ for (Object element : list) {
+ if (element != null) {
+ result = element.getClass();
+ break;
+ }
+ }
+ }
+
+ return result;
}
- public class StringComparator implements Comparator<String> {
+ /**
+ * Special comparator used to support comparing strings while also ignoring
+ * case.
+ */
+ public static class StringComparator implements Comparator<String> {
- protected boolean sortAscending = true;
- protected boolean ignoreCase = true;
+ protected boolean ignoreCase;
- public StringComparator(boolean sortAscending, boolean ignoreCase) {
- this.sortAscending = sortAscending;
+ public StringComparator(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
}
public int compare(String s1, String s2) {
- if (s1 != null) {
- if (s2 != null) {
- int flag = 0;
- if (ignoreCase) {
- flag = s1.compareToIgnoreCase(s2);
- } else {
- flag = s1.compareTo(s2);
- }
- if (flag > 0) {
- return (sortAscending) ? 1: -1;
- }
- if (flag < 0) {
- return (sortAscending) ? -1: 1;
- }
- } else {
- return (sortAscending) ? 1: -1;
- }
- } else if (s2 != null) {
- return (sortAscending) ? -1: 1;
- }
- return 0;
+ if (s1 == null || s2 == null) {
+ if (s1 == null && s2 == null) { return 0; }
+ else if (s1 == null) { return -1; }
+ else { return 1; }
+ }
+ else if (ignoreCase) { return s1.compareToIgnoreCase(s2); }
+ else { return s1.compareTo(s2); }
}
}
+
+ /**
+ * Special comparator used to allow non-Comparable properties to be
+ * converted to strings and sorted accordingly.
+ */
+ public static class ToStringComparator implements Comparator<Object> {
+
+ @Override
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public int compare(Object object1, Object object2) {
+ if (object1 instanceof Comparable &&
+ object2 instanceof Comparable) {
+ return ((Comparable) object1).compareTo(object2);
+ }
+ else if (object1 == null && object2 == null) {
+ return 0;
+ }
+ else if (object1 != null && object2 != null) {
+ String string1 = object1.toString();
+ String string2 = object2.toString();
+ return compare(string1, string2);
+ }
+ else if (object1 == null) { return 1; }
+ else { return -1; }
+ }
+
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Object[], String, boolean)
+ */
+ @Deprecated
+ public void sortArray(Object[] array, String property, String reverse) {
+ sort(array, property, Boolean.parseBoolean(reverse));
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Object[], String, boolean)
+ */
+ @Deprecated
+ public void sortArray(Object[] array, String property, boolean reverse) {
+ sort(array, property, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Object[], Class, String, boolean)
+ */
+ @Deprecated
+ public void sortArray(Object[] array, Class<?> arrayType, String property,
+ boolean reverse) {
+ sort(array, arrayType, property, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Object[], Class, String[], boolean[])
+ */
+ @Deprecated
+ public void sortArray(Object[] array,
+ String[] properties, boolean[] reverse) {
+ sort(array, properties, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Object[], Class, String[], boolean[])
+ */
+ @Deprecated
+ public void sortArray(Object[] array, Class<?> arrayType,
+ String[] properties, boolean[] reverse) {
+ sort(array, arrayType, properties, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(String[], boolean)
+ */
+ @Deprecated
+ public void sortArray(String[] array, boolean reverse) {
+ sort(array, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(Integer[], boolean)
+ */
+ @Deprecated
+ public void sortArray(Integer[] array, boolean reverse) {
+ sort(array, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(int[], boolean)
+ */
+ @Deprecated
+ public void sortArray(int[] array, boolean reverse) {
+ sortAscending(array);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, boolean)
+ */
+ @Deprecated
+ public void sortList(List<?> list, boolean reverse) {
+ sort(list, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, String, boolean)
+ */
+ @Deprecated
+ public void sortList(List<?> list, String property, boolean reverse) {
+ sort(list, property, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, String, boolean)
+ */
+ @Deprecated
+ public void sortList(List<?> list, String property, String reverse) {
+ sort(list, property, Boolean.parseBoolean(reverse));
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, String[], boolean[])
+ */
+ @Deprecated
+ public void sortList(List<?> list, String[] properties, boolean[] reverse) {
+ sort(list, properties, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, Class, String, boolean)
+ */
+ @Deprecated
+ public void sortList(List<?> list, Class<?> type,
+ String property, boolean reverse) {
+ sortList(list, type, property, reverse);
+ }
+
+ /**
+ * Deprecated.
+ *
+ * @see #sort(List, Class, String[], boolean[])
+ */
+ @Deprecated
+ public void sortList(List<?> list, Class<?> type,
+ String[] properties, boolean[] reverse) {
+ sortList(list, type, properties, reverse);
+ }
}
View
3  teaapps/src/main/java/org/teatrove/teaapps/contexts/StringContext.java
@@ -15,12 +15,11 @@
*/
package org.teatrove.teaapps.contexts;
+import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
-import java.io.UnsupportedEncodingException;
-
/**
* Custom Tea context that provides helper methods when dealing with strings.
*/
View
7 teaapps/src/main/resources/META-INF/teaservlet.xml
@@ -150,6 +150,13 @@
<contextClass>org.teatrove.teaapps.contexts.TemplateExceptionContext</contextClass>
</init>
</TemplateExceptionApplication>
+
+ <HTMLApplication>
+ <class>org.teatrove.teaapps.apps.DefaultApplication</class>
+ <init>
+ <contextClass>org.teatrove.teaapps.contexts.HTMLContext</contextClass>
+ </init>
+ </HTMLApplication>
</applications>
Please sign in to comment.
Something went wrong with that request. Please try again.