/*
* 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 <========================================
}