docs: protected static Boolean compare( // was BooleanValue
      Object val0,
      Object val1,
      boolean lessThan,
      boolean permissive)
    {
      if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
        tc.entry(cclass, 
                 "compare", 
                 new Object[] { val0, val1, new Boolean(lessThan), new Boolean(permissive) });
      
      Boolean theReturn = null;
      // Encode the types of the two arguments into sixteen cases based on the classification
      // Number/Boolean/String/Serializable.  All other types (and nulls) result in the NULL
      // answer.
      int ans0 =
        (val0 instanceof Number) // was NumericValue
          ? 0
          : (val0 instanceof Boolean) // was BooleanValue
          ? 1
          : (val0 instanceof String)
          ? 2
          : (val0 instanceof Serializable) 
          ? 3
          : 4;
      int ans1 =
        (val1 instanceof Number) // was NumericValue
          ? 0
          : (val1 instanceof Boolean) // was BooleanValue
          ? 1
          : (val1 instanceof String)
          ? 2
          : (val1 instanceof Serializable) 
          ? 3
          : 4;
      if (ans0 > 3 || ans1 > 3)
      {
        if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
          tc.exit(cclass, "compare", null);       
        return null;
      }
      int category = ans0 * 4 + ans1;
      // Categories:        val1
      // val0     Number Boolean String Object
      // Number      0      1       2      3
      // Boolean     4      5       6      7
      // String      8      9      10     11
      // Object     12     13      14     15
      // For the permissive mode (only) do the permissive mode casts according to the
      // category.
      if (permissive)
        switch (category)
        {
          case 0 :
          case 5 :
          case 10 :
          case 15 :
            break; // equal types: no casts needed
          case 2 : // Number/String: attempt to cast val1 to number
            val1 = castToNumber(val1);
            if (val1 == null)
            {
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", null); 
              return null;
            }
            category = 0;
            break;
          case 6 : // Boolean/String: attempt to cast val1 to Boolean
            val1 = castToBoolean(val1);
            if (val1 == null)
            {
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", null); 
              return null;
            }
            category = 5;
            break;
          case 8 : // String/Number: attempt to cast val0 to number
            val0 = castToNumber(val0);
            if (val0 == null)
            {
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", null); 
              return null;
            }
            category = 0;
            break;
          case 9 : // String/Boolean: attempt to cast val0 to Boolean
            val0 = castToBoolean(val0);
            if (val0 == null)
            {
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", null); 
              return null;
            }
            category = 4;
            break;
          default : // Other cases have no legal casts, even in permissive mode
            if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
              tc.exit(cclass, "compare", null); 
            return null;
        }
      // Now do the actual evalution
      switch (category)
      {
        case 0 : // both types are Number.  Do the arithimetic in the appropriate precision
          // depending on the Java binary promotion rules.
          int comp = compare((Number) val0, (Number) val1);
          if (lessThan)
            theReturn = Boolean.valueOf(comp < 0);
          else
            theReturn = Boolean.valueOf(comp == 0);
          if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
            tc.exit(cclass, "compare", theReturn);           
          return theReturn; 
        case 5 :
        case 10:
          if (lessThan)
            if(permissive)
            {
              val1 = castToNumber(val1);
              if (val1 == null)
              {
                if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                  tc.exit(cclass, "compare", null); 
                return null;
              }
              val0 = castToNumber(val0);
              if (val0 == null)
              {
                if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                  tc.exit(cclass, "compare", null); 
                return null;
              }
              int comp2 = compare((Number) val0, (Number) val1); // was ((NumericValue) val0).compareTo(val1)
              theReturn = Boolean.valueOf(comp2 < 0); // was BooleanValue
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", theReturn);           
              return theReturn;             
            }
            else
            {
              if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
                tc.exit(cclass, "compare", null); 
              return null;
            }
        case 15:
          // Both types are comparable and non-Null, but only equality is defined
          if (lessThan)
          {
            if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
              tc.exit(cclass, "compare", null); 
            return null;
          }
          else
          {
            theReturn = Boolean.valueOf(val0.equals(val1));
            if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
              tc.exit(cclass, "compare", theReturn);           
            return theReturn; 
          }
        default : // All other cases are disallowed
          if (tc.isAnyTracingEnabled() && tc.isEntryEnabled())
            tc.exit(cclass, "compare", null); 
          return null;
      }
    }