[476465] - use alphanumerical sort to keep numeric portions in order

Signed-off-by: Rob Stryker <stryker@redhat.com>
Change-Id: I9c02cf9fcfd529fd45e60634fee11b37fefc3cda
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/DefaultViewerSorter.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/DefaultViewerSorter.java
index 97f6158..9fb509c 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/DefaultViewerSorter.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/viewers/DefaultViewerSorter.java
@@ -11,6 +11,7 @@
 package org.eclipse.wst.server.ui.internal.viewers;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.StringTokenizer;
 
@@ -23,6 +24,8 @@
  * New wizards.
  */
 public class DefaultViewerSorter extends ViewerSorter {
+	private static AlphanumComparator alphanum = new AlphanumComparator();
+	
 	public static class Version implements Comparable {
 		private static final String SEPARATORS = ".,";
 
@@ -181,8 +184,8 @@
 	protected static int compareRuntimeTypes(IRuntimeType r1, IRuntimeType r2) {
 		if (isSameFamily(r1.getName(), r1.getVersion(), r2.getName(), r2.getVersion()))
 			return compareVersions(r1.getVersion(), r2.getVersion());
-		
-		return r1.getName().compareToIgnoreCase(r2.getName());
+
+		return alphanum.compare(r1.getName(),r2.getName());
 	}
 
 	/**
@@ -202,7 +205,94 @@
 			if (isSameFamily(s1.getName(), r1.getVersion(), s2.getName(), r2.getVersion()))
 				return compareVersions(r1.getVersion(), r2.getVersion());
 		}
-		
-		return s1.getName().compareToIgnoreCase(s2.getName());
+
+		return alphanum.compare(s1.getName(), s2.getName());
 	}
+
+	/**
+	 * Split strings into blocks of numerics and non-numerics, and compare those blocks. 
+	 * This will allow a list containing "SomeServer 9.0" and "SomeServer 10.0" to have 
+	 * proper numeric sorting for the numeric portions.  
+	 */
+	private static class AlphanumComparator implements Comparator<String> {
+		public AlphanumComparator() {
+		}
+
+		private final boolean isDigit(char ch) {
+			return ((ch >= 48) && (ch <= 57));
+		}
+
+		/**
+		 * Length of string is passed in for improved efficiency (only need to calculate
+		 * it once)
+		 **/
+		private final String getChunk(String s, int slength, int marker) {
+			StringBuilder chunk = new StringBuilder();
+			char c = s.charAt(marker);
+			chunk.append(c);
+			marker++;
+			if (isDigit(c)) {
+				while (marker < slength) {
+					c = s.charAt(marker);
+					if (!isDigit(c))
+						break;
+					chunk.append(c);
+					marker++;
+				}
+			} else {
+				while (marker < slength) {
+					c = s.charAt(marker);
+					if (isDigit(c))
+						break;
+					chunk.append(c);
+					marker++;
+				}
+			}
+			return chunk.toString();
+		}
+
+		public int compare(String s1, String s2) {
+			if ((s1 == null) || (s2 == null)) {
+				return 0;
+			}
+
+			int thisMarker = 0;
+			int thatMarker = 0;
+			int s1Length = s1.length();
+			int s2Length = s2.length();
+
+			while (thisMarker < s1Length && thatMarker < s2Length) {
+				String thisChunk = getChunk(s1, s1Length, thisMarker);
+				thisMarker += thisChunk.length();
+
+				String thatChunk = getChunk(s2, s2Length, thatMarker);
+				thatMarker += thatChunk.length();
+
+				// If both chunks contain numeric characters, sort them numerically
+				int result = 0;
+				if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
+					// Simple chunk comparison by length.
+					int thisChunkLength = thisChunk.length();
+					result = thisChunkLength - thatChunk.length();
+					// If equal, the first different number counts
+					if (result == 0) {
+						for (int i = 0; i < thisChunkLength; i++) {
+							result = thisChunk.charAt(i) - thatChunk.charAt(i);
+							if (result != 0) {
+								return result;
+							}
+						}
+					}
+				} else {
+					result = thisChunk.compareTo(thatChunk);
+				}
+
+				if (result != 0)
+					return result;
+			}
+
+			return s1Length - s2Length;
+		}
+	}
+
 }
\ No newline at end of file