You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2112 lines
81 KiB
2112 lines
81 KiB
/*
|
|
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.management;
|
|
|
|
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
|
import com.sun.jmx.mbeanserver.Util;
|
|
import java.io.IOException;
|
|
import java.io.InvalidObjectException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.io.ObjectStreamField;
|
|
import java.security.AccessController;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Hashtable;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* <p>Represents the object name of an MBean, or a pattern that can
|
|
* match the names of several MBeans. Instances of this class are
|
|
* immutable.</p>
|
|
*
|
|
* <p>An instance of this class can be used to represent:</p>
|
|
* <ul>
|
|
* <li>An object name</li>
|
|
* <li>An object name pattern, within the context of a query</li>
|
|
* </ul>
|
|
*
|
|
* <p>An object name consists of two parts, the domain and the key
|
|
* properties.</p>
|
|
*
|
|
* <p>The <em>domain</em> is a string of characters not including
|
|
* the character colon (<code>:</code>). It is recommended that the domain
|
|
* should not contain the string "{@code //}", which is reserved for future use.
|
|
*
|
|
* <p>If the domain includes at least one occurrence of the wildcard
|
|
* characters asterisk (<code>*</code>) or question mark
|
|
* (<code>?</code>), then the object name is a pattern. The asterisk
|
|
* matches any sequence of zero or more characters, while the question
|
|
* mark matches any single character.</p>
|
|
*
|
|
* <p>If the domain is empty, it will be replaced in certain contexts
|
|
* by the <em>default domain</em> of the MBean server in which the
|
|
* ObjectName is used.</p>
|
|
*
|
|
* <p>The <em>key properties</em> are an unordered set of keys and
|
|
* associated values.</p>
|
|
*
|
|
* <p>Each <em>key</em> is a nonempty string of characters which may
|
|
* not contain any of the characters comma (<code>,</code>), equals
|
|
* (<code>=</code>), colon, asterisk, or question mark. The same key
|
|
* may not occur twice in a given ObjectName.</p>
|
|
*
|
|
* <p>Each <em>value</em> associated with a key is a string of
|
|
* characters that is either unquoted or quoted.</p>
|
|
*
|
|
* <p>An <em>unquoted value</em> is a possibly empty string of
|
|
* characters which may not contain any of the characters comma,
|
|
* equals, colon, or quote.</p>
|
|
*
|
|
* <p>If the <em>unquoted value</em> contains at least one occurrence
|
|
* of the wildcard characters asterisk or question mark, then the object
|
|
* name is a <em>property value pattern</em>. The asterisk matches any
|
|
* sequence of zero or more characters, while the question mark matches
|
|
* any single character.</p>
|
|
*
|
|
* <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
|
|
* followed by a possibly empty string of characters, followed by
|
|
* another quote. Within the string of characters, the backslash
|
|
* (<code>\</code>) has a special meaning. It must be followed by
|
|
* one of the following characters:</p>
|
|
*
|
|
* <ul>
|
|
* <li>Another backslash. The second backslash has no special
|
|
* meaning and the two characters represent a single backslash.</li>
|
|
*
|
|
* <li>The character 'n'. The two characters represent a newline
|
|
* ('\n' in Java).</li>
|
|
*
|
|
* <li>A quote. The two characters represent a quote, and that quote
|
|
* is not considered to terminate the quoted value. An ending closing
|
|
* quote must be present for the quoted value to be valid.</li>
|
|
*
|
|
* <li>A question mark (?) or asterisk (*). The two characters represent
|
|
* a question mark or asterisk respectively.</li>
|
|
* </ul>
|
|
*
|
|
* <p>A quote may not appear inside a quoted value except immediately
|
|
* after an odd number of consecutive backslashes.</p>
|
|
*
|
|
* <p>The quotes surrounding a quoted value, and any backslashes
|
|
* within that value, are considered to be part of the value.</p>
|
|
*
|
|
* <p>If the <em>quoted value</em> contains at least one occurrence of
|
|
* the characters asterisk or question mark and they are not preceded
|
|
* by a backslash, then they are considered as wildcard characters and
|
|
* the object name is a <em>property value pattern</em>. The asterisk
|
|
* matches any sequence of zero or more characters, while the question
|
|
* mark matches any single character.</p>
|
|
*
|
|
* <p>An ObjectName may be a <em>property list pattern</em>. In this
|
|
* case it may have zero or more keys and associated values. It matches
|
|
* a nonpattern ObjectName whose domain matches and that contains the
|
|
* same keys and associated values, as well as possibly other keys and
|
|
* values.</p>
|
|
*
|
|
* <p>An ObjectName is a <em>property value pattern</em> when at least
|
|
* one of its <em>quoted</em> or <em>unquoted</em> key property values
|
|
* contains the wildcard characters asterisk or question mark as described
|
|
* above. In this case it has one or more keys and associated values, with
|
|
* at least one of the values containing wildcard characters. It matches a
|
|
* nonpattern ObjectName whose domain matches and that contains the same
|
|
* keys whose values match; if the property value pattern is also a
|
|
* property list pattern then the nonpattern ObjectName can contain
|
|
* other keys and values.</p>
|
|
*
|
|
* <p>An ObjectName is a <em>property pattern</em> if it is either a
|
|
* <em>property list pattern</em> or a <em>property value pattern</em>
|
|
* or both.</p>
|
|
*
|
|
* <p>An ObjectName is a pattern if its domain contains a wildcard or
|
|
* if the ObjectName is a property pattern.</p>
|
|
*
|
|
* <p>If an ObjectName is not a pattern, it must contain at least one
|
|
* key with its associated value.</p>
|
|
*
|
|
* <p>Examples of ObjectName patterns are:</p>
|
|
*
|
|
* <ul>
|
|
* <li>{@code *:type=Foo,name=Bar} to match names in any domain whose
|
|
* exact set of keys is {@code type=Foo,name=Bar}.</li>
|
|
* <li>{@code d:type=Foo,name=Bar,*} to match names in the domain
|
|
* {@code d} that have the keys {@code type=Foo,name=Bar} plus
|
|
* zero or more other keys.</li>
|
|
* <li>{@code *:type=Foo,name=Bar,*} to match names in any domain
|
|
* that has the keys {@code type=Foo,name=Bar} plus zero or
|
|
* more other keys.</li>
|
|
* <li>{@code d:type=F?o,name=Bar} will match e.g.
|
|
* {@code d:type=Foo,name=Bar} and {@code d:type=Fro,name=Bar}.</li>
|
|
* <li>{@code d:type=F*o,name=Bar} will match e.g.
|
|
* {@code d:type=Fo,name=Bar} and {@code d:type=Frodo,name=Bar}.</li>
|
|
* <li>{@code d:type=Foo,name="B*"} will match e.g.
|
|
* {@code d:type=Foo,name="Bling"}. Wildcards are recognized even
|
|
* inside quotes, and like other special characters can be escaped
|
|
* with {@code \}.</li>
|
|
* </ul>
|
|
*
|
|
* <p>An ObjectName can be written as a String with the following
|
|
* elements in order:</p>
|
|
*
|
|
* <ul>
|
|
* <li>The domain.
|
|
* <li>A colon (<code>:</code>).
|
|
* <li>A key property list as defined below.
|
|
* </ul>
|
|
*
|
|
* <p>A key property list written as a String is a comma-separated
|
|
* list of elements. Each element is either an asterisk or a key
|
|
* property. A key property consists of a key, an equals
|
|
* (<code>=</code>), and the associated value.</p>
|
|
*
|
|
* <p>At most one element of a key property list may be an asterisk.
|
|
* If the key property list contains an asterisk element, the
|
|
* ObjectName is a property list pattern.</p>
|
|
*
|
|
* <p>Spaces have no special significance in a String representing an
|
|
* ObjectName. For example, the String:
|
|
* <pre>
|
|
* domain: key1 = value1 , key2 = value2
|
|
* </pre>
|
|
* represents an ObjectName with two keys. The name of each key
|
|
* contains six characters, of which the first and last are spaces.
|
|
* The value associated with the key <code>" key1 "</code>
|
|
* also begins and ends with a space.
|
|
*
|
|
* <p>In addition to the restrictions on characters spelt out above,
|
|
* no part of an ObjectName may contain a newline character
|
|
* (<code>'\n'</code>), whether the domain, a key, or a value, whether
|
|
* quoted or unquoted. The newline character can be represented in a
|
|
* quoted value with the sequence <code>\n</code>.
|
|
*
|
|
* <p>The rules on special characters and quoting apply regardless of
|
|
* which constructor is used to make an ObjectName.</p>
|
|
*
|
|
* <p>To avoid collisions between MBeans supplied by different
|
|
* vendors, a useful convention is to begin the domain name with the
|
|
* reverse DNS name of the organization that specifies the MBeans,
|
|
* followed by a period and a string whose interpretation is
|
|
* determined by that organization. For example, MBeans specified by
|
|
* <code>example.com</code> would have
|
|
* domains such as <code>com.example.MyDomain</code>. This is essentially
|
|
* the same convention as for Java-language package names.</p>
|
|
*
|
|
* <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
@SuppressWarnings("serial") // don't complain serialVersionUID not constant
|
|
public class ObjectName implements Comparable<ObjectName>, QueryExp {
|
|
|
|
/**
|
|
* A structure recording property structure and
|
|
* proposing minimal services
|
|
*/
|
|
private static class Property {
|
|
|
|
int _key_index;
|
|
int _key_length;
|
|
int _value_length;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
Property(int key_index, int key_length, int value_length) {
|
|
_key_index = key_index;
|
|
_key_length = key_length;
|
|
_value_length = value_length;
|
|
}
|
|
|
|
/**
|
|
* Assigns the key index of property
|
|
*/
|
|
void setKeyIndex(int key_index) {
|
|
_key_index = key_index;
|
|
}
|
|
|
|
/**
|
|
* Returns a key string for receiver key
|
|
*/
|
|
String getKeyString(String name) {
|
|
return name.substring(_key_index, _key_index + _key_length);
|
|
}
|
|
|
|
/**
|
|
* Returns a value string for receiver key
|
|
*/
|
|
String getValueString(String name) {
|
|
int in_begin = _key_index + _key_length + 1;
|
|
int out_end = in_begin + _value_length;
|
|
return name.substring(in_begin, out_end);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Marker class for value pattern property.
|
|
*/
|
|
private static class PatternProperty extends Property {
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
PatternProperty(int key_index, int key_length, int value_length) {
|
|
super(key_index, key_length, value_length);
|
|
}
|
|
}
|
|
|
|
// Inner classes <========================================
|
|
|
|
|
|
|
|
// Private fields ---------------------------------------->
|
|
|
|
|
|
// Serialization compatibility stuff -------------------->
|
|
|
|
// Two serial forms are supported in this class. The selected form depends
|
|
// on system property "jmx.serial.form":
|
|
// - "1.0" for JMX 1.0
|
|
// - any other value for JMX 1.1 and higher
|
|
//
|
|
// Serial version for old serial form
|
|
private static final long oldSerialVersionUID = -5467795090068647408L;
|
|
//
|
|
// Serial version for new serial form
|
|
private static final long newSerialVersionUID = 1081892073854801359L;
|
|
//
|
|
// Serializable fields in old serial form
|
|
private static final ObjectStreamField[] oldSerialPersistentFields =
|
|
{
|
|
new ObjectStreamField("domain", String.class),
|
|
new ObjectStreamField("propertyList", Hashtable.class),
|
|
new ObjectStreamField("propertyListString", String.class),
|
|
new ObjectStreamField("canonicalName", String.class),
|
|
new ObjectStreamField("pattern", Boolean.TYPE),
|
|
new ObjectStreamField("propertyPattern", Boolean.TYPE)
|
|
};
|
|
//
|
|
// Serializable fields in new serial form
|
|
private static final ObjectStreamField[] newSerialPersistentFields = { };
|
|
//
|
|
// Actual serial version and serial form
|
|
private static final long serialVersionUID;
|
|
private static final ObjectStreamField[] serialPersistentFields;
|
|
private static boolean compat = false;
|
|
static {
|
|
try {
|
|
GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
|
|
String form = AccessController.doPrivileged(act);
|
|
compat = (form != null && form.equals("1.0"));
|
|
} catch (Exception e) {
|
|
// OK: exception means no compat with 1.0, too bad
|
|
}
|
|
if (compat) {
|
|
serialPersistentFields = oldSerialPersistentFields;
|
|
serialVersionUID = oldSerialVersionUID;
|
|
} else {
|
|
serialPersistentFields = newSerialPersistentFields;
|
|
serialVersionUID = newSerialVersionUID;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Serialization compatibility stuff <==============================
|
|
|
|
// Class private fields ----------------------------------->
|
|
|
|
/**
|
|
* a shared empty array for empty property lists
|
|
*/
|
|
static final private Property[] _Empty_property_array = new Property[0];
|
|
|
|
|
|
// Class private fields <==============================
|
|
|
|
// Instance private fields ----------------------------------->
|
|
|
|
/**
|
|
* a String containing the canonical name
|
|
*/
|
|
private transient String _canonicalName;
|
|
|
|
|
|
/**
|
|
* An array of properties in the same seq order as time creation
|
|
*/
|
|
private transient Property[] _kp_array;
|
|
|
|
/**
|
|
* An array of properties in the same seq order as canonical order
|
|
*/
|
|
private transient Property[] _ca_array;
|
|
|
|
|
|
/**
|
|
* The length of the domain part of built objectname
|
|
*/
|
|
private transient int _domain_length = 0;
|
|
|
|
|
|
/**
|
|
* The propertyList of built object name. Initialized lazily.
|
|
* Table that contains all the pairs (key,value) for this ObjectName.
|
|
*/
|
|
private transient Map<String,String> _propertyList;
|
|
|
|
/**
|
|
* boolean that declares if this ObjectName domain part is a pattern
|
|
*/
|
|
private transient boolean _domain_pattern = false;
|
|
|
|
/**
|
|
* boolean that declares if this ObjectName contains a pattern on the
|
|
* key property list
|
|
*/
|
|
private transient boolean _property_list_pattern = false;
|
|
|
|
/**
|
|
* boolean that declares if this ObjectName contains a pattern on the
|
|
* value of at least one key property
|
|
*/
|
|
private transient boolean _property_value_pattern = false;
|
|
|
|
// Instance private fields <=======================================
|
|
|
|
// Private fields <========================================
|
|
|
|
|
|
// Private methods ---------------------------------------->
|
|
|
|
// Category : Instance construction ------------------------->
|
|
|
|
/**
|
|
* Initializes this {@link ObjectName} from the given string
|
|
* representation.
|
|
*
|
|
* @param name A string representation of the {@link ObjectName}
|
|
*
|
|
* @exception MalformedObjectNameException The string passed as a
|
|
* parameter does not have the right format.
|
|
* @exception NullPointerException The <code>name</code> parameter
|
|
* is null.
|
|
*/
|
|
private void construct(String name)
|
|
throws MalformedObjectNameException {
|
|
|
|
// The name cannot be null
|
|
if (name == null)
|
|
throw new NullPointerException("name cannot be null");
|
|
|
|
// Test if the name is empty
|
|
if (name.length() == 0) {
|
|
// this is equivalent to the whole word query object name.
|
|
_canonicalName = "*:*";
|
|
_kp_array = _Empty_property_array;
|
|
_ca_array = _Empty_property_array;
|
|
_domain_length = 1;
|
|
_propertyList = null;
|
|
_domain_pattern = true;
|
|
_property_list_pattern = true;
|
|
_property_value_pattern = false;
|
|
return;
|
|
}
|
|
|
|
// initialize parsing of the string
|
|
final char[] name_chars = name.toCharArray();
|
|
final int len = name_chars.length;
|
|
final char[] canonical_chars = new char[len]; // canonical form will
|
|
// be same length at most
|
|
int cname_index = 0;
|
|
int index = 0;
|
|
char c, c1;
|
|
|
|
// parses domain part
|
|
domain_parsing:
|
|
while (index < len) {
|
|
switch (name_chars[index]) {
|
|
case ':' :
|
|
_domain_length = index++;
|
|
break domain_parsing;
|
|
case '=' :
|
|
// ":" omission check.
|
|
//
|
|
// Although "=" is a valid character in the domain part
|
|
// it is true that it is rarely used in the real world.
|
|
// So check straight away if the ":" has been omitted
|
|
// from the ObjectName. This allows us to provide a more
|
|
// accurate exception message.
|
|
int i = ++index;
|
|
while ((i < len) && (name_chars[i++] != ':'))
|
|
if (i == len)
|
|
throw new MalformedObjectNameException(
|
|
"Domain part must be specified");
|
|
break;
|
|
case '\n' :
|
|
throw new MalformedObjectNameException(
|
|
"Invalid character '\\n' in domain name");
|
|
case '*' :
|
|
case '?' :
|
|
_domain_pattern = true;
|
|
index++;
|
|
break;
|
|
default :
|
|
index++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check for non-empty properties
|
|
if (index == len)
|
|
throw new MalformedObjectNameException(
|
|
"Key properties cannot be empty");
|
|
|
|
// we have got the domain part, begins building of _canonicalName
|
|
System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length);
|
|
canonical_chars[_domain_length] = ':';
|
|
cname_index = _domain_length + 1;
|
|
|
|
// parses property list
|
|
Property prop;
|
|
Map<String,Property> keys_map = new HashMap<String,Property>();
|
|
String[] keys;
|
|
String key_name;
|
|
boolean quoted_value;
|
|
int property_index = 0;
|
|
int in_index;
|
|
int key_index, key_length, value_index, value_length;
|
|
|
|
keys = new String[10];
|
|
_kp_array = new Property[10];
|
|
_property_list_pattern = false;
|
|
_property_value_pattern = false;
|
|
|
|
while (index < len) {
|
|
c = name_chars[index];
|
|
|
|
// case of pattern properties
|
|
if (c == '*') {
|
|
if (_property_list_pattern)
|
|
throw new MalformedObjectNameException(
|
|
"Cannot have several '*' characters in pattern " +
|
|
"property list");
|
|
else {
|
|
_property_list_pattern = true;
|
|
if ((++index < len ) && (name_chars[index] != ','))
|
|
throw new MalformedObjectNameException(
|
|
"Invalid character found after '*': end of " +
|
|
"name or ',' expected");
|
|
else if (index == len) {
|
|
if (property_index == 0) {
|
|
// empty properties case
|
|
_kp_array = _Empty_property_array;
|
|
_ca_array = _Empty_property_array;
|
|
_propertyList = Collections.emptyMap();
|
|
}
|
|
break;
|
|
} else {
|
|
// correct pattern spec in props, continue
|
|
index++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// standard property case, key part
|
|
in_index = index;
|
|
key_index = in_index;
|
|
if (name_chars[in_index] == '=')
|
|
throw new MalformedObjectNameException("Invalid key (empty)");
|
|
while ((in_index < len) && ((c1 = name_chars[in_index++]) != '='))
|
|
switch (c1) {
|
|
// '=' considered to introduce value part
|
|
case '*' :
|
|
case '?' :
|
|
case ',' :
|
|
case ':' :
|
|
case '\n' :
|
|
final String ichar = ((c1=='\n')?"\\n":""+c1);
|
|
throw new MalformedObjectNameException(
|
|
"Invalid character '" + ichar +
|
|
"' in key part of property");
|
|
}
|
|
if (name_chars[in_index - 1] != '=')
|
|
throw new MalformedObjectNameException(
|
|
"Unterminated key property part");
|
|
value_index = in_index; // in_index pointing after '=' char
|
|
key_length = value_index - key_index - 1; // found end of key
|
|
|
|
// standard property case, value part
|
|
boolean value_pattern = false;
|
|
if (in_index < len && name_chars[in_index] == '\"') {
|
|
quoted_value = true;
|
|
// the case of quoted value part
|
|
quoted_value_parsing:
|
|
while ((++in_index < len) &&
|
|
((c1 = name_chars[in_index]) != '\"')) {
|
|
// the case of an escaped character
|
|
if (c1 == '\\') {
|
|
if (++in_index == len)
|
|
throw new MalformedObjectNameException(
|
|
"Unterminated quoted value");
|
|
switch (c1 = name_chars[in_index]) {
|
|
case '\\' :
|
|
case '\"' :
|
|
case '?' :
|
|
case '*' :
|
|
case 'n' :
|
|
break; // valid character
|
|
default :
|
|
throw new MalformedObjectNameException(
|
|
"Invalid escape sequence '\\" +
|
|
c1 + "' in quoted value");
|
|
}
|
|
} else if (c1 == '\n') {
|
|
throw new MalformedObjectNameException(
|
|
"Newline in quoted value");
|
|
} else {
|
|
switch (c1) {
|
|
case '?' :
|
|
case '*' :
|
|
value_pattern = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (in_index == len)
|
|
throw new MalformedObjectNameException(
|
|
"Unterminated quoted value");
|
|
else value_length = ++in_index - value_index;
|
|
} else {
|
|
// the case of standard value part
|
|
quoted_value = false;
|
|
while ((in_index < len) && ((c1 = name_chars[in_index]) != ','))
|
|
switch (c1) {
|
|
// ',' considered to be the value separator
|
|
case '*' :
|
|
case '?' :
|
|
value_pattern = true;
|
|
in_index++;
|
|
break;
|
|
case '=' :
|
|
case ':' :
|
|
case '"' :
|
|
case '\n' :
|
|
final String ichar = ((c1=='\n')?"\\n":""+c1);
|
|
throw new MalformedObjectNameException(
|
|
"Invalid character '" + ichar +
|
|
"' in value part of property");
|
|
default :
|
|
in_index++;
|
|
break;
|
|
}
|
|
value_length = in_index - value_index;
|
|
}
|
|
|
|
// Parsed property, checks the end of name
|
|
if (in_index == len - 1) {
|
|
if (quoted_value)
|
|
throw new MalformedObjectNameException(
|
|
"Invalid ending character `" +
|
|
name_chars[in_index] + "'");
|
|
else throw new MalformedObjectNameException(
|
|
"Invalid ending comma");
|
|
} else in_index++;
|
|
|
|
// we got the key and value part, prepare a property for this
|
|
if (!value_pattern) {
|
|
prop = new Property(key_index, key_length, value_length);
|
|
} else {
|
|
_property_value_pattern = true;
|
|
prop = new PatternProperty(key_index, key_length, value_length);
|
|
}
|
|
key_name = name.substring(key_index, key_index + key_length);
|
|
|
|
if (property_index == keys.length) {
|
|
String[] tmp_string_array = new String[property_index + 10];
|
|
System.arraycopy(keys, 0, tmp_string_array, 0, property_index);
|
|
keys = tmp_string_array;
|
|
}
|
|
keys[property_index] = key_name;
|
|
|
|
addProperty(prop, property_index, keys_map, key_name);
|
|
property_index++;
|
|
index = in_index;
|
|
}
|
|
|
|
// computes and set canonical name
|
|
setCanonicalName(name_chars, canonical_chars, keys,
|
|
keys_map, cname_index, property_index);
|
|
}
|
|
|
|
/**
|
|
* Construct an ObjectName from a domain and a Hashtable.
|
|
*
|
|
* @param domain Domain of the ObjectName.
|
|
* @param props Map containing couples <i>key</i> {@literal ->} <i>value</i>.
|
|
*
|
|
* @exception MalformedObjectNameException The <code>domain</code>
|
|
* contains an illegal character, or one of the keys or values in
|
|
* <code>table</code> contains an illegal character, or one of the
|
|
* values in <code>table</code> does not follow the rules for quoting.
|
|
* @exception NullPointerException One of the parameters is null.
|
|
*/
|
|
private void construct(String domain, Map<String,String> props)
|
|
throws MalformedObjectNameException {
|
|
|
|
// The domain cannot be null
|
|
if (domain == null)
|
|
throw new NullPointerException("domain cannot be null");
|
|
|
|
// The key property list cannot be null
|
|
if (props == null)
|
|
throw new NullPointerException("key property list cannot be null");
|
|
|
|
// The key property list cannot be empty
|
|
if (props.isEmpty())
|
|
throw new MalformedObjectNameException(
|
|
"key property list cannot be empty");
|
|
|
|
// checks domain validity
|
|
if (!isDomain(domain))
|
|
throw new MalformedObjectNameException("Invalid domain: " + domain);
|
|
|
|
// init canonicalname
|
|
final StringBuilder sb = new StringBuilder();
|
|
sb.append(domain).append(':');
|
|
_domain_length = domain.length();
|
|
|
|
// allocates the property array
|
|
int nb_props = props.size();
|
|
_kp_array = new Property[nb_props];
|
|
|
|
String[] keys = new String[nb_props];
|
|
final Map<String,Property> keys_map = new HashMap<String,Property>();
|
|
Property prop;
|
|
int key_index;
|
|
int i = 0;
|
|
for (Map.Entry<String,String> entry : props.entrySet()) {
|
|
if (sb.length() > 0)
|
|
sb.append(",");
|
|
String key = entry.getKey();
|
|
String value;
|
|
try {
|
|
value = entry.getValue();
|
|
} catch (ClassCastException e) {
|
|
throw new MalformedObjectNameException(e.getMessage());
|
|
}
|
|
key_index = sb.length();
|
|
checkKey(key);
|
|
sb.append(key);
|
|
keys[i] = key;
|
|
sb.append("=");
|
|
boolean value_pattern = checkValue(value);
|
|
sb.append(value);
|
|
if (!value_pattern) {
|
|
prop = new Property(key_index,
|
|
key.length(),
|
|
value.length());
|
|
} else {
|
|
_property_value_pattern = true;
|
|
prop = new PatternProperty(key_index,
|
|
key.length(),
|
|
value.length());
|
|
}
|
|
addProperty(prop, i, keys_map, key);
|
|
i++;
|
|
}
|
|
|
|
// initialize canonical name and data structure
|
|
int len = sb.length();
|
|
char[] initial_chars = new char[len];
|
|
sb.getChars(0, len, initial_chars, 0);
|
|
char[] canonical_chars = new char[len];
|
|
System.arraycopy(initial_chars, 0, canonical_chars, 0,
|
|
_domain_length + 1);
|
|
setCanonicalName(initial_chars, canonical_chars, keys, keys_map,
|
|
_domain_length + 1, _kp_array.length);
|
|
}
|
|
// Category : Instance construction <==============================
|
|
|
|
// Category : Internal utilities ------------------------------>
|
|
|
|
/**
|
|
* Add passed property to the list at the given index
|
|
* for the passed key name
|
|
*/
|
|
private void addProperty(Property prop, int index,
|
|
Map<String,Property> keys_map, String key_name)
|
|
throws MalformedObjectNameException {
|
|
|
|
if (keys_map.containsKey(key_name)) throw new
|
|
MalformedObjectNameException("key `" +
|
|
key_name +"' already defined");
|
|
|
|
// if no more space for property arrays, have to increase it
|
|
if (index == _kp_array.length) {
|
|
Property[] tmp_prop_array = new Property[index + 10];
|
|
System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
|
|
_kp_array = tmp_prop_array;
|
|
}
|
|
_kp_array[index] = prop;
|
|
keys_map.put(key_name, prop);
|
|
}
|
|
|
|
/**
|
|
* Sets the canonical name of receiver from input 'specified_chars'
|
|
* array, by filling 'canonical_chars' array with found 'nb-props'
|
|
* properties starting at position 'prop_index'.
|
|
*/
|
|
private void setCanonicalName(char[] specified_chars,
|
|
char[] canonical_chars,
|
|
String[] keys, Map<String,Property> keys_map,
|
|
int prop_index, int nb_props) {
|
|
|
|
// Sort the list of found properties
|
|
if (_kp_array != _Empty_property_array) {
|
|
String[] tmp_keys = new String[nb_props];
|
|
Property[] tmp_props = new Property[nb_props];
|
|
|
|
System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
|
|
Arrays.sort(tmp_keys);
|
|
keys = tmp_keys;
|
|
System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props);
|
|
_kp_array = tmp_props;
|
|
_ca_array = new Property[nb_props];
|
|
|
|
// now assigns _ca_array to the sorted list of keys
|
|
// (there cannot be two identical keys in an objectname.
|
|
for (int i = 0; i < nb_props; i++)
|
|
_ca_array[i] = keys_map.get(keys[i]);
|
|
|
|
// now we build the canonical name and set begin indexes of
|
|
// properties to reflect canonical form
|
|
int last_index = nb_props - 1;
|
|
int prop_len;
|
|
Property prop;
|
|
for (int i = 0; i <= last_index; i++) {
|
|
prop = _ca_array[i];
|
|
// length of prop including '=' char
|
|
prop_len = prop._key_length + prop._value_length + 1;
|
|
System.arraycopy(specified_chars, prop._key_index,
|
|
canonical_chars, prop_index, prop_len);
|
|
prop.setKeyIndex(prop_index);
|
|
prop_index += prop_len;
|
|
if (i != last_index) {
|
|
canonical_chars[prop_index] = ',';
|
|
prop_index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// terminate canonicalname with '*' in case of pattern
|
|
if (_property_list_pattern) {
|
|
if (_kp_array != _Empty_property_array)
|
|
canonical_chars[prop_index++] = ',';
|
|
canonical_chars[prop_index++] = '*';
|
|
}
|
|
|
|
// we now build the canonicalname string
|
|
_canonicalName = (new String(canonical_chars, 0, prop_index)).intern();
|
|
}
|
|
|
|
/**
|
|
* Parse a key.
|
|
* <pre>final int endKey=parseKey(s,startKey);</pre>
|
|
* <p>key starts at startKey (included), and ends at endKey (excluded).
|
|
* If (startKey == endKey), then the key is empty.
|
|
*
|
|
* @param s The char array of the original string.
|
|
* @param startKey index at which to begin parsing.
|
|
* @return The index following the last character of the key.
|
|
**/
|
|
private static int parseKey(final char[] s, final int startKey)
|
|
throws MalformedObjectNameException {
|
|
int next = startKey;
|
|
int endKey = startKey;
|
|
final int len = s.length;
|
|
while (next < len) {
|
|
final char k = s[next++];
|
|
switch (k) {
|
|
case '*':
|
|
case '?':
|
|
case ',':
|
|
case ':':
|
|
case '\n':
|
|
final String ichar = ((k=='\n')?"\\n":""+k);
|
|
throw new
|
|
MalformedObjectNameException("Invalid character in key: `"
|
|
+ ichar + "'");
|
|
case '=':
|
|
// we got the key.
|
|
endKey = next-1;
|
|
break;
|
|
default:
|
|
if (next < len) continue;
|
|
else endKey=next;
|
|
}
|
|
break;
|
|
}
|
|
return endKey;
|
|
}
|
|
|
|
/**
|
|
* Parse a value.
|
|
* <pre>final int endVal=parseValue(s,startVal);</pre>
|
|
* <p>value starts at startVal (included), and ends at endVal (excluded).
|
|
* If (startVal == endVal), then the key is empty.
|
|
*
|
|
* @param s The char array of the original string.
|
|
* @param startValue index at which to begin parsing.
|
|
* @return The first element of the int array indicates the index
|
|
* following the last character of the value. The second
|
|
* element of the int array indicates that the value is
|
|
* a pattern when its value equals 1.
|
|
**/
|
|
private static int[] parseValue(final char[] s, final int startValue)
|
|
throws MalformedObjectNameException {
|
|
|
|
boolean value_pattern = false;
|
|
|
|
int next = startValue;
|
|
int endValue = startValue;
|
|
|
|
final int len = s.length;
|
|
final char q=s[startValue];
|
|
|
|
if (q == '"') {
|
|
// quoted value
|
|
if (++next == len) throw new
|
|
MalformedObjectNameException("Invalid quote");
|
|
while (next < len) {
|
|
char last = s[next];
|
|
if (last == '\\') {
|
|
if (++next == len) throw new
|
|
MalformedObjectNameException(
|
|
"Invalid unterminated quoted character sequence");
|
|
last = s[next];
|
|
switch (last) {
|
|
case '\\' :
|
|
case '?' :
|
|
case '*' :
|
|
case 'n' :
|
|
break;
|
|
case '\"' :
|
|
// We have an escaped quote. If this escaped
|
|
// quote is the last character, it does not
|
|
// qualify as a valid termination quote.
|
|
//
|
|
if (next+1 == len) throw new
|
|
MalformedObjectNameException(
|
|
"Missing termination quote");
|
|
break;
|
|
default:
|
|
throw new
|
|
MalformedObjectNameException(
|
|
"Invalid quoted character sequence '\\" +
|
|
last + "'");
|
|
}
|
|
} else if (last == '\n') {
|
|
throw new MalformedObjectNameException(
|
|
"Newline in quoted value");
|
|
} else if (last == '\"') {
|
|
next++;
|
|
break;
|
|
} else {
|
|
switch (last) {
|
|
case '?' :
|
|
case '*' :
|
|
value_pattern = true;
|
|
break;
|
|
}
|
|
}
|
|
next++;
|
|
|
|
// Check that last character is a termination quote.
|
|
// We have already handled the case were the last
|
|
// character is an escaped quote earlier.
|
|
//
|
|
if ((next >= len) && (last != '\"')) throw new
|
|
MalformedObjectNameException("Missing termination quote");
|
|
}
|
|
endValue = next;
|
|
if (next < len) {
|
|
if (s[next++] != ',') throw new
|
|
MalformedObjectNameException("Invalid quote");
|
|
}
|
|
} else {
|
|
// Non quoted value.
|
|
while (next < len) {
|
|
final char v=s[next++];
|
|
switch(v) {
|
|
case '*':
|
|
case '?':
|
|
value_pattern = true;
|
|
if (next < len) continue;
|
|
else endValue=next;
|
|
break;
|
|
case '=':
|
|
case ':':
|
|
case '\n' :
|
|
final String ichar = ((v=='\n')?"\\n":""+v);
|
|
throw new
|
|
MalformedObjectNameException("Invalid character `" +
|
|
ichar + "' in value");
|
|
case ',':
|
|
endValue = next-1;
|
|
break;
|
|
default:
|
|
if (next < len) continue;
|
|
else endValue=next;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return new int[] { endValue, value_pattern ? 1 : 0 };
|
|
}
|
|
|
|
/**
|
|
* Check if the supplied value is a valid value.
|
|
*
|
|
* @return true if the value is a pattern, otherwise false.
|
|
*/
|
|
private static boolean checkValue(String val)
|
|
throws MalformedObjectNameException {
|
|
|
|
if (val == null) throw new
|
|
NullPointerException("Invalid value (null)");
|
|
|
|
final int len = val.length();
|
|
if (len == 0)
|
|
return false;
|
|
|
|
final char[] s = val.toCharArray();
|
|
final int[] result = parseValue(s,0);
|
|
final int endValue = result[0];
|
|
final boolean value_pattern = result[1] == 1;
|
|
if (endValue < len) throw new
|
|
MalformedObjectNameException("Invalid character in value: `" +
|
|
s[endValue] + "'");
|
|
return value_pattern;
|
|
}
|
|
|
|
/**
|
|
* Check if the supplied key is a valid key.
|
|
*/
|
|
private static void checkKey(String key)
|
|
throws MalformedObjectNameException {
|
|
|
|
if (key == null) throw new
|
|
NullPointerException("Invalid key (null)");
|
|
|
|
final int len = key.length();
|
|
if (len == 0) throw new
|
|
MalformedObjectNameException("Invalid key (empty)");
|
|
final char[] k=key.toCharArray();
|
|
final int endKey = parseKey(k,0);
|
|
if (endKey < len) throw new
|
|
MalformedObjectNameException("Invalid character in value: `" +
|
|
k[endKey] + "'");
|
|
}
|
|
|
|
|
|
// Category : Internal utilities <==============================
|
|
|
|
// Category : Internal accessors ------------------------------>
|
|
|
|
/**
|
|
* Check if domain is a valid domain. Set _domain_pattern if appropriate.
|
|
*/
|
|
private boolean isDomain(String domain) {
|
|
if (domain == null) return true;
|
|
final int len = domain.length();
|
|
int next = 0;
|
|
while (next < len) {
|
|
final char c = domain.charAt(next++);
|
|
switch (c) {
|
|
case ':' :
|
|
case '\n' :
|
|
return false;
|
|
case '*' :
|
|
case '?' :
|
|
_domain_pattern = true;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Category : Internal accessors <==============================
|
|
|
|
// Category : Serialization ----------------------------------->
|
|
|
|
/**
|
|
* Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
|
|
* @serialData <ul>
|
|
* <li>In the current serial form (value of property
|
|
* <code>jmx.serial.form</code> differs from
|
|
* <code>1.0</code>): the string
|
|
* "<domain>:<properties><wild>",
|
|
* where: <ul>
|
|
* <li><domain> represents the domain part
|
|
* of the {@link ObjectName}</li>
|
|
* <li><properties> represents the list of
|
|
* properties, as returned by
|
|
* {@link #getKeyPropertyListString}
|
|
* <li><wild> is empty if not
|
|
* <code>isPropertyPattern</code>, or
|
|
* is the character "<code>*</code>" if
|
|
* <code>isPropertyPattern</code>
|
|
* and <properties> is empty, or
|
|
* is "<code>,*</code>" if
|
|
* <code>isPropertyPattern</code> and
|
|
* <properties> is not empty.
|
|
* </li>
|
|
* </ul>
|
|
* The intent is that this string could be supplied
|
|
* to the {@link #ObjectName(String)} constructor to
|
|
* produce an equivalent {@link ObjectName}.
|
|
* </li>
|
|
* <li>In the old serial form (value of property
|
|
* <code>jmx.serial.form</code> is
|
|
* <code>1.0</code>): <domain> <propertyList>
|
|
* <propertyListString> <canonicalName>
|
|
* <pattern> <propertyPattern>,
|
|
* where: <ul>
|
|
* <li><domain> represents the domain part
|
|
* of the {@link ObjectName}</li>
|
|
* <li><propertyList> is the
|
|
* {@link Hashtable} that contains all the
|
|
* pairs (key,value) for this
|
|
* {@link ObjectName}</li>
|
|
* <li><propertyListString> is the
|
|
* {@link String} representation of the
|
|
* list of properties in any order (not
|
|
* mandatorily a canonical representation)
|
|
* </li>
|
|
* <li><canonicalName> is the
|
|
* {@link String} containing this
|
|
* {@link ObjectName}'s canonical name</li>
|
|
* <li><pattern> is a boolean which is
|
|
* <code>true</code> if this
|
|
* {@link ObjectName} contains a pattern</li>
|
|
* <li><propertyPattern> is a boolean which
|
|
* is <code>true</code> if this
|
|
* {@link ObjectName} contains a pattern in
|
|
* the list of properties</li>
|
|
* </ul>
|
|
* </li>
|
|
* </ul>
|
|
*/
|
|
private void readObject(ObjectInputStream in)
|
|
throws IOException, ClassNotFoundException {
|
|
|
|
String cn;
|
|
if (compat) {
|
|
// Read an object serialized in the old serial form
|
|
//
|
|
//in.defaultReadObject();
|
|
final ObjectInputStream.GetField fields = in.readFields();
|
|
String propListString =
|
|
(String)fields.get("propertyListString", "");
|
|
|
|
// 6616825: take care of property patterns
|
|
final boolean propPattern =
|
|
fields.get("propertyPattern" , false);
|
|
if (propPattern) {
|
|
propListString =
|
|
(propListString.length()==0?"*":(propListString+",*"));
|
|
}
|
|
|
|
cn = (String)fields.get("domain", "default")+
|
|
":"+ propListString;
|
|
} else {
|
|
// Read an object serialized in the new serial form
|
|
//
|
|
in.defaultReadObject();
|
|
cn = (String)in.readObject();
|
|
}
|
|
|
|
try {
|
|
construct(cn);
|
|
} catch (NullPointerException e) {
|
|
throw new InvalidObjectException(e.toString());
|
|
} catch (MalformedObjectNameException e) {
|
|
throw new InvalidObjectException(e.toString());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
|
|
* @serialData <ul>
|
|
* <li>In the current serial form (value of property
|
|
* <code>jmx.serial.form</code> differs from
|
|
* <code>1.0</code>): the string
|
|
* "<domain>:<properties><wild>",
|
|
* where: <ul>
|
|
* <li><domain> represents the domain part
|
|
* of the {@link ObjectName}</li>
|
|
* <li><properties> represents the list of
|
|
* properties, as returned by
|
|
* {@link #getKeyPropertyListString}
|
|
* <li><wild> is empty if not
|
|
* <code>isPropertyPattern</code>, or
|
|
* is the character "<code>*</code>" if
|
|
* this <code>isPropertyPattern</code>
|
|
* and <properties> is empty, or
|
|
* is "<code>,*</code>" if
|
|
* <code>isPropertyPattern</code> and
|
|
* <properties> is not empty.
|
|
* </li>
|
|
* </ul>
|
|
* The intent is that this string could be supplied
|
|
* to the {@link #ObjectName(String)} constructor to
|
|
* produce an equivalent {@link ObjectName}.
|
|
* </li>
|
|
* <li>In the old serial form (value of property
|
|
* <code>jmx.serial.form</code> is
|
|
* <code>1.0</code>): <domain> <propertyList>
|
|
* <propertyListString> <canonicalName>
|
|
* <pattern> <propertyPattern>,
|
|
* where: <ul>
|
|
* <li><domain> represents the domain part
|
|
* of the {@link ObjectName}</li>
|
|
* <li><propertyList> is the
|
|
* {@link Hashtable} that contains all the
|
|
* pairs (key,value) for this
|
|
* {@link ObjectName}</li>
|
|
* <li><propertyListString> is the
|
|
* {@link String} representation of the
|
|
* list of properties in any order (not
|
|
* mandatorily a canonical representation)
|
|
* </li>
|
|
* <li><canonicalName> is the
|
|
* {@link String} containing this
|
|
* {@link ObjectName}'s canonical name</li>
|
|
* <li><pattern> is a boolean which is
|
|
* <code>true</code> if this
|
|
* {@link ObjectName} contains a pattern</li>
|
|
* <li><propertyPattern> is a boolean which
|
|
* is <code>true</code> if this
|
|
* {@link ObjectName} contains a pattern in
|
|
* the list of properties</li>
|
|
* </ul>
|
|
* </li>
|
|
* </ul>
|
|
*/
|
|
private void writeObject(ObjectOutputStream out)
|
|
throws IOException {
|
|
|
|
if (compat)
|
|
{
|
|
// Serializes this instance in the old serial form
|
|
// Read CR 6441274 before making any changes to this code
|
|
ObjectOutputStream.PutField fields = out.putFields();
|
|
fields.put("domain", _canonicalName.substring(0, _domain_length));
|
|
fields.put("propertyList", getKeyPropertyList());
|
|
fields.put("propertyListString", getKeyPropertyListString());
|
|
fields.put("canonicalName", _canonicalName);
|
|
fields.put("pattern", (_domain_pattern || _property_list_pattern));
|
|
fields.put("propertyPattern", _property_list_pattern);
|
|
out.writeFields();
|
|
}
|
|
else
|
|
{
|
|
// Serializes this instance in the new serial form
|
|
//
|
|
out.defaultWriteObject();
|
|
out.writeObject(getSerializedNameString());
|
|
}
|
|
}
|
|
|
|
// Category : Serialization <===================================
|
|
|
|
// Private methods <========================================
|
|
|
|
// Public methods ---------------------------------------->
|
|
|
|
// Category : ObjectName Construction ------------------------------>
|
|
|
|
/**
|
|
* <p>Return an instance of ObjectName that can be used anywhere
|
|
* an object obtained with {@link #ObjectName(String) new
|
|
* ObjectName(name)} can be used. The returned object may be of
|
|
* a subclass of ObjectName. Calling this method twice with the
|
|
* same parameters may return the same object or two equal but
|
|
* not identical objects.</p>
|
|
*
|
|
* @param name A string representation of the object name.
|
|
*
|
|
* @return an ObjectName corresponding to the given String.
|
|
*
|
|
* @exception MalformedObjectNameException The string passed as a
|
|
* parameter does not have the right format.
|
|
* @exception NullPointerException The <code>name</code> parameter
|
|
* is null.
|
|
*
|
|
*/
|
|
public static ObjectName getInstance(String name)
|
|
throws MalformedObjectNameException, NullPointerException {
|
|
return new ObjectName(name);
|
|
}
|
|
|
|
/**
|
|
* <p>Return an instance of ObjectName that can be used anywhere
|
|
* an object obtained with {@link #ObjectName(String, String,
|
|
* String) new ObjectName(domain, key, value)} can be used. The
|
|
* returned object may be of a subclass of ObjectName. Calling
|
|
* this method twice with the same parameters may return the same
|
|
* object or two equal but not identical objects.</p>
|
|
*
|
|
* @param domain The domain part of the object name.
|
|
* @param key The attribute in the key property of the object name.
|
|
* @param value The value in the key property of the object name.
|
|
*
|
|
* @return an ObjectName corresponding to the given domain,
|
|
* key, and value.
|
|
*
|
|
* @exception MalformedObjectNameException The
|
|
* <code>domain</code>, <code>key</code>, or <code>value</code>
|
|
* contains an illegal character, or <code>value</code> does not
|
|
* follow the rules for quoting.
|
|
* @exception NullPointerException One of the parameters is null.
|
|
*
|
|
*/
|
|
public static ObjectName getInstance(String domain, String key,
|
|
String value)
|
|
throws MalformedObjectNameException {
|
|
return new ObjectName(domain, key, value);
|
|
}
|
|
|
|
/**
|
|
* <p>Return an instance of ObjectName that can be used anywhere
|
|
* an object obtained with {@link #ObjectName(String, Hashtable)
|
|
* new ObjectName(domain, table)} can be used. The returned
|
|
* object may be of a subclass of ObjectName. Calling this method
|
|
* twice with the same parameters may return the same object or
|
|
* two equal but not identical objects.</p>
|
|
*
|
|
* @param domain The domain part of the object name.
|
|
* @param table A hash table containing one or more key
|
|
* properties. The key of each entry in the table is the key of a
|
|
* key property in the object name. The associated value in the
|
|
* table is the associated value in the object name.
|
|
*
|
|
* @return an ObjectName corresponding to the given domain and
|
|
* key mappings.
|
|
*
|
|
* @exception MalformedObjectNameException The <code>domain</code>
|
|
* contains an illegal character, or one of the keys or values in
|
|
* <code>table</code> contains an illegal character, or one of the
|
|
* values in <code>table</code> does not follow the rules for
|
|
* quoting.
|
|
* @exception NullPointerException One of the parameters is null.
|
|
*
|
|
*/
|
|
public static ObjectName getInstance(String domain,
|
|
Hashtable<String,String> table)
|
|
throws MalformedObjectNameException {
|
|
return new ObjectName(domain, table);
|
|
}
|
|
|
|
/**
|
|
* <p>Return an instance of ObjectName that can be used anywhere
|
|
* the given object can be used. The returned object may be of a
|
|
* subclass of ObjectName. If <code>name</code> is of a subclass
|
|
* of ObjectName, it is not guaranteed that the returned object
|
|
* will be of the same class.</p>
|
|
*
|
|
* <p>The returned value may or may not be identical to
|
|
* <code>name</code>. Calling this method twice with the same
|
|
* parameters may return the same object or two equal but not
|
|
* identical objects.</p>
|
|
*
|
|
* <p>Since ObjectName is immutable, it is not usually useful to
|
|
* make a copy of an ObjectName. The principal use of this method
|
|
* is to guard against a malicious caller who might pass an
|
|
* instance of a subclass with surprising behavior to sensitive
|
|
* code. Such code can call this method to obtain an ObjectName
|
|
* that is known not to have surprising behavior.</p>
|
|
*
|
|
* @param name an instance of the ObjectName class or of a subclass
|
|
*
|
|
* @return an instance of ObjectName or a subclass that is known to
|
|
* have the same semantics. If <code>name</code> respects the
|
|
* semantics of ObjectName, then the returned object is equal
|
|
* (though not necessarily identical) to <code>name</code>.
|
|
*
|
|
* @exception NullPointerException The <code>name</code> is null.
|
|
*
|
|
*/
|
|
public static ObjectName getInstance(ObjectName name) {
|
|
if (name.getClass().equals(ObjectName.class))
|
|
return name;
|
|
return Util.newObjectName(name.getSerializedNameString());
|
|
}
|
|
|
|
/**
|
|
* Construct an object name from the given string.
|
|
*
|
|
* @param name A string representation of the object name.
|
|
*
|
|
* @exception MalformedObjectNameException The string passed as a
|
|
* parameter does not have the right format.
|
|
* @exception NullPointerException The <code>name</code> parameter
|
|
* is null.
|
|
*/
|
|
public ObjectName(String name)
|
|
throws MalformedObjectNameException {
|
|
construct(name);
|
|
}
|
|
|
|
/**
|
|
* Construct an object name with exactly one key property.
|
|
*
|
|
* @param domain The domain part of the object name.
|
|
* @param key The attribute in the key property of the object name.
|
|
* @param value The value in the key property of the object name.
|
|
*
|
|
* @exception MalformedObjectNameException The
|
|
* <code>domain</code>, <code>key</code>, or <code>value</code>
|
|
* contains an illegal character, or <code>value</code> does not
|
|
* follow the rules for quoting.
|
|
* @exception NullPointerException One of the parameters is null.
|
|
*/
|
|
public ObjectName(String domain, String key, String value)
|
|
throws MalformedObjectNameException {
|
|
// If key or value are null a NullPointerException
|
|
// will be thrown by the put method in Hashtable.
|
|
//
|
|
Map<String,String> table = Collections.singletonMap(key, value);
|
|
construct(domain, table);
|
|
}
|
|
|
|
/**
|
|
* Construct an object name with several key properties from a Hashtable.
|
|
*
|
|
* @param domain The domain part of the object name.
|
|
* @param table A hash table containing one or more key
|
|
* properties. The key of each entry in the table is the key of a
|
|
* key property in the object name. The associated value in the
|
|
* table is the associated value in the object name.
|
|
*
|
|
* @exception MalformedObjectNameException The <code>domain</code>
|
|
* contains an illegal character, or one of the keys or values in
|
|
* <code>table</code> contains an illegal character, or one of the
|
|
* values in <code>table</code> does not follow the rules for
|
|
* quoting.
|
|
* @exception NullPointerException One of the parameters is null.
|
|
*/
|
|
public ObjectName(String domain, Hashtable<String,String> table)
|
|
throws MalformedObjectNameException {
|
|
construct(domain, table);
|
|
/* The exception for when a key or value in the table is not a
|
|
String is now ClassCastException rather than
|
|
MalformedObjectNameException. This was not previously
|
|
specified. */
|
|
}
|
|
|
|
// Category : ObjectName Construction <==============================
|
|
|
|
|
|
// Category : Getter methods ------------------------------>
|
|
|
|
/**
|
|
* Checks whether the object name is a pattern.
|
|
* <p>
|
|
* An object name is a pattern if its domain contains a
|
|
* wildcard or if the object name is a property pattern.
|
|
*
|
|
* @return True if the name is a pattern, otherwise false.
|
|
*/
|
|
public boolean isPattern() {
|
|
return (_domain_pattern ||
|
|
_property_list_pattern ||
|
|
_property_value_pattern);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the object name is a pattern on the domain part.
|
|
*
|
|
* @return True if the name is a domain pattern, otherwise false.
|
|
*
|
|
*/
|
|
public boolean isDomainPattern() {
|
|
return _domain_pattern;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the object name is a pattern on the key properties.
|
|
* <p>
|
|
* An object name is a pattern on the key properties if it is a
|
|
* pattern on the key property list (e.g. "d:k=v,*") or on the
|
|
* property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*").
|
|
*
|
|
* @return True if the name is a property pattern, otherwise false.
|
|
*/
|
|
public boolean isPropertyPattern() {
|
|
return _property_list_pattern || _property_value_pattern;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the object name is a pattern on the key property list.
|
|
* <p>
|
|
* For example, "d:k=v,*" and "d:k=*,*" are key property list patterns
|
|
* whereas "d:k=*" is not.
|
|
*
|
|
* @return True if the name is a property list pattern, otherwise false.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public boolean isPropertyListPattern() {
|
|
return _property_list_pattern;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the object name is a pattern on the value part
|
|
* of at least one of the key properties.
|
|
* <p>
|
|
* For example, "d:k=*" and "d:k=*,*" are property value patterns
|
|
* whereas "d:k=v,*" is not.
|
|
*
|
|
* @return True if the name is a property value pattern, otherwise false.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public boolean isPropertyValuePattern() {
|
|
return _property_value_pattern;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the value associated with a key in a key
|
|
* property is a pattern.
|
|
*
|
|
* @param property The property whose value is to be checked.
|
|
*
|
|
* @return True if the value associated with the given key property
|
|
* is a pattern, otherwise false.
|
|
*
|
|
* @exception NullPointerException If <code>property</code> is null.
|
|
* @exception IllegalArgumentException If <code>property</code> is not
|
|
* a valid key property for this ObjectName.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public boolean isPropertyValuePattern(String property) {
|
|
if (property == null)
|
|
throw new NullPointerException("key property can't be null");
|
|
for (int i = 0; i < _ca_array.length; i++) {
|
|
Property prop = _ca_array[i];
|
|
String key = prop.getKeyString(_canonicalName);
|
|
if (key.equals(property))
|
|
return (prop instanceof PatternProperty);
|
|
}
|
|
throw new IllegalArgumentException("key property not found");
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the canonical form of the name; that is, a string
|
|
* representation where the properties are sorted in lexical
|
|
* order.</p>
|
|
*
|
|
* <p>More precisely, the canonical form of the name is a String
|
|
* consisting of the <em>domain part</em>, a colon
|
|
* (<code>:</code>), the <em>canonical key property list</em>, and
|
|
* a <em>pattern indication</em>.</p>
|
|
*
|
|
* <p>The <em>canonical key property list</em> is the same string
|
|
* as described for {@link #getCanonicalKeyPropertyListString()}.</p>
|
|
*
|
|
* <p>The <em>pattern indication</em> is:
|
|
* <ul>
|
|
* <li>empty for an ObjectName
|
|
* that is not a property list pattern;
|
|
* <li>an asterisk for an ObjectName
|
|
* that is a property list pattern with no keys; or
|
|
* <li>a comma and an
|
|
* asterisk (<code>,*</code>) for an ObjectName that is a property
|
|
* list pattern with at least one key.
|
|
* </ul>
|
|
*
|
|
* @return The canonical form of the name.
|
|
*/
|
|
public String getCanonicalName() {
|
|
return _canonicalName;
|
|
}
|
|
|
|
/**
|
|
* Returns the domain part.
|
|
*
|
|
* @return The domain.
|
|
*/
|
|
public String getDomain() {
|
|
return _canonicalName.substring(0, _domain_length);
|
|
}
|
|
|
|
/**
|
|
* Obtains the value associated with a key in a key property.
|
|
*
|
|
* @param property The property whose value is to be obtained.
|
|
*
|
|
* @return The value of the property, or null if there is no such
|
|
* property in this ObjectName.
|
|
*
|
|
* @exception NullPointerException If <code>property</code> is null.
|
|
*/
|
|
public String getKeyProperty(String property) {
|
|
return _getKeyPropertyList().get(property);
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the key properties as a Map. The returned
|
|
* value is a Map in which each key is a key in the
|
|
* ObjectName's key property list and each value is the associated
|
|
* value.</p>
|
|
*
|
|
* <p>The returned value must not be modified.</p>
|
|
*
|
|
* @return The table of key properties.
|
|
*/
|
|
private Map<String,String> _getKeyPropertyList() {
|
|
synchronized (this) {
|
|
if (_propertyList == null) {
|
|
// build (lazy eval) the property list from the canonical
|
|
// properties array
|
|
_propertyList = new HashMap<String,String>();
|
|
int len = _ca_array.length;
|
|
Property prop;
|
|
for (int i = len - 1; i >= 0; i--) {
|
|
prop = _ca_array[i];
|
|
_propertyList.put(prop.getKeyString(_canonicalName),
|
|
prop.getValueString(_canonicalName));
|
|
}
|
|
}
|
|
}
|
|
return _propertyList;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the key properties as a Hashtable. The returned
|
|
* value is a Hashtable in which each key is a key in the
|
|
* ObjectName's key property list and each value is the associated
|
|
* value.</p>
|
|
*
|
|
* <p>The returned value may be unmodifiable. If it is
|
|
* modifiable, changing it has no effect on this ObjectName.</p>
|
|
*
|
|
* @return The table of key properties.
|
|
*/
|
|
// CR 6441274 depends on the modification property defined above
|
|
public Hashtable<String,String> getKeyPropertyList() {
|
|
return new Hashtable<String,String>(_getKeyPropertyList());
|
|
}
|
|
|
|
/**
|
|
* <p>Returns a string representation of the list of key
|
|
* properties specified at creation time. If this ObjectName was
|
|
* constructed with the constructor {@link #ObjectName(String)},
|
|
* the key properties in the returned String will be in the same
|
|
* order as in the argument to the constructor.</p>
|
|
*
|
|
* @return The key property list string. This string is
|
|
* independent of whether the ObjectName is a pattern.
|
|
*/
|
|
public String getKeyPropertyListString() {
|
|
// BEWARE : we rebuild the propertyliststring at each call !!
|
|
if (_kp_array.length == 0) return "";
|
|
|
|
// the size of the string is the canonical one minus domain
|
|
// part and pattern part
|
|
final int total_size = _canonicalName.length() - _domain_length - 1
|
|
- (_property_list_pattern?2:0);
|
|
|
|
final char[] dest_chars = new char[total_size];
|
|
final char[] value = _canonicalName.toCharArray();
|
|
writeKeyPropertyListString(value,dest_chars,0);
|
|
return new String(dest_chars);
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the serialized string of the ObjectName.
|
|
* properties specified at creation time. If this ObjectName was
|
|
* constructed with the constructor {@link #ObjectName(String)},
|
|
* the key properties in the returned String will be in the same
|
|
* order as in the argument to the constructor.</p>
|
|
*
|
|
* @return The key property list string. This string is
|
|
* independent of whether the ObjectName is a pattern.
|
|
*/
|
|
private String getSerializedNameString() {
|
|
|
|
// the size of the string is the canonical one
|
|
final int total_size = _canonicalName.length();
|
|
final char[] dest_chars = new char[total_size];
|
|
final char[] value = _canonicalName.toCharArray();
|
|
final int offset = _domain_length+1;
|
|
|
|
// copy "domain:" into dest_chars
|
|
//
|
|
System.arraycopy(value, 0, dest_chars, 0, offset);
|
|
|
|
// Add property list string
|
|
final int end = writeKeyPropertyListString(value,dest_chars,offset);
|
|
|
|
// Add ",*" if necessary
|
|
if (_property_list_pattern) {
|
|
if (end == offset) {
|
|
// Property list string is empty.
|
|
dest_chars[end] = '*';
|
|
} else {
|
|
// Property list string is not empty.
|
|
dest_chars[end] = ',';
|
|
dest_chars[end+1] = '*';
|
|
}
|
|
}
|
|
|
|
return new String(dest_chars);
|
|
}
|
|
|
|
/**
|
|
* <p>Write a string representation of the list of key
|
|
* properties specified at creation time in the given array, starting
|
|
* at the specified offset. If this ObjectName was
|
|
* constructed with the constructor {@link #ObjectName(String)},
|
|
* the key properties in the returned String will be in the same
|
|
* order as in the argument to the constructor.</p>
|
|
*
|
|
* @return offset + #of chars written
|
|
*/
|
|
private int writeKeyPropertyListString(char[] canonicalChars,
|
|
char[] data, int offset) {
|
|
if (_kp_array.length == 0) return offset;
|
|
|
|
final char[] dest_chars = data;
|
|
final char[] value = canonicalChars;
|
|
|
|
int index = offset;
|
|
final int len = _kp_array.length;
|
|
final int last = len - 1;
|
|
for (int i = 0; i < len; i++) {
|
|
final Property prop = _kp_array[i];
|
|
final int prop_len = prop._key_length + prop._value_length + 1;
|
|
System.arraycopy(value, prop._key_index, dest_chars, index,
|
|
prop_len);
|
|
index += prop_len;
|
|
if (i < last ) dest_chars[index++] = ',';
|
|
}
|
|
return index;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Returns a string representation of the list of key properties,
|
|
* in which the key properties are sorted in lexical order. This
|
|
* is used in lexicographic comparisons performed in order to
|
|
* select MBeans based on their key property list. Lexical order
|
|
* is the order implied by {@link String#compareTo(String)
|
|
* String.compareTo(String)}.
|
|
*
|
|
* @return The canonical key property list string. This string is
|
|
* independent of whether the ObjectName is a pattern.
|
|
*/
|
|
public String getCanonicalKeyPropertyListString() {
|
|
if (_ca_array.length == 0) return "";
|
|
|
|
int len = _canonicalName.length();
|
|
if (_property_list_pattern) len -= 2;
|
|
return _canonicalName.substring(_domain_length +1, len);
|
|
}
|
|
// Category : Getter methods <===================================
|
|
|
|
// Category : Utilities ---------------------------------------->
|
|
|
|
/**
|
|
* <p>Returns a string representation of the object name. The
|
|
* format of this string is not specified, but users can expect
|
|
* that two ObjectNames return the same string if and only if they
|
|
* are equal.</p>
|
|
*
|
|
* @return a string representation of this object name.
|
|
*/
|
|
@Override
|
|
public String toString() {
|
|
return getSerializedNameString();
|
|
}
|
|
|
|
/**
|
|
* Compares the current object name with another object name. Two
|
|
* ObjectName instances are equal if and only if their canonical
|
|
* forms are equal. The canonical form is the string described
|
|
* for {@link #getCanonicalName()}.
|
|
*
|
|
* @param object The object name that the current object name is to be
|
|
* compared with.
|
|
*
|
|
* @return True if <code>object</code> is an ObjectName whose
|
|
* canonical form is equal to that of this ObjectName.
|
|
*/
|
|
@Override
|
|
public boolean equals(Object object) {
|
|
|
|
// same object case
|
|
if (this == object) return true;
|
|
|
|
// object is not an object name case
|
|
if (!(object instanceof ObjectName)) return false;
|
|
|
|
// equality when canonical names are the same
|
|
// (because usage of intern())
|
|
ObjectName on = (ObjectName) object;
|
|
String on_string = on._canonicalName;
|
|
if (_canonicalName == on_string) return true; // ES: OK
|
|
|
|
// Because we are sharing canonical form between object names,
|
|
// we have finished the comparison at this stage ==> unequal
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a hash code for this object name.
|
|
*
|
|
*/
|
|
@Override
|
|
public int hashCode() {
|
|
return _canonicalName.hashCode();
|
|
}
|
|
|
|
/**
|
|
* <p>Returns a quoted form of the given String, suitable for
|
|
* inclusion in an ObjectName. The returned value can be used as
|
|
* the value associated with a key in an ObjectName. The String
|
|
* <code>s</code> may contain any character. Appropriate quoting
|
|
* ensures that the returned value is legal in an ObjectName.</p>
|
|
*
|
|
* <p>The returned value consists of a quote ('"'), a sequence of
|
|
* characters corresponding to the characters of <code>s</code>,
|
|
* and another quote. Characters in <code>s</code> appear
|
|
* unchanged within the returned value except:</p>
|
|
*
|
|
* <ul>
|
|
* <li>A quote ('"') is replaced by a backslash (\) followed by a quote.</li>
|
|
* <li>An asterisk ('*') is replaced by a backslash (\) followed by an
|
|
* asterisk.</li>
|
|
* <li>A question mark ('?') is replaced by a backslash (\) followed by
|
|
* a question mark.</li>
|
|
* <li>A backslash ('\') is replaced by two backslashes.</li>
|
|
* <li>A newline character (the character '\n' in Java) is replaced
|
|
* by a backslash followed by the character '\n'.</li>
|
|
* </ul>
|
|
*
|
|
* @param s the String to be quoted.
|
|
*
|
|
* @return the quoted String.
|
|
*
|
|
* @exception NullPointerException if <code>s</code> is null.
|
|
*
|
|
*/
|
|
public static String quote(String s) {
|
|
final StringBuilder buf = new StringBuilder("\"");
|
|
final int len = s.length();
|
|
for (int i = 0; i < len; i++) {
|
|
char c = s.charAt(i);
|
|
switch (c) {
|
|
case '\n':
|
|
c = 'n';
|
|
buf.append('\\');
|
|
break;
|
|
case '\\':
|
|
case '\"':
|
|
case '*':
|
|
case '?':
|
|
buf.append('\\');
|
|
break;
|
|
}
|
|
buf.append(c);
|
|
}
|
|
buf.append('"');
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* <p>Returns an unquoted form of the given String. If
|
|
* <code>q</code> is a String returned by {@link #quote quote(s)},
|
|
* then <code>unquote(q).equals(s)</code>. If there is no String
|
|
* <code>s</code> for which <code>quote(s).equals(q)</code>, then
|
|
* unquote(q) throws an IllegalArgumentException.</p>
|
|
*
|
|
* <p>These rules imply that there is a one-to-one mapping between
|
|
* quoted and unquoted forms.</p>
|
|
*
|
|
* @param q the String to be unquoted.
|
|
*
|
|
* @return the unquoted String.
|
|
*
|
|
* @exception IllegalArgumentException if <code>q</code> could not
|
|
* have been returned by the {@link #quote} method, for instance
|
|
* if it does not begin and end with a quote (").
|
|
*
|
|
* @exception NullPointerException if <code>q</code> is null.
|
|
*
|
|
*/
|
|
public static String unquote(String q) {
|
|
final StringBuilder buf = new StringBuilder();
|
|
final int len = q.length();
|
|
if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
|
|
throw new IllegalArgumentException("Argument not quoted");
|
|
for (int i = 1; i < len - 1; i++) {
|
|
char c = q.charAt(i);
|
|
if (c == '\\') {
|
|
if (i == len - 2)
|
|
throw new IllegalArgumentException("Trailing backslash");
|
|
c = q.charAt(++i);
|
|
switch (c) {
|
|
case 'n':
|
|
c = '\n';
|
|
break;
|
|
case '\\':
|
|
case '\"':
|
|
case '*':
|
|
case '?':
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
"Bad character '" + c + "' after backslash");
|
|
}
|
|
} else {
|
|
switch (c) {
|
|
case '*' :
|
|
case '?' :
|
|
case '\"':
|
|
case '\n':
|
|
throw new IllegalArgumentException(
|
|
"Invalid unescaped character '" + c +
|
|
"' in the string to unquote");
|
|
}
|
|
}
|
|
buf.append(c);
|
|
}
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* Defines the wildcard "*:*" ObjectName.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public static final ObjectName WILDCARD = Util.newObjectName("*:*");
|
|
|
|
// Category : Utilities <===================================
|
|
|
|
// Category : QueryExp Interface ---------------------------------------->
|
|
|
|
/**
|
|
* <p>Test whether this ObjectName, which may be a pattern,
|
|
* matches another ObjectName. If <code>name</code> is a pattern,
|
|
* the result is false. If this ObjectName is a pattern, the
|
|
* result is true if and only if <code>name</code> matches the
|
|
* pattern. If neither this ObjectName nor <code>name</code> is
|
|
* a pattern, the result is true if and only if the two
|
|
* ObjectNames are equal as described for the {@link
|
|
* #equals(Object)} method.</p>
|
|
*
|
|
* @param name The name of the MBean to compare to.
|
|
*
|
|
* @return True if <code>name</code> matches this ObjectName.
|
|
*
|
|
* @exception NullPointerException if <code>name</code> is null.
|
|
*
|
|
*/
|
|
public boolean apply(ObjectName name) {
|
|
|
|
if (name == null) throw new NullPointerException();
|
|
|
|
if (name._domain_pattern ||
|
|
name._property_list_pattern ||
|
|
name._property_value_pattern)
|
|
return false;
|
|
|
|
// No pattern
|
|
if (!_domain_pattern &&
|
|
!_property_list_pattern &&
|
|
!_property_value_pattern)
|
|
return _canonicalName.equals(name._canonicalName);
|
|
|
|
return matchDomains(name) && matchKeys(name);
|
|
}
|
|
|
|
private final boolean matchDomains(ObjectName name) {
|
|
if (_domain_pattern) {
|
|
// wildmatch domains
|
|
// This ObjectName is the pattern
|
|
// The other ObjectName is the string.
|
|
return Util.wildmatch(name.getDomain(),getDomain());
|
|
}
|
|
return getDomain().equals(name.getDomain());
|
|
}
|
|
|
|
private final boolean matchKeys(ObjectName name) {
|
|
// If key property value pattern but not key property list
|
|
// pattern, then the number of key properties must be equal
|
|
//
|
|
if (_property_value_pattern &&
|
|
!_property_list_pattern &&
|
|
(name._ca_array.length != _ca_array.length))
|
|
return false;
|
|
|
|
// If key property value pattern or key property list pattern,
|
|
// then every property inside pattern should exist in name
|
|
//
|
|
if (_property_value_pattern || _property_list_pattern) {
|
|
final Map<String,String> nameProps = name._getKeyPropertyList();
|
|
final Property[] props = _ca_array;
|
|
final String cn = _canonicalName;
|
|
for (int i = props.length - 1; i >= 0 ; i--) {
|
|
// Find value in given object name for key at current
|
|
// index in receiver
|
|
//
|
|
final Property p = props[i];
|
|
final String k = p.getKeyString(cn);
|
|
final String v = nameProps.get(k);
|
|
// Did we find a value for this key ?
|
|
//
|
|
if (v == null) return false;
|
|
// If this property is ok (same key, same value), go to next
|
|
//
|
|
if (_property_value_pattern && (p instanceof PatternProperty)) {
|
|
// wildmatch key property values
|
|
// p is the property pattern, v is the string
|
|
if (Util.wildmatch(v,p.getValueString(cn)))
|
|
continue;
|
|
else
|
|
return false;
|
|
}
|
|
if (v.equals(p.getValueString(cn))) continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// If no pattern, then canonical names must be equal
|
|
//
|
|
final String p1 = name.getCanonicalKeyPropertyListString();
|
|
final String p2 = getCanonicalKeyPropertyListString();
|
|
return (p1.equals(p2));
|
|
}
|
|
|
|
/* Method inherited from QueryExp, no implementation needed here
|
|
because ObjectName is not relative to an MBeanServer and does
|
|
not contain a subquery.
|
|
*/
|
|
public void setMBeanServer(MBeanServer mbs) { }
|
|
|
|
// Category : QueryExp Interface <=========================
|
|
|
|
// Category : Comparable Interface ---------------------------------------->
|
|
|
|
/**
|
|
* <p>Compares two ObjectName instances. The ordering relation between
|
|
* ObjectNames is not completely specified but is intended to be such
|
|
* that a sorted list of ObjectNames will appear in an order that is
|
|
* convenient for a person to read.</p>
|
|
*
|
|
* <p>In particular, if the two ObjectName instances have different
|
|
* domains then their order is the lexicographical order of the domains.
|
|
* The ordering of the key property list remains unspecified.</p>
|
|
*
|
|
* <p>For example, the ObjectName instances below:</p>
|
|
* <ul>
|
|
* <li>Shapes:type=Square,name=3</li>
|
|
* <li>Colors:type=Red,name=2</li>
|
|
* <li>Shapes:type=Triangle,side=isosceles,name=2</li>
|
|
* <li>Colors:type=Red,name=1</li>
|
|
* <li>Shapes:type=Square,name=1</li>
|
|
* <li>Colors:type=Blue,name=1</li>
|
|
* <li>Shapes:type=Square,name=2</li>
|
|
* <li>JMImplementation:type=MBeanServerDelegate</li>
|
|
* <li>Shapes:type=Triangle,side=scalene,name=1</li>
|
|
* </ul>
|
|
* <p>could be ordered as follows:</p>
|
|
* <ul>
|
|
* <li>Colors:type=Blue,name=1</li>
|
|
* <li>Colors:type=Red,name=1</li>
|
|
* <li>Colors:type=Red,name=2</li>
|
|
* <li>JMImplementation:type=MBeanServerDelegate</li>
|
|
* <li>Shapes:type=Square,name=1</li>
|
|
* <li>Shapes:type=Square,name=2</li>
|
|
* <li>Shapes:type=Square,name=3</li>
|
|
* <li>Shapes:type=Triangle,side=scalene,name=1</li>
|
|
* <li>Shapes:type=Triangle,side=isosceles,name=2</li>
|
|
* </ul>
|
|
*
|
|
* @param name the ObjectName to be compared.
|
|
*
|
|
* @return a negative integer, zero, or a positive integer as this
|
|
* ObjectName is less than, equal to, or greater than the
|
|
* specified ObjectName.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public int compareTo(ObjectName name) {
|
|
// Quick optimization:
|
|
//
|
|
if (name == this) return 0;
|
|
|
|
// (1) Compare domains
|
|
//
|
|
int domainValue = this.getDomain().compareTo(name.getDomain());
|
|
if (domainValue != 0)
|
|
return domainValue;
|
|
|
|
// (2) Compare "type=" keys
|
|
//
|
|
// Within a given domain, all names with missing or empty "type="
|
|
// come before all names with non-empty type.
|
|
//
|
|
// When both types are missing or empty, canonical-name ordering
|
|
// applies which is a total order.
|
|
//
|
|
String thisTypeKey = this.getKeyProperty("type");
|
|
String anotherTypeKey = name.getKeyProperty("type");
|
|
if (thisTypeKey == null)
|
|
thisTypeKey = "";
|
|
if (anotherTypeKey == null)
|
|
anotherTypeKey = "";
|
|
int typeKeyValue = thisTypeKey.compareTo(anotherTypeKey);
|
|
if (typeKeyValue != 0)
|
|
return typeKeyValue;
|
|
|
|
// (3) Compare canonical names
|
|
//
|
|
return this.getCanonicalName().compareTo(name.getCanonicalName());
|
|
}
|
|
|
|
// Category : Comparable Interface <=========================
|
|
|
|
// Public methods <========================================
|
|
|
|
}
|