You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by me...@apache.org on 2009/05/12 19:19:03 UTC

svn commit: r773987 - in /incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH: ./ etc/ src/com/ecyrd/jspwiki/ src/com/ecyrd/jspwiki/attachment/ src/com/ecyrd/jspwiki/dav/ src/com/ecyrd/jspwiki/plugin/ src/com/ecyrd/jspwiki/util/ src/com/ecyrd/jspwiki/util/co...

Author: metskem
Date: Tue May 12 17:19:02 2009
New Revision: 773987

URL: http://svn.apache.org/viewvc?rev=773987&view=rev
Log:
2.8.3-svn-7

        * JSPWIKI-526 Allow administrators to select the Comparator 
           used for sorting page lists by name, thanks to Greg Kable

Added:
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/PageSorter.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/CollatorComparator.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/HumanComparator.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/JavaNaturalComparator.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/LocaleComparator.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/PageSorterTest.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/AllTests.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/HumanComparatorTest.java
Modified:
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/ChangeLog
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/etc/jspwiki.properties.tmpl
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/PageTimeComparator.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/Release.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiEngine.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/AbstractReferralPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/IndexPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferredPagesPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferringPagesPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UndefinedPagesPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UnusedPagesPlugin.java
    incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/AllTests.java

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/ChangeLog?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/ChangeLog (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/ChangeLog Tue May 12 17:19:02 2009
@@ -1,3 +1,10 @@
+2009-05-12 Harry Metske <me...@apache.org>
+
+        * 2.8.3-svn-7
+
+        * JSPWIKI-526 Allow administrators to select the Comparator 
+           used for sorting page lists by name, thanks to Greg Kable
+        
 2009-05-11 Dirk Frederickx <di...@apache.org>
 
         * 2.8.3-svn-6

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/etc/jspwiki.properties.tmpl
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/etc/jspwiki.properties.tmpl?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/etc/jspwiki.properties.tmpl (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/etc/jspwiki.properties.tmpl Tue May 12 17:19:02 2009
@@ -40,6 +40,33 @@
 jspwiki.pageProvider = FileSystemProvider
 
 #
+# How to order pages in various lists and plugins.  By default, they are
+# ordered using Java's "natural" String order (purely character by character
+# comparison). This means that the pages named "Page1", "Page2" and "Page10"
+# appear in the order "Page1", "Page10", "Page2".
+#
+# To specify a different ordering, give the fully qualified name of a String
+# Comparator. If the class can't be found, an error will be logged and page
+# sorting will fall back to the default "natural" ordering. You can specify
+# any String Comparator class here BUT be careful of using case insensitive
+# comparators as JSPWiki page names may be case sensitive.
+# Example: jspwiki.pageNameComparator.class = my.fully.qualified.comparator
+#
+# For convenience, we have provided a few possibly useful comparators:
+#
+# HumanComparator understands numbers and sorts in a more human friendly
+# order (English only).  Using HumanComparator, the pages "Page1", "Page2",
+# "Page10" will sort in that order.
+# Example: jspwiki.pageNameComparator.class = HumanComparator
+#
+# LocaleComparator uses the server's Locale for ordering.  Useful if your
+# content is always in a language other than English.  Does not do human
+# friendly numeric ordering.
+# Example: jspwiki.pageNameComparator.class = LocaleComparator
+#
+#jspwiki.pageNameComparator.class =
+
+#
 #  Set to true, if you want to cache page data into memory.  This is
 #  in general a good idea.
 #

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/PageTimeComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/PageTimeComparator.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/PageTimeComparator.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/PageTimeComparator.java Tue May 12 17:19:02 2009
@@ -71,7 +71,7 @@
 
         if( timecomparison == 0 )
         {
-            return w1.getName().compareTo( w2.getName() );
+            return w1.compareTo( w2 );
         }
 
         return timecomparison;

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/Release.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/Release.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/Release.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/Release.java Tue May 12 17:19:02 2009
@@ -77,7 +77,7 @@
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "6";
+    public static final String     BUILD         = "7";
     
     /**
      *  This is the generic version string you should use

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiEngine.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiEngine.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiEngine.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiEngine.java Tue May 12 17:19:02 2009
@@ -71,6 +71,7 @@
 import com.ecyrd.jspwiki.ui.progress.ProgressManager;
 import com.ecyrd.jspwiki.url.URLConstructor;
 import com.ecyrd.jspwiki.util.ClassUtil;
+import com.ecyrd.jspwiki.util.PageSorter;
 import com.ecyrd.jspwiki.util.WatchDog;
 import com.ecyrd.jspwiki.workflow.*;
 
@@ -148,6 +149,9 @@
     /** If this property is set to false, all filters are disabled when translating. */
     public static final String PROP_RUNFILTERS   = "jspwiki.runFilters";
 
+    /** Compares pages by name */
+    private PageSorter     m_pageSorter = null;
+    
     /** Does the work in renaming pages. */
     private PageRenamer    m_pageRenamer = null;
 
@@ -507,6 +511,10 @@
 
         m_templateDir    = TextUtil.getStringProperty( props, PROP_TEMPLATEDIR, "default" );
         m_frontPage      = TextUtil.getStringProperty( props, PROP_FRONTPAGE,   "Main" );
+        
+        // Initialize the page name comparator now as it may be used while
+        // initializing other modules
+        initPageSorter( props );
 
         //
         //  Initialize the important modules.  Any exception thrown by the
@@ -2445,4 +2453,23 @@
     {
         return WatchDog.getCurrentWatchDog(this);
     }
+    
+    /**
+     * Initialize the page name comparator.
+     */
+    private void initPageSorter( Properties props )
+    {
+        m_pageSorter = new PageSorter();
+        m_pageSorter.initialize( props );
+    }
+    
+    /**
+     * Get this engine's page name comparator.
+     * 
+     * @return the PageSorter used to sort pages by name in this engine
+     */
+    public PageSorter getPageSorter()
+    {
+        return m_pageSorter;
+    }
 }

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/WikiPage.java Tue May 12 17:19:02 2009
@@ -348,26 +348,14 @@
     }
     
     /**
-     *  Compares a page with another.  The primary sorting order
-     *  is according to page name, and if they have the same name,
-     *  then according to the page version.
+     *  Compares a page with another using the defined PageNameComparator.  See com.ecyrd.jspwiki.util.PageSorter.
      *  
-     *  @param o The object to compare against
+     *  @param page The object to compare against
      *  @return -1, 0 or 1
      */
-    public int compareTo( Object o )
+    public int compareTo( Object page )
     {
-        int res = 0;
-        if( o instanceof WikiPage )
-        {
-            WikiPage c = (WikiPage)o;
-        
-            res = this.getName().compareTo(c.getName());
-            
-            if( res == 0 ) res = this.getVersion()-c.getVersion();
-        }
-            
-        return res;
+        return m_engine.getPageSorter().compare( this, (WikiPage) page );
     }
     
     /**

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/attachment/AttachmentManager.java Tue May 12 17:19:02 2009
@@ -324,7 +324,7 @@
         //
         if( atts instanceof List )
         {
-            Collections.sort( (List) atts );
+            m_engine.getPageSorter().sortPages( (List) atts );
         }
 
         return atts;

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java Tue May 12 17:19:02 2009
@@ -79,7 +79,7 @@
             log.error("Unable to get all attachments",e);
         }
 
-        Collections.sort( pageNames );
+        m_engine.getPageSorter().sort( pageNames );
 
         ArrayList<DirectoryItem> result = new ArrayList<DirectoryItem>();
 

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/AbstractReferralPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/AbstractReferralPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/AbstractReferralPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/AbstractReferralPlugin.java Tue May 12 17:19:02 2009
@@ -21,6 +21,9 @@
 package com.ecyrd.jspwiki.plugin;
 
 import java.io.IOException;
+import java.text.Collator;
+import java.text.ParseException;
+import java.text.RuleBasedCollator;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
@@ -34,6 +37,11 @@
 import com.ecyrd.jspwiki.preferences.Preferences;
 import com.ecyrd.jspwiki.preferences.Preferences.TimeFormat;
 import com.ecyrd.jspwiki.render.RenderingManager;
+import com.ecyrd.jspwiki.util.PageSorter;
+import com.ecyrd.jspwiki.util.comparators.CollatorComparator;
+import com.ecyrd.jspwiki.util.comparators.HumanComparator;
+import com.ecyrd.jspwiki.util.comparators.JavaNaturalComparator;
+import com.ecyrd.jspwiki.util.comparators.LocaleComparator;
 
 /**
  *  This is a base class for all plugins using referral things.
@@ -49,6 +57,8 @@
  *  <li><b>show</b> - value is either "pages" (default) or "count".  When "count" is specified, shows only the count
  *      of pages which match. (since 2.8)</li>
  *  <li><b>showLastModified</b> - When show=count, shows also the last modified date. (since 2.8)</li>
+ *  <li><b>sortOrder</b> - specifies the sort order for the resulting list.  Options are
+ *  'human', 'java', 'locale' or a <code>RuleBasedCollator</code> rule string. (since 2.8.3)</li>
  *  </ul>
  *  
  */
@@ -89,6 +99,12 @@
     
     /** Parameter name for showing the last modification count.  Value is <tt>{@value}</tt>. */
     public static final String PARAM_LASTMODIFIED     = "showLastModified";
+    
+    /** Parameter name for specifying the sort order.  Value is <tt>{@value}</tt>. */
+    protected static final String PARAM_SORTORDER        = "sortOrder";
+    protected static final String PARAM_SORTORDER_HUMAN  = "human";
+    protected static final String PARAM_SORTORDER_JAVA   = "java";
+    protected static final String PARAM_SORTORDER_LOCALE = "locale";
 
     protected           int      m_maxwidth = Integer.MAX_VALUE;
     protected           String   m_before = ""; // null not blank
@@ -97,6 +113,7 @@
 
     protected           Pattern[]  m_exclude;
     protected           Pattern[]  m_include;
+    protected           PageSorter m_sorter;
     
     protected           String m_show = "pages";
     protected           boolean m_lastModified=false;
@@ -219,10 +236,12 @@
                 }
             }
         }
+        
+        initSorter( context, params );
     }
     
     /**
-     *  Filters a collection according to the include and exclude -parameters.
+     *  Filters a collection according to the include and exclude parameters.
      *  
      *  @param c The collection to filter.
      *  @return A filtered collection.
@@ -315,6 +334,20 @@
     }
 
     /**
+     *  Filters and sorts a collection according to the include and exclude parameters.
+     *  
+     *  @param c The collection to filter.
+     *  @return A filtered and sorted collection.
+     */
+    @SuppressWarnings( "unchecked" )
+    protected Collection filterAndSortCollection( Collection c )
+    {
+        ArrayList<Object> result = (ArrayList<Object>)filterCollection( c );
+        m_sorter.sortPages( result );
+        return result;
+    }
+
+    /**
      *  Makes WikiText from a Collection.
      *
      *  @param links Collection to make into WikiText.
@@ -416,4 +449,44 @@
             return text;
         }
     }
+    
+    /**
+     * Helper method to initialize the comparator for this page.
+     */
+    private void initSorter( WikiContext context, Map params )
+    {
+        String order = (String) params.get( PARAM_SORTORDER );
+        if( order == null || order.length() == 0 )
+        {
+            // Use the configured comparator
+            m_sorter = context.getEngine().getPageSorter();
+        }
+        else if( order.equalsIgnoreCase( PARAM_SORTORDER_JAVA ) )
+        {
+            // use Java "natural" ordering
+            m_sorter = new PageSorter( JavaNaturalComparator.DEFAULT_JAVA_COMPARATOR );
+        }
+        else if( order.equalsIgnoreCase( PARAM_SORTORDER_LOCALE ) )
+        {
+            // use this locale's ordering
+            m_sorter = new PageSorter( LocaleComparator.DEFAULT_LOCALE_COMPARATOR );
+        }
+        else if( order.equalsIgnoreCase( PARAM_SORTORDER_HUMAN ) )
+        {
+            // use human ordering
+            m_sorter = new PageSorter( HumanComparator.DEFAULT_HUMAN_COMPARATOR );
+        }
+        else
+            try
+            {
+                Collator collator = new RuleBasedCollator( order );
+                collator.setStrength( Collator.PRIMARY );
+                m_sorter = new PageSorter( new CollatorComparator( collator ) );
+            }
+            catch( ParseException pe )
+            {
+                log.info( "Failed to parse requested collator - using default ordering", pe );
+                m_sorter = context.getEngine().getPageSorter();
+            }
+    }
 }

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/IndexPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/IndexPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/IndexPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/IndexPlugin.java Tue May 12 17:19:02 2009
@@ -65,7 +65,7 @@
         try
         {
             pages = listPages( context, include, exclude );
-            Collections.sort( pages );
+            context.getEngine().getPageSorter().sort( pages );
             
             char initialChar = ' ';
             

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferredPagesPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferredPagesPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferredPagesPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferredPagesPlugin.java Tue May 12 17:19:02 2009
@@ -197,7 +197,7 @@
         if( links != null )
             allLinks.addAll( links );
 
-        if( m_formatSort ) Collections.sort(allLinks);
+        if( m_formatSort ) context.getEngine().getPageSorter().sort( allLinks );
 
         for( Iterator i = allLinks.iterator(); i.hasNext(); )
         {

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferringPagesPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferringPagesPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferringPagesPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/ReferringPagesPlugin.java Tue May 12 17:19:02 2009
@@ -100,7 +100,7 @@
         
             if( links != null && links.size() > 0 )
             {
-                links = filterCollection( links );
+                links = filterAndSortCollection( links );
                 wikitext = wikitizeCollection( links, m_separator, items );
 
                 result.append( makeHTML( context, wikitext ) );

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UndefinedPagesPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UndefinedPagesPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UndefinedPagesPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UndefinedPagesPlugin.java Tue May 12 17:19:02 2009
@@ -49,11 +49,7 @@
 
         super.initialize( context, params );
 
-        TreeSet<String> sortedSet = new TreeSet<String>();
-
-        links = filterCollection( links );
-
-        sortedSet.addAll( links );
+        links = filterAndSortCollection( links );
         
         String wikitext = null;
         
@@ -68,7 +64,7 @@
         }
         else
         {
-            wikitext = wikitizeCollection(sortedSet, m_separator, ALL_ITEMS);
+            wikitext = wikitizeCollection( links, m_separator, ALL_ITEMS );
         }
         
         return makeHTML( context, wikitext );

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UnusedPagesPlugin.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UnusedPagesPlugin.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UnusedPagesPlugin.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/plugin/UnusedPagesPlugin.java Tue May 12 17:19:02 2009
@@ -72,11 +72,7 @@
 
         super.initialize( context, params );
 
-        TreeSet<String> sortedSet = new TreeSet<String>();
-        
-        links = filterCollection( links );
-        
-        sortedSet.addAll( links );
+        links = filterAndSortCollection( links );
 
         String wikitext = null;
         
@@ -90,7 +86,7 @@
         }
         else
         {
-            wikitext = wikitizeCollection(sortedSet, m_separator, ALL_ITEMS);
+            wikitext = wikitizeCollection( links, m_separator, ALL_ITEMS );
         }        
         return makeHTML( context, wikitext );
     }

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/PageSorter.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/PageSorter.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/PageSorter.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/PageSorter.java Tue May 12 17:19:02 2009
@@ -0,0 +1,211 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util;
+
+import java.util.*;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.util.comparators.JavaNaturalComparator;
+
+/**
+ * Wrapper class for managing and using the PageNameComparator.
+ * <p>
+ * <b>Note</b> - this class is deliberately not null safe. Never call any of the
+ * methods with a null argument!
+ */
+public class PageSorter implements Comparator
+{
+    private static Logger log = Logger.getLogger( PageSorter.class );
+
+    // The name of the property that specifies the desired page name comparator
+    protected static final String PROP_PAGE_NAME_COMPARATOR = "jspwiki.pageNameComparator.class";
+
+    private Comparator<String> m_comparator;
+
+    /**
+     * Default constructor uses Java "natural" ordering.
+     */
+    public PageSorter()
+    {
+        m_comparator = JavaNaturalComparator.DEFAULT_JAVA_COMPARATOR;
+    }
+
+    /**
+     * Construct with a particular comparator.
+     * 
+     * @param comparator the Comparator to use
+     */
+    public PageSorter( Comparator<String> comparator )
+    {
+        m_comparator = comparator;
+    }
+
+    /**
+     * Compare two page names (Object version). Arguments must be either String
+     * or WikiPage.
+     * 
+     * @throws IllegalArgumentException if incorrect argument types.
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( Object o1, Object o2 )
+    {
+        if( o1 instanceof String )
+        {
+            if( o2 instanceof String )
+                return m_comparator.compare( (String) o1, (String) o2 );
+            if( o2 instanceof WikiPage )
+                return m_comparator.compare( (String) o1, ((WikiPage) o2).getName() );
+        }
+        else if( o1 instanceof WikiPage )
+        {
+            if( o2 instanceof WikiPage )
+                return m_comparator.compare( ((WikiPage) o1).getName(), ((WikiPage) o2).getName() );
+            if( o2 instanceof String )
+                return m_comparator.compare( ((WikiPage) o1).getName(), (String) o2 );
+        }
+
+        throw new IllegalArgumentException( "Can only compare String or WikiPage" );
+    }
+
+    /**
+     * Compare two page names (String version).
+     * 
+     * @param pageName1 the first page name
+     * @param pageName2 the second page name
+     * @return see java.util.Comparator
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( String pageName1, String pageName2 )
+    {
+        return m_comparator.compare( pageName1, pageName2 );
+    }
+
+    /**
+     * Compare two pages (WikiPage version). Compares them by name first. If the
+     * same name, compares their versions.
+     * 
+     * @param page1 the first page
+     * @param page2 the second page
+     * @return see java.util.Comparator
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( WikiPage page1, WikiPage page2 )
+    {
+        if( page1 == page2 )
+            return 0; // the same object
+
+        int res = m_comparator.compare( page1.getName(), page2.getName() );
+        if( res == 0 )
+            res = page1.getVersion() - page2.getVersion();
+        return res;
+    }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if( !(o instanceof PageSorter) )
+            return false; // Definitely not equal
+
+        PageSorter that = (PageSorter) o;
+        if( this == that || m_comparator == that.m_comparator )
+            return true; // Essentially the same object
+        return m_comparator.equals( that.m_comparator );
+    }
+
+    /**
+     * Called by WikiEngine to initialise this instance. Tries to use class
+     * given by the PROP_PAGE_NAME_COMPARATOR property as the page name
+     * comparator. Uses a default comparator if this property is not set or
+     * there is any problem loading the specified class.
+     * 
+     * @param props this WikiEngine's properties.
+     */
+    @SuppressWarnings( "unchecked" )
+    public void initialize( Properties props )
+    {
+        // Default is Java natural order
+        m_comparator = JavaNaturalComparator.DEFAULT_JAVA_COMPARATOR;
+        String className = props.getProperty( PROP_PAGE_NAME_COMPARATOR );
+        if( className != null && className.length() > 0 )
+            try
+            {
+                m_comparator = (Comparator<String>) ClassUtil.findClass( "com.ecyrd.jspwiki.util.comparators", className )
+                    .newInstance();
+            }
+            catch( Exception e )
+            {
+                log.error( "Falling back to default \"natural\" comparator", e );
+            }
+    }
+
+    /**
+     * Sorts the specified list into ascending order based on the
+     * PageNameComparator. The actual sort is done using
+     * <code>Collections.sort()</code>.
+     * 
+     * @param nameList the page names to be sorted
+     */
+    public void sort( List<String> nameList )
+    {
+        Collections.sort( nameList, m_comparator );
+    }
+
+    /**
+     * Sorts the specified array into ascending order based on the
+     * PageNameComparator. The actual sort is done using
+     * <code>Arrays.sort()</code>.
+     * 
+     * @param nameArray the page names to be sorted
+     */
+    public void sort( String[] nameArray )
+    {
+        Arrays.sort( nameArray, m_comparator );
+    }
+
+    /**
+     * Sorts the specified list into ascending order based on the
+     * PageNameComparator. The actual sort is done using
+     * <code>Collections.sort()</code>.
+     * 
+     * @param pageList the pages to be sorted
+     */
+    @SuppressWarnings( "unchecked" )
+    public void sortPages( List<Object> pageList )
+    {
+        Collections.sort( pageList, this );
+    }
+
+    /**
+     * Sorts the specified array into ascending order based on the
+     * PageNameComparator. The actual sort is done using
+     * <code>Arrays.sort()</code>.
+     * 
+     * @param pageArray the pages to be sorted
+     */
+    @SuppressWarnings( "unchecked" )
+    public void sortPages( Object[] pageArray )
+    {
+        Arrays.sort( pageArray, this );
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/CollatorComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/CollatorComparator.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/CollatorComparator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/CollatorComparator.java Tue May 12 17:19:02 2009
@@ -0,0 +1,85 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util.comparators;
+
+import java.text.Collator;
+import java.util.Comparator;
+
+/**
+ * A comparator that sorts Strings using a Collator. This class is needed
+ * because, even though Collator implements
+ * <code>Comparator&lt;Object&gt;</code> and the required
+ * <code>compare(String, String)</code> method, you can't safely cast Collator
+ * to <code>Comparator&lt;String&gt;</code>.
+ * 
+ * @author Greg Kable, Serapsys
+ */
+public class CollatorComparator implements Comparator<String>
+{
+    // A special singleton instance for quick access
+    public static final Comparator<String> DEFAULT_LOCALE_COMPARATOR = new CollatorComparator();
+
+    protected Collator m_collator;
+
+    /**
+     * Default constructor uses the current locale's collator.
+     */
+    public CollatorComparator()
+    {
+        m_collator = Collator.getInstance();
+    }
+
+    /**
+     * Construct with a specific collator.
+     * 
+     * @param collator the collator to be used for comparisons
+     */
+    public CollatorComparator( Collator collator )
+    {
+        m_collator = collator;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( String str1, String str2 )
+    {
+        if( str1 == str2 )
+            return 0; // the same object
+        if( str1 == null )
+            return -1; // str1 is null and str2 isn't so str1 is smaller
+        if( str2 == null )
+            return 1; // str2 is null and str1 isn't so str1 is bigger
+        return m_collator.compare( str1, str2 );
+    }
+
+    /**
+     * Specify a new collator.
+     * 
+     * @param collator the collator to be used from now on
+     */
+    public void setCollator( Collator collator )
+    {
+        m_collator = collator;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/HumanComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/HumanComparator.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/HumanComparator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/HumanComparator.java Tue May 12 17:19:02 2009
@@ -0,0 +1,260 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util.comparators;
+
+import java.util.Comparator;
+
+/**
+ * A comparator that sorts Strings using "human" ordering, including decimal
+ * ordering. Only works for languages where every Character is lexigraphically
+ * distinct and correctly unicode ordered (e.g. English). Other languages should use
+ * <code>CollatedHumanComparator</code>. Pretty efficient but still slower than
+ * String.compareTo().
+ * 
+ * @author Greg Kable, Serapsys
+ */
+public class HumanComparator implements Comparator<String>
+{
+    // Constants for categorising characters and specifying category level
+    // ordering
+    public enum CharType
+    {
+        TYPE_OTHER, TYPE_DIGIT, TYPE_LETTER
+    }
+
+    // A special singleton instance for quick access
+    public static final Comparator<String> DEFAULT_HUMAN_COMPARATOR = new HumanComparator();
+
+    /**
+     * Returns a singleton comparator that implements the default behaviour.
+     * 
+     * @return the singleton comparator.
+     */
+    public static Comparator<String> getInstance()
+    {
+        return DEFAULT_HUMAN_COMPARATOR;
+    }
+
+    private CharType[] sortOrder = { CharType.TYPE_OTHER, CharType.TYPE_DIGIT, CharType.TYPE_LETTER };
+
+    /**
+     * Default constructor which does nothing. Here because it has a non-default
+     * constructor.
+     */
+    public HumanComparator()
+    {
+        // Empty
+    }
+
+    /**
+     * Constructor specifying all the character type order.
+     * 
+     * @param sortOrder see setSortOrder
+     */
+    public HumanComparator( CharType[] sortOrder )
+    {
+        setSortOrder( sortOrder );
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( String str1, String str2 )
+    {
+        // Some quick and easy checks
+        if( str1 == str2 )
+            return 0; // they're identical, possibly both null
+        if( str1 == null )
+            return -1; // str1 is null and str2 isn't so str1 is smaller
+        if( str2 == null )
+            return 1; // str2 is null and str1 isn't so str1 is bigger
+
+        char[] s1 = str1.toCharArray();
+        char[] s2 = str2.toCharArray();
+        int len1 = s1.length;
+        int len2 = s2.length;
+        int idx = 0;
+        int caseComparison = 0; // Used to defer a case sensitive comparison
+
+        while ( idx < len1 && idx < len2 )
+        {
+            char c1 = s1[idx];
+            char c2 = s2[idx++];
+
+            // Convert to lower case
+            char lc1 = Character.toLowerCase( c1 );
+            char lc2 = Character.toLowerCase( c2 );
+
+            // If case makes a difference, note the difference the first time
+            // it's encountered
+            if( caseComparison == 0 && c1 != c2 && lc1 == lc2 )
+                if( Character.isLowerCase( c1 ) )
+                    caseComparison = 1;
+                else if( Character.isLowerCase( c2 ) )
+                    caseComparison = -1;
+
+            // Do the rest of the tests in lower case
+            c1 = lc1;
+            c2 = lc2;
+
+            if( c1 != c2 || c1 == '0' ) // leading zeros are a special case
+            {
+                // They might be different, now we can do a comparison
+                CharType type1 = mapCharTypes( c1 );
+                CharType type2 = mapCharTypes( c2 );
+
+                // Do the character class check
+                int result = compareCharTypes( type1, type2 );
+                if( result != 0 )
+                    return result; // different character classes so that's
+                // sufficient
+
+                // If they're not digits, use character to character comparison
+                if( type1 != CharType.TYPE_DIGIT )
+                {
+                    Character ch1 = new Character( c1 );
+                    Character ch2 = new Character( c2 );
+                    return ch1.compareTo( ch2 );
+                }
+
+                // The only way to get here is both characters are digits
+                assert( type1 == CharType.TYPE_DIGIT && type2 == CharType.TYPE_DIGIT );
+                result = compareDigits( s1, s2, idx - 1 );
+                if( result != 0 )
+                    return result; // Got a result so return it
+
+                // No result yet, spin through the digits and continue trying
+                while ( idx < len1 && idx < len2 && Character.isDigit( s1[idx] ) )
+                    idx++;
+            }
+        }
+
+        if( len1 == len2 )
+            return caseComparison; // identical so return any case dependency
+        return len1 - len2; // Shorter String is less
+    }
+
+    /**
+     * Implements ordering based on broad categories (e.g. numbers are always
+     * less than digits)
+     * 
+     * @param type1 first CharType
+     * @param type2 second CharType
+     * @return -1 if type1 < type2, 0 if type1 == type2, 1 if type1 > type2
+     */
+    private int compareCharTypes( CharType type1, CharType type2 )
+    {
+        if( type1 == type2 )
+            return 0; // Same type so equal
+        if( type1 == sortOrder[0] )
+            return -1; // t1 is the lowest order and t2 isn't so t1 must be less
+        if( type2 == sortOrder[0] )
+            return 1; // t2 is the lowest order and t1 isn't so t1 must be more
+        if( type1 == sortOrder[1] )
+            return -1; // t1 is the middle order and t2 isn't so t1 must be less
+        if( type2 == sortOrder[1] )
+            return 1; // t2 is the middle order and t1 isn't so t1 must be more
+        // Can't possible get here as that would mean they're both sortOrder[2]
+        assert( type1 != type2 );
+        return 0;
+    }
+
+    /**
+     * Do a numeric comparison on two otherwise identical char arrays.
+     * 
+     * @param left the left hand character array.
+     * @param offset the index of the first digit of the number in both char
+     *            arrays.
+     * @return negative, zero or positive depending on the numeric comparison of
+     *         left and right.
+     */
+    private int compareDigits( char[] left, char[] right, int offset )
+    {
+        // Calculate the integer value of the left hand side
+        int idx = offset;
+        while ( idx < left.length && Character.isDigit( left[idx] ) )
+            idx++;
+        int leftLen = idx - offset;
+        int leftValue = Integer.valueOf( new String( left, offset, leftLen ) );
+
+        // Calculate the integer value of the right hand side
+        idx = offset;
+        while ( idx < right.length && Character.isDigit( right[idx] ) )
+            idx++;
+        int rightLen = idx - offset;
+        int rightValue = Integer.valueOf( new String( right, offset, rightLen ) );
+
+        if( leftValue == rightValue )
+            return leftLen - rightLen; // Same value so use the lengths
+        return leftValue - rightValue; // Otherwise compare the values
+    }
+
+    public CharType[] getSortOrder()
+    {
+        return sortOrder;
+    }
+
+    /**
+     * Very broadly characterises a character as a digit, a letter or a
+     * punctuation character.
+     * 
+     * @param c <code>char</code> to be characterised
+     * @return <code>IS_DIGIT</code> if it's a digit, <code>IS_LETTER</code> if
+     *         it's a letter, <code>IS_PUNC</code> otherwise.
+     */
+    private CharType mapCharTypes( char c )
+    {
+        if( Character.isDigit( c ) )
+            return CharType.TYPE_DIGIT;
+        else if( Character.isLetter( c ) )
+            return CharType.TYPE_LETTER;
+        else
+            return CharType.TYPE_OTHER;
+    }
+
+    /**
+     * Set the order in which letters, numbers and everything else is presented.
+     * Default is other, digits and then letters. For example, the strings
+     * "abb", "a1b" and "a b" will sort in the order "a b", "a1b" then "abb" by
+     * default.
+     * 
+     * @param sortOrder Must be an array of <code>CharType</code> containing
+     *            exactly 3 elements each of which must be distinct.
+     * @throws IllegalArgumentException if being called on the result of
+     *             <code>HumanStringComparator.getInstance()</code> or
+     *             <code>sortOrder</code> is not exactly 3 different
+     *             <code>CharType</code>.
+     */
+    public void setSortOrder( CharType[] sortOrder )
+    {
+        if( this == DEFAULT_HUMAN_COMPARATOR )
+            throw new IllegalArgumentException( "Can't call setters on default " + this.getClass().getName() );
+
+        // Sanity check the sort order
+        if( sortOrder.length != 3 )
+            throw new IllegalArgumentException( "There must be exactly three elements in the sort order" );
+        if( sortOrder[0] == sortOrder[1] || sortOrder[0] == sortOrder[2] || sortOrder[1] == sortOrder[2] )
+            throw new IllegalArgumentException( "The sort order must contain EXACTLY one of each CharType" );
+        this.sortOrder = sortOrder;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/JavaNaturalComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/JavaNaturalComparator.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/JavaNaturalComparator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/JavaNaturalComparator.java Tue May 12 17:19:02 2009
@@ -0,0 +1,50 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util.comparators;
+
+import java.util.Comparator;
+
+/**
+ * A comparator that sorts Strings using Java's "natural" order.
+ * 
+ * @author Greg Kable, Serapsys
+ */
+public class JavaNaturalComparator implements Comparator<String>
+{
+    // A special singleton instance for quick access
+    public static final Comparator<String> DEFAULT_JAVA_COMPARATOR = new JavaNaturalComparator();
+
+    /*
+     * (non-Javadoc)
+     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+     */
+    public int compare( String str1, String str2 )
+    {
+        if ( str1 == str2 )
+            return 0; // the same object
+        if( str1 == null )
+            return -1; // str1 is null and str2 isn't so str1 is smaller
+        if( str2 == null )
+            return 1; // str2 is null and str1 isn't so str1 is bigger
+        return str1.compareTo( str2 );
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/LocaleComparator.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/LocaleComparator.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/LocaleComparator.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/src/com/ecyrd/jspwiki/util/comparators/LocaleComparator.java Tue May 12 17:19:02 2009
@@ -0,0 +1,59 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util.comparators;
+
+import java.text.Collator;
+import java.util.Locale;
+
+/**
+ * A comparator that sorts Strings using the locale's collator.
+ * 
+ * @author Greg Kable, Serapsys
+ */
+public class LocaleComparator extends CollatorComparator
+{
+    /**
+     * Default constructor uses the current locale's collator.
+     */
+    public LocaleComparator()
+    {
+        m_collator = Collator.getInstance();
+    }
+
+    /**
+     * use a specific locale's collator.
+     */
+    public LocaleComparator( Locale locale )
+    {
+        m_collator = Collator.getInstance( locale );
+    }
+
+    /**
+     * Specify a new locale.
+     * 
+     * @param locale the locale for future comparisons
+     */
+    public void setLocale( Locale locale)
+    {
+        m_collator = Collator.getInstance(locale);
+    }
+}

Modified: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/AllTests.java?rev=773987&r1=773986&r2=773987&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/AllTests.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/AllTests.java Tue May 12 17:19:02 2009
@@ -18,7 +18,9 @@
         suite.addTest( ClassUtilTest.suite() );
         suite.addTest( CommentedPropertiesTest.suite() );
         suite.addTest( CryptoUtilTest.suite() );
+        suite.addTest( com.ecyrd.jspwiki.util.comparators.AllTests.suite() );
         suite.addTest( MailUtilTest.suite() );
+        suite.addTest( PageSorterTest.suite() );
         suite.addTest( PriorityListTest.suite() );
         suite.addTest( SerializerTest.suite() );
         suite.addTest( TextUtilTest.suite() );

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/PageSorterTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/PageSorterTest.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/PageSorterTest.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/PageSorterTest.java Tue May 12 17:19:02 2009
@@ -0,0 +1,97 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.ecyrd.jspwiki.util.comparators.HumanComparator;
+import com.ecyrd.jspwiki.util.comparators.LocaleComparator;
+
+/**
+ * Wrapper class for managing and using the PageNameComparator.
+ * <p>
+ * <b>Note</b> - this class is deliberately not null safe. Never call any of the
+ * methods with a null argument!
+ */
+public class PageSorterTest extends TestCase
+{
+    public static Test suite()
+    {
+        return new TestSuite( PageSorterTest.class );
+    }
+
+    public void testPageSorterBadProperty()
+    {
+        // Initialised with a broken property
+        PageSorter sorter = new PageSorter();
+        Properties props = new Properties();
+        props.put( PageSorter.PROP_PAGE_NAME_COMPARATOR, "haha.this.isnt.a.class" );
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) > 0 );
+    }
+
+    public void testPageSorterDefaultConstructor()
+    {
+        // Check uninitialised behaviour
+        PageSorter sorter = new PageSorter();
+        assertTrue( sorter.compare( "ab2", "ab10" ) > 0 );
+    }
+
+    public void testPageSorterHumanComparator()
+    {
+        // Initialised with the human comparator
+        PageSorter sorter = new PageSorter();
+        Properties props = new Properties();
+        props.put( PageSorter.PROP_PAGE_NAME_COMPARATOR, HumanComparator.class.getPackage().getName() + ".HumanComparator" );
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) < 0 );
+        props.put( PageSorter.PROP_PAGE_NAME_COMPARATOR, "HumanComparator" );
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) < 0 );
+    }
+
+    public void testPageSorterLocaleComparator()
+    {
+        // Initialised with the human comparator
+        PageSorter sorter = new PageSorter();
+        Properties props = new Properties();
+        props.put( PageSorter.PROP_PAGE_NAME_COMPARATOR, LocaleComparator.class.getPackage().getName() + ".LocaleComparator" );
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) > 0 );
+        props.put( PageSorter.PROP_PAGE_NAME_COMPARATOR, "LocaleComparator" );
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) > 0 );
+    }
+
+    public void testPageSorterNoProperty()
+    {
+        // Initialised without the necessary property
+        PageSorter sorter = new PageSorter();
+        Properties props = new Properties();
+        sorter.initialize( props );
+        assertTrue( sorter.compare( "ab2", "ab10" ) > 0 );
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/AllTests.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/AllTests.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/AllTests.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/AllTests.java Tue May 12 17:19:02 2009
@@ -0,0 +1,22 @@
+
+package com.ecyrd.jspwiki.util.comparators;
+
+
+import junit.framework.*;
+
+public class AllTests extends TestCase
+{
+    public AllTests( String s )
+    {
+        super( s );
+    }
+
+    public static Test suite()
+    {
+        TestSuite suite = new TestSuite( "Comparator utility suite tests" );
+
+        suite.addTest( HumanComparatorTest.suite() );
+        
+        return suite;
+    }
+}

Added: incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/HumanComparatorTest.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/HumanComparatorTest.java?rev=773987&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/HumanComparatorTest.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_2_8_BRANCH/tests/com/ecyrd/jspwiki/util/comparators/HumanComparatorTest.java Tue May 12 17:19:02 2009
@@ -0,0 +1,90 @@
+/* 
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    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 com.ecyrd.jspwiki.util.comparators;
+
+import java.util.Comparator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class HumanComparatorTest extends TestCase
+{
+    public static Test suite()
+    {
+        return new TestSuite( HumanComparatorTest.class );
+    }
+
+    public void testCharOrder()
+    {
+        HumanComparator comparator = new HumanComparator();
+
+        // Default order first
+        assertTrue( comparator.compare( "a c", "a1c" ) < 0 );
+        assertTrue( comparator.compare( "a1c", "abc" ) < 0 );
+
+        // Now letters then numbers then other
+        HumanComparator.CharType sortOrder[] = { HumanComparator.CharType.TYPE_LETTER, HumanComparator.CharType.TYPE_DIGIT,
+                                                HumanComparator.CharType.TYPE_OTHER };
+        comparator.setSortOrder( sortOrder );
+        assertTrue( comparator.compare( "a c", "a1c" ) > 0 );
+        assertTrue( comparator.compare( "a1c", "abc" ) > 0 );
+
+        // Now numbers then letters then other
+        sortOrder[0] = HumanComparator.CharType.TYPE_DIGIT;
+        sortOrder[1] = HumanComparator.CharType.TYPE_LETTER;
+        sortOrder[2] = HumanComparator.CharType.TYPE_OTHER;
+        comparator.setSortOrder( sortOrder );
+        assertTrue( comparator.compare( "a c", "a1c" ) > 0 );
+        assertTrue( comparator.compare( "a1c", "abc" ) < 0 );
+
+        // Finally try to break it
+        try
+        {
+            sortOrder[0] = HumanComparator.CharType.TYPE_DIGIT;
+            sortOrder[1] = HumanComparator.CharType.TYPE_DIGIT;
+            sortOrder[2] = HumanComparator.CharType.TYPE_OTHER;
+            comparator.setSortOrder( sortOrder );
+            fail( "Expected IllegalArgumentException" );
+        }
+        catch( IllegalArgumentException e )
+        {
+            // All worked
+        }
+    }
+
+    public void testCompare()
+    {
+        Comparator<String> comparator = new HumanComparator();
+
+        assertTrue( comparator.compare( "abcd001", "ABCD001" ) > 0 );
+        assertTrue( comparator.compare( "abcd001a", "ABCD001z" ) < 0 );
+        assertTrue( comparator.compare( "abc8", "abcd1" ) < 0 );
+        assertTrue( comparator.compare( "abc 8", "abc1" ) < 0 );
+        assertTrue( comparator.compare( "abc  8", "abc 1" ) < 0 );
+        assertTrue( comparator.compare( "abdc001", "ABCD001" ) > 0 );
+        assertTrue( comparator.compare( "ab cd001", "ABDC001" ) < 0 );
+        assertTrue( comparator.compare( "10", "01" ) > 0 );
+        assertTrue( comparator.compare( "10", "00000001" ) > 0 );
+        assertTrue( comparator.compare( "01", "00000001" ) < 0 );
+    }
+}
\ No newline at end of file