static int hashCode(OpenMBeanParameterInfo info) {
   int value = 0;
   value += info.getName().hashCode();
   value += info.getOpenType().hashCode();
   if (info.hasDefaultValue()) value += info.getDefaultValue().hashCode();
   if (info.hasMinValue()) value += info.getMinValue().hashCode();
   if (info.hasMaxValue()) value += info.getMaxValue().hashCode();
   if (info.hasLegalValues()) value += info.getLegalValues().hashCode();
   if (info instanceof DescriptorRead) value += ((DescriptorRead) info).getDescriptor().hashCode();
   return value;
 }
 @SuppressWarnings({"unchecked", "rawtypes"}) // cast to Comparable
 static boolean isValue(OpenMBeanParameterInfo info, Object obj) {
   if (info.hasDefaultValue() && obj == null) return true;
   return info.getOpenType().isValue(obj)
       && (!info.hasLegalValues() || info.getLegalValues().contains(obj))
       && (!info.hasMinValue() || ((Comparable) info.getMinValue()).compareTo(obj) <= 0)
       && (!info.hasMaxValue() || ((Comparable) info.getMaxValue()).compareTo(obj) >= 0);
 }
 static String toString(OpenMBeanParameterInfo info) {
   Descriptor d =
       (info instanceof DescriptorRead) ? ((DescriptorRead) info).getDescriptor() : null;
   return info.getClass().getName()
       + "(name="
       + info.getName()
       + ",openType="
       + info.getOpenType()
       + ",default="
       + info.getDefaultValue()
       + ",minValue="
       + info.getMinValue()
       + ",maxValue="
       + info.getMaxValue()
       + ",legalValues="
       + info.getLegalValues()
       + ((d == null) ? "" : ",descriptor=" + d)
       + ")";
 }
  static boolean equal(OpenMBeanParameterInfo x1, OpenMBeanParameterInfo x2) {
    if (x1 instanceof DescriptorRead) {
      if (!(x2 instanceof DescriptorRead)) return false;
      Descriptor d1 = ((DescriptorRead) x1).getDescriptor();
      Descriptor d2 = ((DescriptorRead) x2).getDescriptor();
      if (!d1.equals(d2)) return false;
    } else if (x2 instanceof DescriptorRead) return false;

    return x1.getName().equals(x2.getName())
        && x1.getOpenType().equals(x2.getOpenType())
        && (x1.hasDefaultValue()
            ? x1.getDefaultValue().equals(x2.getDefaultValue())
            : !x2.hasDefaultValue())
        && (x1.hasMinValue() ? x1.getMinValue().equals(x2.getMinValue()) : !x2.hasMinValue())
        && (x1.hasMaxValue() ? x1.getMaxValue().equals(x2.getMaxValue()) : !x2.hasMaxValue())
        && (x1.hasLegalValues()
            ? x1.getLegalValues().equals(x2.getLegalValues())
            : !x2.hasLegalValues());
  }
  static void check(OpenMBeanParameterInfo info) throws OpenDataException {
    OpenType<?> openType = info.getOpenType();
    if (openType == null) throw new IllegalArgumentException("OpenType cannot be null");

    if (info.getName() == null || info.getName().trim().equals(""))
      throw new IllegalArgumentException("Name cannot be null or empty");

    if (info.getDescription() == null || info.getDescription().trim().equals(""))
      throw new IllegalArgumentException("Description cannot be null or empty");

    // Check and initialize defaultValue
    //
    if (info.hasDefaultValue()) {
      // Default value not supported for ArrayType and TabularType
      // Cast to Object because "OpenType<T> instanceof" is illegal
      if (openType.isArray() || (Object) openType instanceof TabularType) {
        throw new OpenDataException(
            "Default value not supported " + "for ArrayType and TabularType");
      }
      // Check defaultValue's class
      if (!openType.isValue(info.getDefaultValue())) {
        final String msg =
            "Argument defaultValue's class [\""
                + info.getDefaultValue().getClass().getName()
                + "\"] does not match the one defined in openType[\""
                + openType.getClassName()
                + "\"]";
        throw new OpenDataException(msg);
      }
    }

    // Check that we don't have both legalValues and min or max
    //
    if (info.hasLegalValues() && (info.hasMinValue() || info.hasMaxValue())) {
      throw new OpenDataException("cannot have both legalValue and " + "minValue or maxValue");
    }

    // Check minValue and maxValue
    if (info.hasMinValue() && !openType.isValue(info.getMinValue())) {
      final String msg =
          "Type of minValue ["
              + info.getMinValue().getClass().getName()
              + "] does not match OpenType ["
              + openType.getClassName()
              + "]";
      throw new OpenDataException(msg);
    }
    if (info.hasMaxValue() && !openType.isValue(info.getMaxValue())) {
      final String msg =
          "Type of maxValue ["
              + info.getMaxValue().getClass().getName()
              + "] does not match OpenType ["
              + openType.getClassName()
              + "]";
      throw new OpenDataException(msg);
    }

    // Check that defaultValue is a legal value
    //
    if (info.hasDefaultValue()) {
      Object defaultValue = info.getDefaultValue();
      if (info.hasLegalValues() && !info.getLegalValues().contains(defaultValue)) {
        throw new OpenDataException("defaultValue is not contained " + "in legalValues");
      }

      // Check that minValue <= defaultValue <= maxValue
      //
      if (info.hasMinValue()) {
        if (compare(info.getMinValue(), defaultValue) > 0) {
          throw new OpenDataException("minValue cannot be greater " + "than defaultValue");
        }
      }
      if (info.hasMaxValue()) {
        if (compare(info.getMaxValue(), defaultValue) < 0) {
          throw new OpenDataException("maxValue cannot be less " + "than defaultValue");
        }
      }
    }

    // Check legalValues
    //
    if (info.hasLegalValues()) {
      // legalValues not supported for TabularType and arrays
      if ((Object) openType instanceof TabularType || openType.isArray()) {
        throw new OpenDataException("Legal values not supported " + "for TabularType and arrays");
      }
      // Check legalValues are valid with openType
      for (Object v : info.getLegalValues()) {
        if (!openType.isValue(v)) {
          final String msg =
              "Element of legalValues ["
                  + v
                  + "] is not a valid value for the specified openType ["
                  + openType.toString()
                  + "]";
          throw new OpenDataException(msg);
        }
      }
    }

    // Check that, if both specified, minValue <= maxValue
    //
    if (info.hasMinValue() && info.hasMaxValue()) {
      if (compare(info.getMinValue(), info.getMaxValue()) > 0) {
        throw new OpenDataException("minValue cannot be greater " + "than maxValue");
      }
    }
  }