From 1b481b5eaabfc0690bb13f0a2caaef1becf2110b Mon Sep 17 00:00:00 2001
From: coulbeck <coulbeck@localhost>
Date: Mon, 09 Jul 2007 22:21:47 +0000
Subject: [PATCH] Fix for issue #1737: definitions of message IDs for some categories span multiple files. This changes the category of some messages to match the class in which they are defined. It also adds a unit test to prevent categories from spanning multiple files in future.

---
 opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java                                      |   80 ++++++++--------
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/messages/CategoriesSpanFilesTestCase.java |  167 +++++++++++++++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java                                        |    6 
 opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java                                        |    2 
 opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java                                    |    2 
 5 files changed, 212 insertions(+), 45 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
index 8e73f83..8d0b6a2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -3726,7 +3726,7 @@
    * representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_GET_MAC =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 386;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 386;
 
 
 
@@ -3737,7 +3737,7 @@
    * string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_GET_DIGEST =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 387;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 387;
 
 
 
@@ -3749,7 +3749,7 @@
    * caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_CREATE_ARCHIVE_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 388;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 388;
 
 
 
@@ -3760,7 +3760,7 @@
    * representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_GET_CIPHER =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 389;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 389;
 
 
 
@@ -3770,7 +3770,7 @@
    * arguments, which are the Directory Server product name and the backup ID.
    */
   public static final int MSGID_CONFIG_BACKUP_ZIP_COMMENT =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 390;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 390;
 
 
 
@@ -3782,7 +3782,7 @@
    */
   public static final int
        MSGID_CONFIG_BACKUP_CANNOT_DETERMINE_CONFIG_FILE_LOCATION =
-            CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 391;
+            CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 391;
 
 
 
@@ -3793,7 +3793,7 @@
    * that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_BACKUP_CONFIG_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 392;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 392;
 
 
 
@@ -3804,7 +3804,7 @@
    * exception that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_BACKUP_ARCHIVED_CONFIGS =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 393;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 393;
 
 
 
@@ -3816,7 +3816,7 @@
    * exception that was caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_CLOSE_ZIP_STREAM =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 394;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 394;
 
 
 
@@ -3828,7 +3828,7 @@
    * caught.
    */
   public static final int MSGID_CONFIG_BACKUP_CANNOT_UPDATE_BACKUP_DESCRIPTOR =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 395;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 395;
 
 
 
@@ -3839,7 +3839,7 @@
    * directory.
    */
   public static final int MSGID_CONFIG_RESTORE_NO_SUCH_BACKUP =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 396;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 396;
 
 
 
@@ -3850,7 +3850,7 @@
    * path to the backup directory.
    */
   public static final int MSGID_CONFIG_RESTORE_NO_BACKUP_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 397;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 397;
 
 
 
@@ -3861,7 +3861,7 @@
    * file.
    */
   public static final int MSGID_CONFIG_RESTORE_NO_SUCH_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 398;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 398;
 
 
 
@@ -3872,7 +3872,7 @@
    * archive, and a string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_CHECK_FOR_ARCHIVE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 399;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 399;
 
 
 
@@ -3882,7 +3882,7 @@
    * argument, which is the backup ID.
    */
   public static final int MSGID_CONFIG_RESTORE_UNKNOWN_DIGEST =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 400;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 400;
 
 
 
@@ -3892,7 +3892,7 @@
    * arguments, which are the backup ID and the digest algorithm.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_GET_DIGEST =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 401;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 401;
 
 
 
@@ -3902,7 +3902,7 @@
    * which is the backup ID.
    */
   public static final int MSGID_CONFIG_RESTORE_UNKNOWN_MAC =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 402;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 402;
 
 
 
@@ -3912,7 +3912,7 @@
    * arguments, which are the backup ID and the MAC algorithm.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_GET_MAC =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 403;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 403;
 
 
 
@@ -3923,7 +3923,7 @@
    * string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_OPEN_BACKUP_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 404;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 404;
 
 
 
@@ -3933,7 +3933,7 @@
    * is the backup ID.
    */
   public static final int MSGID_CONFIG_RESTORE_UNKNOWN_CIPHER =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 405;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 405;
 
 
 
@@ -3943,7 +3943,7 @@
    * which are the backup ID and the cipher algorithm.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_GET_CIPHER =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 406;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 406;
 
 
 
@@ -3955,7 +3955,7 @@
    * representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_BACKUP_EXISTING_CONFIG =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 407;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 407;
 
 
 
@@ -3966,7 +3966,7 @@
    * config directory.
    */
   public static final int MSGID_CONFIG_RESTORE_RESTORED_OLD_CONFIG =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_NOTICE | 408;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_NOTICE | 408;
 
 
 
@@ -3977,7 +3977,7 @@
    * to the directory containing the original config files.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_RESTORE_OLD_CONFIG =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 409;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 409;
 
 
 
@@ -3989,7 +3989,7 @@
    * caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_CREATE_CONFIG_DIRECTORY =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 410;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 410;
 
 
 
@@ -4000,7 +4000,7 @@
    * to the directory containing the original config files.
    */
   public static final int MSGID_CONFIG_RESTORE_OLD_CONFIG_SAVED =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 411;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 411;
 
 
 
@@ -4011,7 +4011,7 @@
    * string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_GET_ZIP_ENTRY =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 412;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 412;
 
 
 
@@ -4022,7 +4022,7 @@
    * created, and a string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_CREATE_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 413;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 413;
 
 
 
@@ -4034,7 +4034,7 @@
    * caught.
    */
   public static final int MSGID_CONFIG_RESTORE_CANNOT_PROCESS_ARCHIVE_FILE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 414;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 414;
 
 
 
@@ -4045,7 +4045,7 @@
    * and a string representation of the exception that was caught.
    */
   public static final int MSGID_CONFIG_RESTORE_ERROR_ON_ZIP_STREAM_CLOSE =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 415;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 415;
 
 
 
@@ -4055,7 +4055,7 @@
    * arguments.
    */
   public static final int MSGID_CONFIG_RESTORE_UNSIGNED_HASH_VALID =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_NOTICE | 416;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_NOTICE | 416;
 
 
 
@@ -4065,7 +4065,7 @@
    * argument, which is the backup ID.
    */
   public static final int MSGID_CONFIG_RESTORE_UNSIGNED_HASH_INVALID =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 417;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 417;
 
 
 
@@ -4075,7 +4075,7 @@
    * arguments.
    */
   public static final int MSGID_CONFIG_RESTORE_SIGNED_HASH_VALID =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_NOTICE | 418;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_NOTICE | 418;
 
 
 
@@ -4085,7 +4085,7 @@
    * argument, which is the backup ID.
    */
   public static final int MSGID_CONFIG_RESTORE_SIGNED_HASH_INVALID =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 419;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 419;
 
 
 
@@ -4095,7 +4095,7 @@
    * arguments, which are the backup ID and the path to the backup directory.
    */
   public static final int MSGID_CONFIG_RESTORE_VERIFY_SUCCESSFUL =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_NOTICE | 420;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_NOTICE | 420;
 
 
 
@@ -4105,7 +4105,7 @@
    * arguments, which are the backup ID and the path to the backup directory.
    */
   public static final int MSGID_CONFIG_RESTORE_SUCCESSFUL =
-       CATEGORY_MASK_BACKEND | SEVERITY_MASK_NOTICE | 421;
+       CATEGORY_MASK_CONFIG | SEVERITY_MASK_NOTICE | 421;
 
 
 
@@ -5651,11 +5651,11 @@
 
   /**
    * The message ID of an error indicating that the file permissions for the
-   * database directory will result in an inaccessable database. The orginal or
-   * default value will be used instead
+   * database directory will result in an inaccessible database. The orginal or
+   * default value will be used instead.
    */
   public static final int MSGID_CONFIG_BACKEND_INSANE_MODE =
-      CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_WARNING | 568;
+      CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 568;
 
 
 
@@ -9621,7 +9621,7 @@
     registerMessage(MSGID_CONFIG_BACKEND_INSANE_MODE,
                    "Unable to set the requested file permissions to the " +
                    "backend database directory. The requested permissions " +
-                   "will result in an inaccessable database");
+                   "will result in an inaccessible database");
 
 
     registerMessage(MSGID_CONFIG_GROUP_CANNOT_GET_BASE,
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
index 27e6712..54350d9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -3268,7 +3268,7 @@
    * problem that occurred.
    */
   public static final int MSGID_SHUTDOWN_CANNOT_RELEASE_SHARED_BACKEND_LOCK =
-       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 342;
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 342;
 
 
 
@@ -3290,7 +3290,7 @@
    * could not be released.
    */
   public static final int MSGID_CANNOT_RELEASE_EXCLUSIVE_SERVER_LOCK =
-       CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 344;
+       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 344;
 
 
 
@@ -6001,7 +6001,7 @@
    * are the filter string and the start and end position of the NOT filter.
    */
   public static final int MSGID_SEARCH_FILTER_NOT_EXACTLY_ONE =
-       CATEGORY_MASK_PROTOCOL | SEVERITY_MASK_MILD_ERROR | 602;
+       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 602;
 
 
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java
index 0912149..3d93db9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ProtocolMessages.java
@@ -4604,7 +4604,7 @@
    * problem that was encountered.
    */
   public static final int MSGID_CANNOT_DECODE_GETEFFECTIVERIGHTS_AUTHZID_DN =
-       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 426;
+       CATEGORY_MASK_PROTOCOL | SEVERITY_MASK_INFORMATIONAL | 426;
 
 
   /**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
index 9b3992a..9b8d828 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -6641,7 +6641,7 @@
    * windowsNetStop command-line argument.  This does not take any arguments.
    */
   public static final int MSGID_STOPDS_DESCRIPTION_WINDOWS_NET_STOP =
-       CATEGORY_MASK_CORE | SEVERITY_MASK_INFORMATIONAL | 838;
+       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 838;
 
   /**
    * The message ID for the message that will be used as the description for the
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/messages/CategoriesSpanFilesTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/messages/CategoriesSpanFilesTestCase.java
new file mode 100644
index 0000000..92636f4
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/messages/CategoriesSpanFilesTestCase.java
@@ -0,0 +1,167 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.messages;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import org.opends.server.TestCaseUtils;
+
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.HashSet;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * This class provides a mechanism for determining whether messages
+ * in a given category are defined in a single class file.
+ */
+public class CategoriesSpanFilesTestCase
+     extends MessagesTestCase
+{
+  /**
+   * Look in the build filesystem for files in the org.opends.server.messages
+   * package.  Dynamically load all of those classes and use reflection to look
+   * at all fields in those classes.  Make sure that messages in a given
+   * category are defined in a single class file.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testCategories()
+         throws Exception
+  {
+    // Construct a list of classes in the messages package.
+    String s = File.separator;
+    String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
+    String messageClassesDir = buildRoot + s + "build" + s + "classes" + s +
+                               "org" + s + "opends" + s + "server" + s +
+                               "messages";
+    File f = new File(messageClassesDir);
+    LinkedList<String> classNames = new LinkedList<String>();
+    for (String filename : f.list())
+    {
+      if (! filename.endsWith(".class"))
+      {
+        continue;
+      }
+
+      classNames.add("org.opends.server.messages." +
+                     filename.substring(0, filename.length()-6));
+    }
+
+    assertFalse(classNames.isEmpty());
+
+    // Construct a map from category mask value to user-friendly string.
+    HashMap<Integer,String> categoryMap = new HashMap<Integer, String>(100);
+    Class categoryDefnClass =
+         Class.forName("org.opends.server.messages.MessageHandler");
+    for (Field field : categoryDefnClass.getDeclaredFields())
+    {
+      if (field.getType().getName().equals("int"))
+      {
+        if (Modifier.isStatic(field.getModifiers()) &&
+            Modifier.isFinal(field.getModifiers()) &&
+            field.getName().startsWith("CATEGORY_MASK_"))
+        {
+          int    category = field.getInt(null);
+          categoryMap.put(category, field.getName());
+        }
+      }
+    }
+
+    // Construct a map from category mask value to the list of classes
+    // containing messages of that category.
+    HashMap<Integer,ArrayList<String>> classesByCategory =
+         new HashMap<Integer, ArrayList<String>>(100);
+    for (String className : classNames)
+    {
+      Class c = Class.forName(className);
+      HashSet<Integer> categories = new HashSet<Integer>(1);
+      for (Field field : c.getDeclaredFields())
+      {
+        if (field.getType().getName().equals("int"))
+        {
+          if (Modifier.isStatic(field.getModifiers()) &&
+              Modifier.isFinal(field.getModifiers()) &&
+              field.getName().startsWith("MSGID_"))
+          {
+            int    fieldValue = field.getInt(null);
+            int    category = fieldValue & 0xFFF00000;
+            categories.add(category);
+          }
+        }
+      }
+      for (Integer category : categories)
+      {
+        ArrayList<String> classes = classesByCategory.get(category);
+        if (classes == null)
+        {
+          classes = new ArrayList<String>(1);
+          classes.add(className);
+          classesByCategory.put(category, classes);
+        }
+        else
+        {
+          classes.add(className);
+        }
+      }
+    }
+
+    // Construct an error message for any categories having multiple classes.
+    StringBuilder buffer = new StringBuilder();
+    for (Map.Entry<Integer,ArrayList<String>> entry :
+         classesByCategory.entrySet())
+    {
+      ArrayList<String> classes = entry.getValue();
+      if (classes.size() > 1)
+      {
+        buffer.append("Messages of category ");
+        buffer.append(categoryMap.get(entry.getKey()));
+        buffer.append(" are defined in multiple classes: ");
+        Iterator<String> iterator = classes.iterator();
+        buffer.append(iterator.next());
+
+        while (iterator.hasNext())
+        {
+          buffer.append(", ");
+          buffer.append(iterator.next());
+        }
+
+        buffer.append(". ");
+      }
+    }
+
+    assertTrue(buffer.length() == 0, buffer.toString());
+  }
+}

--
Gitblit v1.10.0