/* * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ 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; /** *
Represents the object name of an MBean, or a pattern that can * match the names of several MBeans. Instances of this class are * immutable.
* *An instance of this class can be used to represent:
*An object name consists of two parts, the domain and the key * properties.
* *The domain is a string of characters not including
* the character colon (:
). It is recommended that the domain
* should not contain the string "{@code //}", which is reserved for future use.
*
*
If the domain includes at least one occurrence of the wildcard
* characters asterisk (*
) or question mark
* (?
), 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.
If the domain is empty, it will be replaced in certain contexts * by the default domain of the MBean server in which the * ObjectName is used.
* *The key properties are an unordered set of keys and * associated values.
* *Each key is a nonempty string of characters which may
* not contain any of the characters comma (,
), equals
* (=
), colon, asterisk, or question mark. The same key
* may not occur twice in a given ObjectName.
Each value associated with a key is a string of * characters that is either unquoted or quoted.
* *An unquoted value is a possibly empty string of * characters which may not contain any of the characters comma, * equals, colon, or quote.
* *If the unquoted value contains at least one occurrence * of the wildcard characters asterisk or question mark, then the object * name is a property value pattern. The asterisk matches any * sequence of zero or more characters, while the question mark matches * any single character.
* *A quoted value consists of a quote ("
),
* followed by a possibly empty string of characters, followed by
* another quote. Within the string of characters, the backslash
* (\
) has a special meaning. It must be followed by
* one of the following characters:
A quote may not appear inside a quoted value except immediately * after an odd number of consecutive backslashes.
* *The quotes surrounding a quoted value, and any backslashes * within that value, are considered to be part of the value.
* *If the quoted value 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 property value pattern. The asterisk * matches any sequence of zero or more characters, while the question * mark matches any single character.
* *An ObjectName may be a property list pattern. 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.
* *An ObjectName is a property value pattern when at least * one of its quoted or unquoted 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.
* *An ObjectName is a property pattern if it is either a * property list pattern or a property value pattern * or both.
* *An ObjectName is a pattern if its domain contains a wildcard or * if the ObjectName is a property pattern.
* *If an ObjectName is not a pattern, it must contain at least one * key with its associated value.
* *Examples of ObjectName patterns are:
* *An ObjectName can be written as a String with the following * elements in order:
* *:
).
* 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
* (=
), and the associated value.
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.
* *Spaces have no special significance in a String representing an * ObjectName. For example, the String: *
* domain: key1 = value1 , key2 = value2 ** 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
" key1 "
* also begins and ends with a space.
*
* In addition to the restrictions on characters spelt out above,
* no part of an ObjectName may contain a newline character
* ('\n'
), 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 \n
.
*
*
The rules on special characters and quoting apply regardless of * which constructor is used to make an ObjectName.
* *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
* Sun Microsystems Inc., DNS name sun.com
, would have
* domains such as com.sun.MyDomain
. This is essentially
* the same convention as for Java-language package names.
The serialVersionUID of this class is 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.
* 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, NullPointerException {
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] + "'");
}
/*
* Tests whether string s is matched by pattern p.
* Supports "?", "*" each of which may be escaped with "\";
* Not yet supported: internationalization; "\" inside brackets.
* Wildcard matching routine by Karl Heuer. Public Domain.
*/
private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
char c;
final int slen = s.length;
final int plen = p.length;
while (pi < plen) { // While still string
c = p[pi++];
if (c == '?') {
if (++si > slen) return false;
} else if (c == '*') { // Wildcard
if (pi >= plen) return true;
do {
if (wildmatch(s,p,si,pi)) return true;
} while (++si < slen);
return false;
} else {
if (si >= slen || c != s[si++]) return false;
}
}
return (si == slen);
}
// 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 char[] d=domain.toCharArray();
final int len = d.length;
int next = 0;
while (next < len) {
final char c = d[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 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. 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. 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. 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 The returned value may or may not be identical to
* 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.
* 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.
*
* 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.
*
* 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.
*
* 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 Returns the canonical form of the name; that is, a string
* representation where the properties are sorted in lexical
* order. More precisely, the canonical form of the name is a String
* consisting of the domain part, a colon
* ( The canonical key property list is the same string
* as described for {@link #getCanonicalKeyPropertyListString()}. The pattern indication is:
* 1081892073854801359L
.
*
* @since 1.5
*/
@SuppressWarnings("serial") // don't complain serialVersionUID not constant
public class ObjectName implements Comparablename
parameter
* is null.
*/
private void construct(String name)
throws MalformedObjectNameException, NullPointerException {
// 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
char[] name_chars = name.toCharArray();
int len = name_chars.length;
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;
Mapdomain
* contains an illegal character, or one of the keys or values in
* table
contains an illegal character, or one of the
* values in table
does not follow the rules for quoting.
* @exception NullPointerException One of the parameters is null.
*/
private void construct(String domain, Mapfinal int endKey=parseKey(s,startKey);
* final int endVal=parseValue(s,startVal);
*
*
*/
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 jmx.serial.form
differs from
* 1.0
): the string
* "<domain>:<properties><wild>",
* where:
*
* The intent is that this string could be supplied
* to the {@link #ObjectName(String)} constructor to
* produce an equivalent {@link ObjectName}.
* isPropertyPattern
, or
* is the character "*
" if
* isPropertyPattern
* and <properties> is empty, or
* is ",*
" if
* isPropertyPattern
and
* <properties> is not empty.
* jmx.serial.form
is
* 1.0
): <domain> <propertyList>
* <propertyListString> <canonicalName>
* <pattern> <propertyPattern>,
* where:
*
* true
if this
* {@link ObjectName} contains a patterntrue
if this
* {@link ObjectName} contains a pattern in
* the list of properties
*
*/
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 ------------------------------>
/**
* jmx.serial.form
differs from
* 1.0
): the string
* "<domain>:<properties><wild>",
* where:
*
* The intent is that this string could be supplied
* to the {@link #ObjectName(String)} constructor to
* produce an equivalent {@link ObjectName}.
* isPropertyPattern
, or
* is the character "*
" if
* this isPropertyPattern
* and <properties> is empty, or
* is ",*
" if
* isPropertyPattern
and
* <properties> is not empty.
* jmx.serial.form
is
* 1.0
): <domain> <propertyList>
* <propertyListString> <canonicalName>
* <pattern> <propertyPattern>,
* where:
*
* true
if this
* {@link ObjectName} contains a patterntrue
if this
* {@link ObjectName} contains a pattern in
* the list of propertiesname
parameter
* is null.
*
*/
public static ObjectName getInstance(String name)
throws MalformedObjectNameException, NullPointerException {
return new ObjectName(name);
}
/**
* domain
, key
, or value
* contains an illegal character, or value
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, NullPointerException {
return new ObjectName(domain, key, value);
}
/**
* domain
* contains an illegal character, or one of the keys or values in
* table
contains an illegal character, or one of the
* values in table
does not follow the rules for
* quoting.
* @exception NullPointerException One of the parameters is null.
*
*/
public static ObjectName getInstance(String domain,
Hashtablename
is of a subclass
* of ObjectName, it is not guaranteed that the returned object
* will be of the same class.name
. Calling this method twice with the same
* parameters may return the same object or two equal but not
* identical objects.name
respects the
* semantics of ObjectName, then the returned object is equal
* (though not necessarily identical) to name
.
*
* @exception NullPointerException The name
is null.
*
*/
public static ObjectName getInstance(ObjectName name)
throws NullPointerException {
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 name
parameter
* is null.
*/
public ObjectName(String name)
throws MalformedObjectNameException, NullPointerException {
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
* domain
, key
, or value
* contains an illegal character, or value
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, NullPointerException {
// If key or value are null a NullPointerException
// will be thrown by the put method in Hashtable.
//
Mapdomain
* contains an illegal character, or one of the keys or values in
* table
contains an illegal character, or one of the
* values in table
does not follow the rules for
* quoting.
* @exception NullPointerException One of the parameters is null.
*/
public ObjectName(String domain, Hashtableproperty
is null.
* @exception IllegalArgumentException If property
is not
* a valid key property for this ObjectName.
*
* @since 1.6
*/
public boolean isPropertyValuePattern(String property)
throws NullPointerException, IllegalArgumentException {
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");
}
/**
* :
), the canonical key property list, and
* a pattern indication.
*
,*
) for an ObjectName that is a property
* list pattern with at least one key.
*
property
is null.
*/
public String getKeyProperty(String property) throws NullPointerException {
return _getKeyPropertyList().get(property);
}
/**
* 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.
* *The returned value must not be modified.
* * @return The table of key properties. */ private MapReturns 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.
* *The returned value may be unmodifiable. If it is * modifiable, changing it has no effect on this ObjectName.
* * @return The table of key properties. */ // CR 6441274 depends on the modification property defined above public HashtableReturns 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.
* * @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); } /** *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.
* * @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); } /** *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.
* * @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 = _canonicalName.toCharArray(); 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 ----------------------------------------> /** *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.
* * @return a string representation of this object name. */ @Override public String toString() { return getSerializedNameString(); } String toQueryString() { return "like " + Query.value(toString()); } /** * 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 ifobject
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;
// 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();
}
/**
* 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
* s
may contain any character. Appropriate quoting
* ensures that the returned value is legal in an ObjectName.
The returned value consists of a quote ('"'), a sequence of
* characters corresponding to the characters of s
,
* and another quote. Characters in s
appear
* unchanged within the returned value except:
s
is null.
*
*/
public static String quote(String s)
throws NullPointerException {
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();
}
/**
* Returns an unquoted form of the given String. If
* q
is a String returned by {@link #quote quote(s)},
* then unquote(q).equals(s)
. If there is no String
* s
for which quote(s).equals(q)
, then
* unquote(q) throws an IllegalArgumentException.
These rules imply that there is a one-to-one mapping between * quoted and unquoted forms.
* * @param q the String to be unquoted. * * @return the unquoted String. * * @exception IllegalArgumentException ifq
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 q
is null.
*
*/
public static String unquote(String q)
throws IllegalArgumentException, NullPointerException {
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 ---------------------------------------->
/**
* Test whether this ObjectName, which may be a pattern,
* matches another ObjectName. If name
is a pattern,
* the result is false. If this ObjectName is a pattern, the
* result is true if and only if name
matches the
* pattern. If neither this ObjectName nor name
is
* a pattern, the result is true if and only if the two
* ObjectNames are equal as described for the {@link
* #equals(Object)} method.
name
matches this ObjectName.
*
* @exception NullPointerException if name
is null.
*
*/
public boolean apply(ObjectName name) throws NullPointerException {
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
final char[] dom_pattern = getDomain().toCharArray();
final char[] dom_string = name.getDomain().toCharArray();
return wildmatch(dom_string,dom_pattern,0,0);
}
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 MapCompares 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.
* *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.
* *For example, the ObjectName instances below:
*could be ordered as follows:
*