/* * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.management; import com.sun.jmx.mbeanserver.Util; import java.io.InvalidObjectException; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Comparator; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * An immutable descriptor. * @since 1.6 */ public class ImmutableDescriptor implements Descriptor { private static final long serialVersionUID = 8853308591080540165L; /** * The names of the fields in this ImmutableDescriptor with their * original case. The names must be in alphabetical order as determined * by {@link String#CASE_INSENSITIVE_ORDER}. */ private final String[] names; /** * The values of the fields in this ImmutableDescriptor. The * elements in this array match the corresponding elements in the * {@code names} array. */ private final Object[] values; private transient int hashCode = -1; /** * An empty descriptor. */ public static final ImmutableDescriptor EMPTY_DESCRIPTOR = new ImmutableDescriptor(); /** * Construct a descriptor containing the given fields and values. * * @throws IllegalArgumentException if either array is null, or * if the arrays have different sizes, or * if a field name is null or empty, or if the same field name * appears more than once. */ public ImmutableDescriptor(String[] fieldNames, Object[] fieldValues) { this(makeMap(fieldNames, fieldValues)); } /** * Construct a descriptor containing the given fields. Each String * must be of the form {@code fieldName=fieldValue}. The field name * ends at the first {@code =} character; for example if the String * is {@code a=b=c} then the field name is {@code a} and its value * is {@code b=c}. * * @throws IllegalArgumentException if the parameter is null, or * if a field name is empty, or if the same field name appears * more than once, or if one of the strings does not contain * an {@code =} character. */ public ImmutableDescriptor(String... fields) { this(makeMap(fields)); } /** *
Construct a descriptor where the names and values of the fields * are the keys and values of the given Map.
* * @throws IllegalArgumentException if the parameter is null, or * if a field name is null or empty, or if the same field name appears * more than once (which can happen because field names are not case * sensitive). */ public ImmutableDescriptor(MapReturn an {@code ImmutableDescriptor} whose contents are the union of * the given descriptors. Every field name that appears in any of * the descriptors will appear in the result with the * value that it has when the method is called. Subsequent changes * to any of the descriptors do not affect the ImmutableDescriptor * returned here.
* *In the simplest case, there is only one descriptor and the * returned {@code ImmutableDescriptor} is a copy of its fields at the * time this method is called:
* ** Descriptor d = something(); * ImmutableDescriptor copy = ImmutableDescriptor.union(d); ** * @param descriptors the descriptors to be combined. Any of the * descriptors can be null, in which case it is skipped. * * @return an {@code ImmutableDescriptor} that is the union of the given * descriptors. The returned object may be identical to one of the * input descriptors if it is an ImmutableDescriptor that contains all of * the required fields. * * @throws IllegalArgumentException if two Descriptors contain the * same field name with different associated values. Primitive array * values are considered the same if they are of the same type with * the same elements. Object array values are considered the same if * {@link Arrays#deepEquals(Object[],Object[])} returns true. */ public static ImmutableDescriptor union(Descriptor... descriptors) { // Optimize the case where exactly one Descriptor is non-Empty // and it is immutable - we can just return it. int index = findNonEmpty(descriptors, 0); if (index < 0) return EMPTY_DESCRIPTOR; if (descriptors[index] instanceof ImmutableDescriptor && findNonEmpty(descriptors, index + 1) < 0) return (ImmutableDescriptor) descriptors[index]; Map
Returns the hash code value for this descriptor. The hash * code is computed as the sum of the hash codes for each field in * the descriptor. The hash code of a field with name {@code n} * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}. * Here {@code h} is the hash code of {@code v}, computed as * follows:
* *Returns a descriptor which is equal to this descriptor. * Changes to the returned descriptor will have no effect on this * descriptor, and vice versa.
* *This method returns the object on which it is called. * A subclass can override it * to return another object provided the contract is respected. * * @exception RuntimeOperationsException for illegal value for field Names * or field Values. * If the descriptor construction fails for any reason, this exception will * be thrown. */ @Override public Descriptor clone() { return this; } /** * This operation is unsupported since this class is immutable. If * this call would change a mutable descriptor with the same contents, * then a {@link RuntimeOperationsException} wrapping an * {@link UnsupportedOperationException} is thrown. Otherwise, * the behavior is the same as it would be for a mutable descriptor: * either an exception is thrown because of illegal parameters, or * there is no effect. */ public final void setFields(String[] fieldNames, Object[] fieldValues) throws RuntimeOperationsException { if (fieldNames == null || fieldValues == null) illegal("Null argument"); if (fieldNames.length != fieldValues.length) illegal("Different array sizes"); for (int i = 0; i < fieldNames.length; i++) checkIllegalFieldName(fieldNames[i]); for (int i = 0; i < fieldNames.length; i++) setField(fieldNames[i], fieldValues[i]); } /** * This operation is unsupported since this class is immutable. If * this call would change a mutable descriptor with the same contents, * then a {@link RuntimeOperationsException} wrapping an * {@link UnsupportedOperationException} is thrown. Otherwise, * the behavior is the same as it would be for a mutable descriptor: * either an exception is thrown because of illegal parameters, or * there is no effect. */ public final void setField(String fieldName, Object fieldValue) throws RuntimeOperationsException { checkIllegalFieldName(fieldName); int i = fieldIndex(fieldName); if (i < 0) unsupported(); Object value = values[i]; if ((value == null) ? (fieldValue != null) : !value.equals(fieldValue)) unsupported(); } /** * Removes a field from the descriptor. * * @param fieldName String name of the field to be removed. * If the field name is illegal or the field is not found, * no exception is thrown. * * @exception RuntimeOperationsException if a field of the given name * exists and the descriptor is immutable. The wrapped exception will * be an {@link UnsupportedOperationException}. */ public final void removeField(String fieldName) { if (fieldName != null && fieldIndex(fieldName) >= 0) unsupported(); } static Descriptor nonNullDescriptor(Descriptor d) { if (d == null) return EMPTY_DESCRIPTOR; else return d; } private static void checkIllegalFieldName(String name) { if (name == null || name.equals("")) illegal("Null or empty field name"); } private static void unsupported() { UnsupportedOperationException uoe = new UnsupportedOperationException("Descriptor is read-only"); throw new RuntimeOperationsException(uoe); } private static void illegal(String message) { IllegalArgumentException iae = new IllegalArgumentException(message); throw new RuntimeOperationsException(iae); } }