improve sort
diff --git a/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/layout/tests/TestRepo.java b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/layout/tests/TestRepo.java
index 7b70e86..9228ea0 100644
--- a/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/layout/tests/TestRepo.java
+++ b/tests/org.eclipse.wtp.releng.tests/src/org/eclipse/wtp/layout/tests/TestRepo.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -43,8 +44,8 @@
 import org.eclipse.wtp.releng.tests.TestActivator;
 
 /**
- * Tests that licenses in the helios repository are consistent with the
- * platform feature license.
+ * Tests that licenses in the helios repository are consistent with the platform
+ * feature license.
  * 
  * This was based on test attached to bug 306627
  * https://bugs.eclipse.org/bugs/show_bug.cgi?id=306627
@@ -60,53 +61,57 @@
 		return new TestSuite(TestRepo.class);
 	}
 
-	private static final String EOL = System.getProperty("line.separator", "\n");
+	private static final String EOL = System
+			.getProperty("line.separator", "\n");
 	private static final boolean DEBUG = false;
 	private static final String OLD_PROVIDER_NAME = "Eclipse.org";
 
-
-	public void testLicenses() throws URISyntaxException, ProvisionException, OperationCanceledException, IOException {
+	public void testLicenses() throws URISyntaxException, ProvisionException,
+			OperationCanceledException, IOException {
 		String repoURL = System.getProperty("repoURLToTest");
 		System.out.println("repoURLToTest: " + repoURL);
 		if (repoURL == null) {
 			repoURL = defaultURLToTest;
 		}
 		System.out.println("repoURLToTest: " + repoURL);
-// String referenceFeature = System.getProperty("referenceFeature");
-// System.out.println("referenceFeature: " + referenceFeature);
-// if (referenceFeature == null) {
-// referenceFeature = defaultReferenceFeature;
-// }
-// System.out.println("referenceFeature: " + referenceFeature);
+		// String referenceFeature = System.getProperty("referenceFeature");
+		// System.out.println("referenceFeature: " + referenceFeature);
+		// if (referenceFeature == null) {
+		// referenceFeature = defaultReferenceFeature;
+		// }
+		// System.out.println("referenceFeature: " + referenceFeature);
 		URI repoLocation = new URI(repoURL);
-		IMetadataRepository repo = getMetadataRepositoryManager().loadRepository(repoLocation, null);
+		IMetadataRepository repo = getMetadataRepositoryManager()
+				.loadRepository(repoLocation, null);
 		if (repo == null) {
-			System.out.println("no repository found at " + repoLocation.toString());
-		}
-		else {
+			System.out.println("no repository found at "
+					+ repoLocation.toString());
+		} else {
 
-
-			IQueryResult<IInstallableUnit> allFeatures = repo.query(QueryUtil.createIUGroupQuery(), null);
-// IQueryResult<IInstallableUnit> platform =
-// allFeatures.query(QueryUtil.createIUQuery(referenceFeature),
-// null);
+			IQueryResult<IInstallableUnit> allFeatures = repo.query(QueryUtil
+					.createIUGroupQuery(), null);
+			// IQueryResult<IInstallableUnit> platform =
+			// allFeatures.query(QueryUtil.createIUQuery(referenceFeature),
+			// null);
 			assertFalse(allFeatures.isEmpty());
-// if (platform == null) {
-// System.out.println("did not find reference feature: " + referenceFeature);
-// System.out.println("list of existing features: ");
-// Iterator<IInstallableUnit> itFeatures = allFeatures.iterator();
-// while (itFeatures.hasNext()) {
-// System.out.println(itFeatures.next());
-// }
-// }
-// assertNotNull(platform);
-// assertFalse(platform.isEmpty());
-// IInstallableUnit platformFeature = platform.iterator().next();
-// ILicense platformLicense =
-// platformFeature.getLicenses(null).iterator().next();
+			// if (platform == null) {
+			// System.out.println("did not find reference feature: " +
+			// referenceFeature);
+			// System.out.println("list of existing features: ");
+			// Iterator<IInstallableUnit> itFeatures = allFeatures.iterator();
+			// while (itFeatures.hasNext()) {
+			// System.out.println(itFeatures.next());
+			// }
+			// }
+			// assertNotNull(platform);
+			// assertFalse(platform.isEmpty());
+			// IInstallableUnit platformFeature = platform.iterator().next();
+			// ILicense platformLicense =
+			// platformFeature.getLicenses(null).iterator().next();
 
 			Properties properties = new Properties();
-			InputStream inStream = this.getClass().getResourceAsStream("standard.properties");
+			InputStream inStream = this.getClass().getResourceAsStream(
+					"standard.properties");
 			properties.load(inStream);
 			String body = properties.getProperty("license");
 			ILicense standardLicense = new License(null, body, null);
@@ -115,17 +120,19 @@
 			List<IInstallableUnit> extraLicense = new ArrayList<IInstallableUnit>();
 			List<IInstallableUnit> goodLicense = new ArrayList<IInstallableUnit>();
 			List<IInstallableUnit> badLicense = new ArrayList<IInstallableUnit>();
-			checkLicenses(standardLicense, allFeatures, goodLicense, badLicense, noLicense, extraLicense);
+			checkLicenses(standardLicense, allFeatures, goodLicense,
+					badLicense, noLicense, extraLicense);
 
 			printReport(goodLicense, badLicense, noLicense, extraLicense);
 		}
 	}
 
-	private void printReport(List<IInstallableUnit> goodLicense, List<IInstallableUnit> badLicense, List<IInstallableUnit> noLicense, List<IInstallableUnit> extraLicense) {
+	private void printReport(List<IInstallableUnit> goodLicense,
+			List<IInstallableUnit> badLicense,
+			List<IInstallableUnit> noLicense,
+			List<IInstallableUnit> extraLicense) {
 		String SPACER = "<br />=======================";
 
-
-
 		FileWriter outfileWriter = null;
 		File outfile = null;
 		String testDirName = System.getProperty(JUNIT_REPORT_OUTPUT);
@@ -133,13 +140,15 @@
 			outfile = new File(testDirName, "licenseConsisency.html");
 			outfileWriter = new FileWriter(outfile);
 
-
-
 			println(outfileWriter, "<br /><br />Summary:" + SPACER);
-			println(outfileWriter, "Features with conforming license: " + goodLicense.size());
-			println(outfileWriter, "Features with different license: " + badLicense.size());
-			println(outfileWriter, "Features with no license: " + noLicense.size());
-			println(outfileWriter, "Features with extra licenses: " + extraLicense.size());
+			println(outfileWriter, "Features with conforming license: "
+					+ goodLicense.size());
+			println(outfileWriter, "Features with different license: "
+					+ badLicense.size());
+			println(outfileWriter, "Features with no license: "
+					+ noLicense.size());
+			println(outfileWriter, "Features with extra licenses: "
+					+ extraLicense.size());
 			println(outfileWriter, "=======================");
 
 			println(outfileWriter, "<br /><br />Details:" + SPACER);
@@ -149,32 +158,33 @@
 				println(outfileWriter, unit.getId());
 			}
 
-			println(outfileWriter, "<br /><br />Features with different license:" + SPACER);
+			println(outfileWriter,
+					"<br /><br />Features with different license:" + SPACER);
 			for (IInstallableUnit unit : sort(badLicense)) {
 				println(outfileWriter, unit.getId());
 			}
 
-			println(outfileWriter, "<br /><br />Features with matching license:" + SPACER);
+			println(outfileWriter,
+					"<br /><br />Features with matching license:" + SPACER);
 			for (IInstallableUnit unit : sort(goodLicense)) {
 				println(outfileWriter, unit.getId());
 			}
-		}
-		catch (IOException e) {
+		} catch (IOException e) {
 			e.printStackTrace();
-		}
-		finally {
+		} finally {
 			if (outfileWriter != null) {
 				try {
 					outfileWriter.close();
-				}
-				catch (IOException e) {
+				} catch (IOException e) {
 					// weirdness
 					e.printStackTrace();
 				}
 			}
 		}
-		if (badLicense.size() > 0 || extraLicense.size() > 0 || noLicense.size() > 0) {
-			fail("Errors in license consistency. For list, see " + outfile.getAbsolutePath());
+		if (badLicense.size() > 0 || extraLicense.size() > 0
+				|| noLicense.size() > 0) {
+			fail("Errors in license consistency. For list, see "
+					+ outfile.getAbsolutePath());
 		}
 	}
 
@@ -188,7 +198,12 @@
 		return noLicense;
 	}
 
-	private void checkLicenses(ILicense platformLicense, IQueryResult<IInstallableUnit> allFeatures, List<IInstallableUnit> goodLicense, List<IInstallableUnit> badLicense, List<IInstallableUnit> noLicense, List<IInstallableUnit> extraLicense) {
+	private void checkLicenses(ILicense platformLicense,
+			IQueryResult<IInstallableUnit> allFeatures,
+			List<IInstallableUnit> goodLicense,
+			List<IInstallableUnit> badLicense,
+			List<IInstallableUnit> noLicense,
+			List<IInstallableUnit> extraLicense) {
 		for (IInstallableUnit feature : allFeatures.toUnmodifiableSet()) {
 			// ignore groups that are not features
 			if (!feature.getId().endsWith(".feature.group"))
@@ -211,7 +226,8 @@
 		}
 	}
 
-	private void checkProviderNames(IQueryResult<IInstallableUnit> allIUs) throws IOException {
+	private void checkProviderNames(IQueryResult<IInstallableUnit> allIUs)
+			throws IOException {
 		FileWriter outfileWriter = null;
 		File outfile = null;
 		List<IInstallableUnit> incorrectProviderName = new ArrayList<IInstallableUnit>();
@@ -221,56 +237,69 @@
 		try {
 			outfile = new File(testDirName, "incorrectProviderNames.html");
 			outfileWriter = new FileWriter(outfile);
-
+			System.out.println("output: " + outfile.getAbsolutePath());
 			for (IInstallableUnit iu : allIUs.toUnmodifiableSet()) {
-				// ignore categories
-				boolean isCategory = "true".equals(iu.getProperty("org.eclipse.equinox.p2.type.category"));
-				// TODO: should we exclude fragments?
-				boolean isFragment = "true".equals(iu.getProperty("org.eclipse.equinox.p2.type.fragment"));
-				// TODO: what are these special things? What ever they are,
-				// they have no provider name.
-				// config.a.jre is identified as a fragment
-				// (org.eclipse.equinox.p2.type.fragment).
-				// a.jre has no properties.
-				boolean isSpecial = "a.jre".equals(iu.getId()) || "config.a.jre".equals(iu.getId());
-				if (!isCategory && !isSpecial) {
-					String providerName = iu.getProperty(IInstallableUnit.PROP_PROVIDER, null);
-					if (EXPECTED_PROVIDER_NAME.equals(providerName)) {
-						correctProviderName.add(iu);
-					}
-					else if (OLD_PROVIDER_NAME.equals(providerName)) {
-						incorrectOldProviderName.add(iu);
-					}
-					else {
-						incorrectProviderName.add(iu);
-					}
-					// experiment to find configs and categories
-					if (DEBUG) {
-						if (providerName == null) {
-							printProperties(outfileWriter, iu);
+				try {
+					// ignore categories
+					boolean isCategory = "true"
+							.equals(iu
+									.getProperty("org.eclipse.equinox.p2.type.category"));
+					// TODO: should we exclude fragments?
+					boolean isFragment = "true"
+							.equals(iu
+									.getProperty("org.eclipse.equinox.p2.type.fragment"));
+					// TODO: what are these special things? What ever they are,
+					// they have no provider name.
+					// config.a.jre is identified as a fragment
+					// (org.eclipse.equinox.p2.type.fragment).
+					// a.jre has no properties.
+					boolean isSpecial = "a.jre".equals(iu.getId())
+							|| "config.a.jre".equals(iu.getId());
+					if (!isCategory && !isSpecial) {
+						String providerName = iu.getProperty(
+								IInstallableUnit.PROP_PROVIDER, null);
+						if (EXPECTED_PROVIDER_NAME.equals(providerName)) {
+							correctProviderName.add(iu);
+						} else if (OLD_PROVIDER_NAME.equals(providerName)) {
+							incorrectOldProviderName.add(iu);
+						} else {
+							incorrectProviderName.add(iu);
+						}
+						// experiment to find configs and categories
+						if (DEBUG) {
+							if (providerName == null) {
+								printProperties(outfileWriter, iu);
+							}
 						}
 					}
+
+				} catch (RuntimeException e) {
+					e.printStackTrace();
 				}
 			}
 
-			outfileWriter.write("<h1>Provider names used in latest build</h1>" + EOL);
-			outfileWriter.write("<h2>Major, missing, or incorrect provider name</h2>" + EOL);
+			outfileWriter.write("<h1>Provider names used in latest build</h1>"
+					+ EOL);
+			outfileWriter
+					.write("<h2>Major, missing, or incorrect provider name</h2>"
+							+ EOL);
 			printLines(outfileWriter, incorrectProviderName);
-			outfileWriter.write("<h2>Minor problem of using old provider name</h2>" + EOL);
+			outfileWriter
+					.write("<h2>Minor problem of using old provider name</h2>"
+							+ EOL);
 			printLines(outfileWriter, incorrectOldProviderName);
 			outfileWriter.write("<h2>Using correct provider name</h2>" + EOL);
 			printLines(outfileWriter, correctProviderName);
 
 			if (incorrectProviderName.size() > 0) {
-				fail("Errors in consistent, correct provider name. For list, see " + outfile.getAbsolutePath());
+				fail("Errors in consistent, correct provider name. For list, see "
+						+ outfile.getAbsolutePath());
 			}
-		}
-		finally {
+		} finally {
 			if (outfileWriter != null) {
 				try {
 					outfileWriter.close();
-				}
-				catch (IOException e) {
+				} catch (IOException e) {
 					// would be weird
 					e.printStackTrace();
 				}
@@ -278,8 +307,33 @@
 		}
 	}
 
-	private void printLines(FileWriter out, List<IInstallableUnit> iuList) throws IOException {
-		Collections.sort(iuList);
+	private void printLines(FileWriter out, List<IInstallableUnit> iuList)
+			throws IOException {
+		Comparator<? super IInstallableUnit> comparatorProviderName = new Comparator<IInstallableUnit>() {
+
+			public int compare(IInstallableUnit iu1, IInstallableUnit iu2) {
+				// neither iu should be null ... but, just to cover all cases
+				if (iu1 == null && iu2 == null) {
+					return 0;
+				} else if (iu1 == null || iu2 == null) {
+					return 1;
+				} else {
+					String p1 = iu1.getProperty(IInstallableUnit.PROP_PROVIDER,
+							null);
+					String p2 = iu2.getProperty(IInstallableUnit.PROP_PROVIDER,
+							null);
+					if (p1 == null) {
+						p1 = "null";
+					}
+					if (p2 == null) {
+						p2 = "null";
+					}
+					return (p1.compareTo(p2));
+				}
+			}
+
+		};
+		Collections.sort(iuList, comparatorProviderName);
 		out.write("<p>Count: " + iuList.size() + EOL);
 		out.write("<ol>" + EOL);
 
@@ -289,7 +343,8 @@
 		out.write("</ol>" + EOL);
 	}
 
-	private void printLinesCopyright(FileWriter out, List<IInstallableUnit> iuList) throws IOException {
+	private void printLinesCopyright(FileWriter out,
+			List<IInstallableUnit> iuList) throws IOException {
 		Collections.sort(iuList);
 		out.write("<p>Count: " + iuList.size() + EOL);
 		out.write("<ol>" + EOL);
@@ -300,14 +355,18 @@
 		out.write("</ol>" + EOL);
 	}
 
-	private void printLine(FileWriter outfileWriter, IInstallableUnit iu) throws IOException {
-		String providerName = iu.getProperty(IInstallableUnit.PROP_PROVIDER, null);
+	private void printLine(FileWriter outfileWriter, IInstallableUnit iu)
+			throws IOException {
+		String providerName = iu.getProperty(IInstallableUnit.PROP_PROVIDER,
+				null);
 		String iuId = iu.getId();
 		String iuVersion = iu.getVersion().toString();
-		println(outfileWriter, providerName + NBSP + iuId + NBSP + iuVersion + NBSP);
+		println(outfileWriter, providerName + NBSP + iuId + NBSP + iuVersion
+				+ NBSP);
 	}
 
-	private void printLineCopyright(FileWriter outfileWriter, IInstallableUnit iu) throws IOException {
+	private void printLineCopyright(FileWriter outfileWriter,
+			IInstallableUnit iu) throws IOException {
 
 		String copyright = null;
 		ICopyright copyrightIu = iu.getCopyright(null);
@@ -316,30 +375,36 @@
 		}
 		String iuId = iu.getId();
 		String iuVersion = iu.getVersion().toString();
-		println(outfileWriter, copyright + NBSP + iuId + NBSP + iuVersion + NBSP);
+		println(outfileWriter, copyright + NBSP + iuId + NBSP + iuVersion
+				+ NBSP);
 	}
 
-	private void printProperties(FileWriter outFileWriter, IInstallableUnit iu) throws IOException {
+	private void printProperties(FileWriter outFileWriter, IInstallableUnit iu)
+			throws IOException {
 		Map<String, String> properties = iu.getProperties();
 		Set keys = properties.keySet();
 		for (Object key : keys) {
 			String value = properties.get(key);
-			println(outFileWriter, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + key + " : " + value);
+			println(outFileWriter, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + key
+					+ " : " + value);
 		}
 
 	}
 
-	public void testProviderNames() throws URISyntaxException, ProvisionException, OperationCanceledException, IOException {
+	public void testProviderNames() throws URISyntaxException,
+			ProvisionException, OperationCanceledException, IOException {
 		IQueryResult<IInstallableUnit> allIUs = getAllIUs();
 		checkProviderNames(allIUs);
 	}
 
-	public void testCopyrights() throws URISyntaxException, ProvisionException, OperationCanceledException, IOException {
+	public void testCopyrights() throws URISyntaxException, ProvisionException,
+			OperationCanceledException, IOException {
 		IQueryResult<IInstallableUnit> allIUs = getAllIUs();
 		checkCopyrights(allIUs);
 	}
 
-	private IQueryResult<IInstallableUnit> getAllIUs() throws URISyntaxException, ProvisionException {
+	private IQueryResult<IInstallableUnit> getAllIUs()
+			throws URISyntaxException, ProvisionException {
 		String repoURL = System.getProperty("repoURLToTest");
 
 		if (repoURL == null) {
@@ -347,14 +412,17 @@
 		}
 		System.out.println("repoURLToTest: " + repoURL);
 		URI repoLocation = new URI(repoURL);
-		IMetadataRepository repo = getMetadataRepositoryManager().loadRepository(repoLocation, null);
+		IMetadataRepository repo = getMetadataRepositoryManager()
+				.loadRepository(repoLocation, null);
 		assertNotNull("no repository found at " + repoLocation.toString(), repo);
-		IQueryResult<IInstallableUnit> allIUs = repo.query(QueryUtil.createIUAnyQuery(), null);
+		IQueryResult<IInstallableUnit> allIUs = repo.query(QueryUtil
+				.createIUAnyQuery(), null);
 		assertFalse(allIUs.isEmpty());
 		return allIUs;
 	}
 
-	private void checkCopyrights(IQueryResult<IInstallableUnit> allIUs) throws IOException {
+	private void checkCopyrights(IQueryResult<IInstallableUnit> allIUs)
+			throws IOException {
 		FileWriter outfileWriter = null;
 		File outfile = null;
 		List<IInstallableUnit> correctcopyright = new ArrayList<IInstallableUnit>();
@@ -364,40 +432,45 @@
 		try {
 			outfile = new File(testDirName, "copyrights.html");
 			outfileWriter = new FileWriter(outfile);
-
+			System.out.println("output: " + outfile.getAbsolutePath());
 			for (IInstallableUnit iu : allIUs.toUnmodifiableSet()) {
 				// ignore categories
-				boolean isCategory = "true".equals(iu.getProperty("org.eclipse.equinox.p2.type.category"));
+				boolean isCategory = "true".equals(iu
+						.getProperty("org.eclipse.equinox.p2.type.category"));
 				// TODO: should we exclude fragments?
-				boolean isFragment = "true".equals(iu.getProperty("org.eclipse.equinox.p2.type.fragment"));
+				boolean isFragment = "true".equals(iu
+						.getProperty("org.eclipse.equinox.p2.type.fragment"));
 				// TODO: what are these special things? What ever they are,
 				// they have no provider name.
 				// config.a.jre is identified as a fragment
 				// (org.eclipse.equinox.p2.type.fragment).
 				// a.jre has no properties.
-				boolean isSpecial = "a.jre".equals(iu.getId()) || "config.a.jre".equals(iu.getId());
+				boolean isSpecial = "a.jre".equals(iu.getId())
+						|| "config.a.jre".equals(iu.getId());
 				if (!isCategory && !isSpecial) {
 					ICopyright copyright = iu.getCopyright(null);
 					if (copyright == null) {
 						nocopyright.add(iu);
 					}
 					/*
-					 * This heuristic may need to be improved. Note that case
-					 * is important, as a common error is to have the property
+					 * This heuristic may need to be improved. Note that case is
+					 * important, as a common error is to have the property
 					 * 'copyright' but no text.
 					 */
 					else if (copyright.getBody().trim().startsWith("Copyright")) {
 						correctcopyright.add(iu);
-					}
-					else {
+					} else {
 						incorrectcopyright.add(iu);
 					}
 
 				}
 			}
 
-			outfileWriter.write("<h1>Copyrights used in latest build</h1>" + EOL);
-			outfileWriter.write("<p>This (simple) test is base on heuristic that a valid copyright is not null and starts with 'Copyright'. </p>" + EOL);
+			outfileWriter.write("<h1>Copyrights used in latest build</h1>"
+					+ EOL);
+			outfileWriter
+					.write("<p>This (simple) test is base on heuristic that a valid copyright is not null and starts with 'Copyright'. </p>"
+							+ EOL);
 			outfileWriter.write("<h2>Major, incorrect copyright?</h2>" + EOL);
 			printLinesCopyright(outfileWriter, incorrectcopyright);
 			outfileWriter.write("<h2>No copyright. </h2>" + EOL);
@@ -408,15 +481,14 @@
 			// note we don't treat missing ones as errors. I'm not sure it is
 			// _required_ for all bundles?
 			if (incorrectcopyright.size() > 0) {
-				fail("Errors in copyright statement? For list, see " + outfile.getAbsolutePath());
+				fail("Errors in copyright statement? For list, see "
+						+ outfile.getAbsolutePath());
 			}
-		}
-		finally {
+		} finally {
 			if (outfileWriter != null) {
 				try {
 					outfileWriter.close();
-				}
-				catch (IOException e) {
+				} catch (IOException e) {
 					// would be weird
 					e.printStackTrace();
 				}
@@ -425,11 +497,13 @@
 	}
 
 	protected static IMetadataRepositoryManager getMetadataRepositoryManager() {
-		return (IMetadataRepositoryManager) getAgent().getService(IMetadataRepositoryManager.SERVICE_NAME);
+		return (IMetadataRepositoryManager) getAgent().getService(
+				IMetadataRepositoryManager.SERVICE_NAME);
 	}
 
 	protected static IProvisioningAgent getAgent() {
 		// get the global agent for the currently running system
-		return (IProvisioningAgent) ServiceHelper.getService(TestActivator.getContext(), IProvisioningAgent.SERVICE_NAME);
+		return (IProvisioningAgent) ServiceHelper.getService(TestActivator
+				.getContext(), IProvisioningAgent.SERVICE_NAME);
 	}
 }