| /* | |
| * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package javax.management; | |
| import com.sun.jmx.mbeanserver.Util; | |
| import java.io.IOException; | |
| import java.io.InvalidObjectException; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.ObjectStreamField; | |
| 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 | |
| * | |
| * @implNote The maximum allowed length of the domain name in this implementation | |
| * is {@code Integer.MAX_VALUE/4} | |
| */ | |
| public class ObjectName implements Comparable<ObjectName>, QueryExp { | |
| private static final int DOMAIN_PATTERN = 0x8000_0000; | |
| private static final int PROPLIST_PATTERN = 0x4000_0000; | |
| private static final int PROPVAL_PATTERN = 0x2000_0000; | |
| private static final int FLAG_MASK = DOMAIN_PATTERN | PROPLIST_PATTERN | | |
| PROPVAL_PATTERN; | |
| private static final int DOMAIN_LENGTH_MASK = ~FLAG_MASK; | |
| /** | |
| * 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 ----------------------------------------> | |
| private static final long serialVersionUID = 1081892073854801359L; | |
| // Class private fields -----------------------------------> | |
| /** | |
| * a shared empty array for empty property lists | |
| */ | |
| private static final 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 propertyList of built object name. Initialized lazily. | |
| * Table that contains all the pairs (key,value) for this ObjectName. | |
| */ | |
| private transient Map<String,String> _propertyList; | |
| /** | |
| * This field encodes _domain_pattern, _property_list_pattern and | |
| * _property_value_pattern booleans and _domain_length integer. | |
| * <p> | |
| * The following masks can be used to extract the value: | |
| * <ul> | |
| * <li>{@linkplain ObjectName#DOMAIN_PATTERN}</li> | |
| * <li>{@linkplain ObjectName#PROPLIST_PATTERN}</li> | |
| * <li>{@linkplain ObjectName#PROPVAL_PATTERN}</li> | |
| * <li>{@linkplain ObjectName#DOMAIN_LENGTH_MASK}</li> | |
| * </ul> | |
| * </p>. | |
| */ | |
| private transient int _compressed_storage = 0x0; | |
| // 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; | |
| setDomainLength(1); | |
| _propertyList = null; | |
| setDomainPattern(true); | |
| setPropertyListPattern(true); | |
| setPropertyValuePattern(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 ':' : | |
| setDomainLength(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 '?' : | |
| setDomainPattern(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 | |
| int _domain_length = getDomainLength(); | |
| 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[] 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]; | |
| setPropertyListPattern(false); | |
| setPropertyValuePattern(false); | |
| while (index < len) { | |
| c = name_chars[index]; | |
| // case of pattern properties | |
| if (c == '*') { | |
| if (isPropertyListPattern()) | |
| throw new MalformedObjectNameException( | |
| "Cannot have several '*' characters in pattern " + | |
| "property list"); | |
| else { | |
| setPropertyListPattern(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 { | |
| setPropertyValuePattern(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, | |
| * or the domain's length exceeds the maximum allowed length. | |
| * @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(':'); | |
| setDomainLength(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<>(); | |
| 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 { | |
| setPropertyValuePattern(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]; | |
| int copyLen = getDomainLength() + 1; | |
| System.arraycopy(initial_chars, 0, canonical_chars, 0, copyLen); | |
| setCanonicalName(initial_chars, canonical_chars, keys, keys_map, | |
| copyLen, _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 (isPropertyListPattern()) { | |
| 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 '?' : | |
| setDomainPattern(true); | |
| break; | |
| } | |
| } | |
| return true; | |
| } | |
| private int getDomainLength() { | |
| return _compressed_storage & DOMAIN_LENGTH_MASK; | |
| } | |
| /** | |
| * Validates and sets the domain length | |
| * @param length The domain length | |
| * @throws MalformedObjectNameException | |
| * When the given domain length exceeds the maximum allowed length | |
| */ | |
| private void setDomainLength(int length) throws MalformedObjectNameException { | |
| if ((length & FLAG_MASK) != 0 ) { | |
| throw new MalformedObjectNameException( | |
| "Domain name too long. Maximum allowed domain name length is:" + | |
| DOMAIN_LENGTH_MASK); | |
| } | |
| _compressed_storage = (_compressed_storage & FLAG_MASK) | length; | |
| } | |
| // Category : Internal accessors <============================== | |
| // Category : Serialization -----------------------------------> | |
| /** | |
| * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}. | |
| * @serialData 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> | |
| * <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}. | |
| */ | |
| private void readObject(ObjectInputStream in) | |
| throws IOException, ClassNotFoundException { | |
| String cn; | |
| in.defaultReadObject(); | |
| cn = (String)in.readObject(); | |
| try { | |
| construct(cn); | |
| } catch (NullPointerException | MalformedObjectNameException e) { | |
| throw new InvalidObjectException(e.toString()); | |
| } | |
| } | |
| /** | |
| * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}. | |
| * @serialData 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> | |
| * <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}. | |
| */ | |
| private void writeObject(ObjectOutputStream out) | |
| throws IOException { | |
| 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, or the domain's length exceeds | |
| * the maximum allowed length. | |
| * @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, or the domain's length exceeds the maximum allowed length. | |
| * @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 sun.management.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, or the domain's length exceeds | |
| * the maximum allowed length. | |
| * @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, or the domain's length exceeds the maximum allowed length. | |
| * @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 (_compressed_storage & FLAG_MASK) != 0; | |
| } | |
| /** | |
| * 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 (_compressed_storage & DOMAIN_PATTERN) != 0; | |
| } | |
| /** | |
| * Marks the object name as representing a pattern on the domain part. | |
| * @param value {@code true} if the domain name is a pattern, | |
| * {@code false} otherwise | |
| */ | |
| private void setDomainPattern(boolean value) { | |
| if (value) { | |
| _compressed_storage |= DOMAIN_PATTERN; | |
| } else { | |
| _compressed_storage &= ~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 (_compressed_storage & (PROPVAL_PATTERN | PROPLIST_PATTERN)) != 0; | |
| } | |
| /** | |
| * 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 (_compressed_storage & PROPLIST_PATTERN) != 0; | |
| } | |
| /** | |
| * Marks the object name as representing a pattern on the key property list. | |
| * @param value {@code true} if the key property list is a pattern, | |
| * {@code false} otherwise | |
| */ | |
| private void setPropertyListPattern(boolean value) { | |
| if (value) { | |
| _compressed_storage |= PROPLIST_PATTERN; | |
| } else { | |
| _compressed_storage &= ~PROPLIST_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 (_compressed_storage & PROPVAL_PATTERN) != 0; | |
| } | |
| /** | |
| * Marks the object name as representing a pattern on the value part. | |
| * @param value {@code true} if the value part of at least one of the | |
| * key properties is a pattern, {@code false} otherwise | |
| */ | |
| private void setPropertyValuePattern(boolean value) { | |
| if (value) { | |
| _compressed_storage |= PROPVAL_PATTERN; | |
| } else { | |
| _compressed_storage &= ~PROPVAL_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, getDomainLength()); | |
| } | |
| /** | |
| * 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<>(); | |
| 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<>(_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() - getDomainLength() - 1 | |
| - (isPropertyListPattern()?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 = getDomainLength() + 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 (isPropertyListPattern()) { | |
| 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 (isPropertyListPattern()) len -= 2; | |
| return _canonicalName.substring(getDomainLength() + 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 = sun.management.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.isPattern()) | |
| return false; | |
| // No pattern | |
| if (!isPattern()) | |
| return _canonicalName.equals(name._canonicalName); | |
| return matchDomains(name) && matchKeys(name); | |
| } | |
| private final boolean matchDomains(ObjectName name) { | |
| boolean useOptimized = (this.getClass() == ObjectName.class) && | |
| (name.getClass() == ObjectName.class); | |
| if (isDomainPattern()) { | |
| // wildmatch domains | |
| // This ObjectName is the pattern | |
| // The other ObjectName is the string. | |
| if (useOptimized) { | |
| return Util.wildmatch(name._canonicalName, _canonicalName, | |
| 0, name.getDomainLength(), 0, getDomainLength()); | |
| } | |
| return Util.wildmatch(name.getDomain(),getDomain()); | |
| } | |
| if (useOptimized) { | |
| return getDomainLength() == name.getDomainLength() && | |
| _canonicalName.regionMatches(0, name._canonicalName, 0, getDomainLength()); | |
| } | |
| 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 (isPropertyValuePattern() && | |
| !isPropertyListPattern() && | |
| (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 (isPropertyPattern()) { | |
| 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 (isPropertyValuePattern() && (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 <======================================== | |
| } |