0001 /*
0002 * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.management;
0027
0028 import com.sun.jmx.mbeanserver.GetPropertyAction;
0029 import java.io.IOException;
0030 import java.io.InvalidObjectException;
0031 import java.io.ObjectInputStream;
0032 import java.io.ObjectOutputStream;
0033 import java.io.ObjectStreamField;
0034 import java.security.AccessController;
0035 import java.util.Arrays;
0036 import java.util.Collections;
0037 import java.util.HashMap;
0038 import java.util.Hashtable;
0039 import java.util.Map;
0040 import javax.management.MBeanServer;
0041 import javax.management.MalformedObjectNameException;
0042 import javax.management.QueryExp;
0043
0044 /**
0045 * <p>Represents the object name of an MBean, or a pattern that can
0046 * match the names of several MBeans. Instances of this class are
0047 * immutable.</p>
0048 *
0049 * <p>An instance of this class can be used to represent:</p>
0050 * <ul>
0051 * <li>An object name</li>
0052 * <li>An object name pattern, within the context of a query</li>
0053 * </ul>
0054 *
0055 * <p>An object name consists of two parts, the domain and the key
0056 * properties.</p>
0057 *
0058 * <p>The <em>domain</em> is a string of characters not including
0059 * the character colon (<code>:</code>). It is recommended that the domain
0060 * should not contain the string "{@code //}", which is reserved for future use.
0061 *
0062 * <p>If the domain includes at least one occurrence of the wildcard
0063 * characters asterisk (<code>*</code>) or question mark
0064 * (<code>?</code>), then the object name is a pattern. The asterisk
0065 * matches any sequence of zero or more characters, while the question
0066 * mark matches any single character.</p>
0067 *
0068 * <p>If the domain is empty, it will be replaced in certain contexts
0069 * by the <em>default domain</em> of the MBean server in which the
0070 * ObjectName is used.</p>
0071 *
0072 * <p>The <em>key properties</em> are an unordered set of keys and
0073 * associated values.</p>
0074 *
0075 * <p>Each <em>key</em> is a nonempty string of characters which may
0076 * not contain any of the characters comma (<code>,</code>), equals
0077 * (<code>=</code>), colon, asterisk, or question mark. The same key
0078 * may not occur twice in a given ObjectName.</p>
0079 *
0080 * <p>Each <em>value</em> associated with a key is a string of
0081 * characters that is either unquoted or quoted.</p>
0082 *
0083 * <p>An <em>unquoted value</em> is a possibly empty string of
0084 * characters which may not contain any of the characters comma,
0085 * equals, colon, or quote.</p>
0086 *
0087 * <p>If the <em>unquoted value</em> contains at least one occurrence
0088 * of the wildcard characters asterisk or question mark, then the object
0089 * name is a <em>property value pattern</em>. The asterisk matches any
0090 * sequence of zero or more characters, while the question mark matches
0091 * any single character.</p>
0092 *
0093 * <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
0094 * followed by a possibly empty string of characters, followed by
0095 * another quote. Within the string of characters, the backslash
0096 * (<code>\</code>) has a special meaning. It must be followed by
0097 * one of the following characters:</p>
0098 *
0099 * <ul>
0100 * <li>Another backslash. The second backslash has no special
0101 * meaning and the two characters represent a single backslash.</li>
0102 *
0103 * <li>The character 'n'. The two characters represent a newline
0104 * ('\n' in Java).</li>
0105 *
0106 * <li>A quote. The two characters represent a quote, and that quote
0107 * is not considered to terminate the quoted value. An ending closing
0108 * quote must be present for the quoted value to be valid.</li>
0109 *
0110 * <li>A question mark (?) or asterisk (*). The two characters represent
0111 * a question mark or asterisk respectively.</li>
0112 * </ul>
0113 *
0114 * <p>A quote may not appear inside a quoted value except immediately
0115 * after an odd number of consecutive backslashes.</p>
0116 *
0117 * <p>The quotes surrounding a quoted value, and any backslashes
0118 * within that value, are considered to be part of the value.</p>
0119 *
0120 * <p>If the <em>quoted value</em> contains at least one occurrence of
0121 * the characters asterisk or question mark and they are not preceded
0122 * by a backslash, then they are considered as wildcard characters and
0123 * the object name is a <em>property value pattern</em>. The asterisk
0124 * matches any sequence of zero or more characters, while the question
0125 * mark matches any single character.</p>
0126 *
0127 * <p>An ObjectName may be a <em>property list pattern</em>. In this
0128 * case it may have zero or more keys and associated values. It matches
0129 * a nonpattern ObjectName whose domain matches and that contains the
0130 * same keys and associated values, as well as possibly other keys and
0131 * values.</p>
0132 *
0133 * <p>An ObjectName is a <em>property value pattern</em> when at least
0134 * one of its <em>quoted</em> or <em>unquoted</em> key property values
0135 * contains the wildcard characters asterisk or question mark as described
0136 * above. In this case it has one or more keys and associated values, with
0137 * at least one of the values containing wildcard characters. It matches a
0138 * nonpattern ObjectName whose domain matches and that contains the same
0139 * keys whose values match; if the property value pattern is also a
0140 * property list pattern then the nonpattern ObjectName can contain
0141 * other keys and values.</p>
0142 *
0143 * <p>An ObjectName is a <em>property pattern</em> if it is either a
0144 * <em>property list pattern</em> or a <em>property value pattern</em>
0145 * or both.</p>
0146 *
0147 * <p>An ObjectName is a pattern if its domain contains a wildcard or
0148 * if the ObjectName is a property pattern.</p>
0149 *
0150 * <p>If an ObjectName is not a pattern, it must contain at least one
0151 * key with its associated value.</p>
0152 *
0153 * <p>Examples of ObjectName patterns are:</p>
0154 *
0155 * <ul>
0156 * <li>{@code *:type=Foo,name=Bar} to match names in any domain whose
0157 * exact set of keys is {@code type=Foo,name=Bar}.</li>
0158 * <li>{@code d:type=Foo,name=Bar,*} to match names in the domain
0159 * {@code d} that have the keys {@code type=Foo,name=Bar} plus
0160 * zero or more other keys.</li>
0161 * <li>{@code *:type=Foo,name=Bar,*} to match names in any domain
0162 * that has the keys {@code type=Foo,name=Bar} plus zero or
0163 * more other keys.</li>
0164 * <li>{@code d:type=F?o,name=Bar} will match e.g.
0165 * {@code d:type=Foo,name=Bar} and {@code d:type=Fro,name=Bar}.</li>
0166 * <li>{@code d:type=F*o,name=Bar} will match e.g.
0167 * {@code d:type=Fo,name=Bar} and {@code d:type=Frodo,name=Bar}.</li>
0168 * <li>{@code d:type=Foo,name="B*"} will match e.g.
0169 * {@code d:type=Foo,name="Bling"}. Wildcards are recognized even
0170 * inside quotes, and like other special characters can be escaped
0171 * with {@code \}.</li>
0172 * </ul>
0173 *
0174 * <p>An ObjectName can be written as a String with the following
0175 * elements in order:</p>
0176 *
0177 * <ul>
0178 * <li>The domain.
0179 * <li>A colon (<code>:</code>).
0180 * <li>A key property list as defined below.
0181 * </ul>
0182 *
0183 * <p>A key property list written as a String is a comma-separated
0184 * list of elements. Each element is either an asterisk or a key
0185 * property. A key property consists of a key, an equals
0186 * (<code>=</code>), and the associated value.</p>
0187 *
0188 * <p>At most one element of a key property list may be an asterisk.
0189 * If the key property list contains an asterisk element, the
0190 * ObjectName is a property list pattern.</p>
0191 *
0192 * <p>Spaces have no special significance in a String representing an
0193 * ObjectName. For example, the String:
0194 * <pre>
0195 * domain: key1 = value1 , key2 = value2
0196 * </pre>
0197 * represents an ObjectName with two keys. The name of each key
0198 * contains six characters, of which the first and last are spaces.
0199 * The value associated with the key <code>" key1 "</code>
0200 * also begins and ends with a space.</p>
0201 *
0202 * <p>In addition to the restrictions on characters spelt out above,
0203 * no part of an ObjectName may contain a newline character
0204 * (<code>'\n'</code>), whether the domain, a key, or a value, whether
0205 * quoted or unquoted. The newline character can be represented in a
0206 * quoted value with the sequence <code>\n</code>.
0207 *
0208 * <p>The rules on special characters and quoting apply regardless of
0209 * which constructor is used to make an ObjectName.</p>
0210 *
0211 * <p>To avoid collisions between MBeans supplied by different
0212 * vendors, a useful convention is to begin the domain name with the
0213 * reverse DNS name of the organization that specifies the MBeans,
0214 * followed by a period and a string whose interpretation is
0215 * determined by that organization. For example, MBeans specified by
0216 * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have
0217 * domains such as <code>com.sun.MyDomain</code>. This is essentially
0218 * the same convention as for Java-language package names.</p>
0219 *
0220 * <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
0221 *
0222 * @since 1.5
0223 */
0224 @SuppressWarnings("serial")
0225 // don't complain serialVersionUID not constant
0226 public class ObjectName implements Comparable<ObjectName>, QueryExp {
0227
0228 /**
0229 * A structure recording property structure and
0230 * proposing minimal services
0231 */
0232 private static class Property {
0233
0234 int _key_index;
0235 int _key_length;
0236 int _value_length;
0237
0238 /**
0239 * Constructor.
0240 */
0241 Property(int key_index, int key_length, int value_length) {
0242 _key_index = key_index;
0243 _key_length = key_length;
0244 _value_length = value_length;
0245 }
0246
0247 /**
0248 * Assigns the key index of property
0249 */
0250 void setKeyIndex(int key_index) {
0251 _key_index = key_index;
0252 }
0253
0254 /**
0255 * Returns a key string for receiver key
0256 */
0257 String getKeyString(String name) {
0258 return name.substring(_key_index, _key_index + _key_length);
0259 }
0260
0261 /**
0262 * Returns a value string for receiver key
0263 */
0264 String getValueString(String name) {
0265 int in_begin = _key_index + _key_length + 1;
0266 int out_end = in_begin + _value_length;
0267 return name.substring(in_begin, out_end);
0268 }
0269 }
0270
0271 /**
0272 * Marker class for value pattern property.
0273 */
0274 private static class PatternProperty extends Property {
0275 /**
0276 * Constructor.
0277 */
0278 PatternProperty(int key_index, int key_length, int value_length) {
0279 super (key_index, key_length, value_length);
0280 }
0281 }
0282
0283 // Inner classes <========================================
0284
0285 // Private fields ---------------------------------------->
0286
0287 // Serialization compatibility stuff -------------------->
0288
0289 // Two serial forms are supported in this class. The selected form depends
0290 // on system property "jmx.serial.form":
0291 // - "1.0" for JMX 1.0
0292 // - any other value for JMX 1.1 and higher
0293 //
0294 // Serial version for old serial form
0295 private static final long oldSerialVersionUID = -5467795090068647408L;
0296 //
0297 // Serial version for new serial form
0298 private static final long newSerialVersionUID = 1081892073854801359L;
0299 //
0300 // Serializable fields in old serial form
0301 private static final ObjectStreamField[] oldSerialPersistentFields = {
0302 new ObjectStreamField("domain", String.class),
0303 new ObjectStreamField("propertyList", Hashtable.class),
0304 new ObjectStreamField("propertyListString", String.class),
0305 new ObjectStreamField("canonicalName", String.class),
0306 new ObjectStreamField("pattern", Boolean.TYPE),
0307 new ObjectStreamField("propertyPattern", Boolean.TYPE) };
0308 //
0309 // Serializable fields in new serial form
0310 private static final ObjectStreamField[] newSerialPersistentFields = {};
0311 //
0312 // Actual serial version and serial form
0313 private static final long serialVersionUID;
0314 private static final ObjectStreamField[] serialPersistentFields;
0315 private static boolean compat = false;
0316 static {
0317 try {
0318 GetPropertyAction act = new GetPropertyAction(
0319 "jmx.serial.form");
0320 String form = AccessController.doPrivileged(act);
0321 compat = (form != null && form.equals("1.0"));
0322 } catch (Exception e) {
0323 // OK: exception means no compat with 1.0, too bad
0324 }
0325 if (compat) {
0326 serialPersistentFields = oldSerialPersistentFields;
0327 serialVersionUID = oldSerialVersionUID;
0328 } else {
0329 serialPersistentFields = newSerialPersistentFields;
0330 serialVersionUID = newSerialVersionUID;
0331 }
0332 }
0333
0334 //
0335 // Serialization compatibility stuff <==============================
0336
0337 // Class private fields ----------------------------------->
0338
0339 /**
0340 * a shared empty array for empty property lists
0341 */
0342 static final private Property[] _Empty_property_array = new Property[0];
0343
0344 // Class private fields <==============================
0345
0346 // Instance private fields ----------------------------------->
0347
0348 /**
0349 * a String containing the canonical name
0350 */
0351 private transient String _canonicalName;
0352
0353 /**
0354 * An array of properties in the same seq order as time creation
0355 */
0356 private transient Property[] _kp_array;
0357
0358 /**
0359 * An array of properties in the same seq order as canonical order
0360 */
0361 private transient Property[] _ca_array;
0362
0363 /**
0364 * The length of the domain part of built objectname
0365 */
0366 private transient int _domain_length = 0;
0367
0368 /**
0369 * The propertyList of built object name. Initialized lazily.
0370 * Table that contains all the pairs (key,value) for this ObjectName.
0371 */
0372 private transient Map<String, String> _propertyList;
0373
0374 /**
0375 * boolean that declares if this ObjectName domain part is a pattern
0376 */
0377 private transient boolean _domain_pattern = false;
0378
0379 /**
0380 * boolean that declares if this ObjectName contains a pattern on the
0381 * key property list
0382 */
0383 private transient boolean _property_list_pattern = false;
0384
0385 /**
0386 * boolean that declares if this ObjectName contains a pattern on the
0387 * value of at least one key property
0388 */
0389 private transient boolean _property_value_pattern = false;
0390
0391 // Instance private fields <=======================================
0392
0393 // Private fields <========================================
0394
0395 // Private methods ---------------------------------------->
0396
0397 // Category : Instance construction ------------------------->
0398
0399 /**
0400 * Initializes this {@link ObjectName} from the given string
0401 * representation.
0402 *
0403 * @param name A string representation of the {@link ObjectName}
0404 *
0405 * @exception MalformedObjectNameException The string passed as a
0406 * parameter does not have the right format.
0407 * @exception NullPointerException The <code>name</code> parameter
0408 * is null.
0409 */
0410 private void construct(String name)
0411 throws MalformedObjectNameException, NullPointerException {
0412
0413 // The name cannot be null
0414 if (name == null)
0415 throw new NullPointerException("name cannot be null");
0416
0417 // Test if the name is empty
0418 if (name.length() == 0) {
0419 // this is equivalent to the whole word query object name.
0420 _canonicalName = "*:*";
0421 _kp_array = _Empty_property_array;
0422 _ca_array = _Empty_property_array;
0423 _domain_length = 1;
0424 _propertyList = null;
0425 _domain_pattern = true;
0426 _property_list_pattern = true;
0427 _property_value_pattern = false;
0428 return;
0429 }
0430
0431 // initialize parsing of the string
0432 char[] name_chars = name.toCharArray();
0433 int len = name_chars.length;
0434 char[] canonical_chars = new char[len]; // canonical form will be same
0435 // length at most
0436 int cname_index = 0;
0437 int index = 0;
0438 char c, c1;
0439
0440 // parses domain part
0441 domain_parsing: while (index < len) {
0442 switch (c = name_chars[index]) {
0443 case ':':
0444 _domain_length = index++;
0445 break domain_parsing;
0446 case '=':
0447 // ":" omission check.
0448 //
0449 // Although "=" is a valid character in the domain part
0450 // it is true that it is rarely used in the real world.
0451 // So check straight away if the ":" has been omitted
0452 // from the ObjectName. This allows us to provide a more
0453 // accurate exception message.
0454 int i = ++index;
0455 while ((i < len) && (name_chars[i++] != ':'))
0456 if (i == len)
0457 throw new MalformedObjectNameException(
0458 "Domain part must be specified");
0459 break;
0460 case '\n':
0461 throw new MalformedObjectNameException(
0462 "Invalid character '\\n' in domain name");
0463 case '*':
0464 case '?':
0465 _domain_pattern = true;
0466 index++;
0467 break;
0468 default:
0469 index++;
0470 break;
0471 }
0472 }
0473
0474 // check for non-empty properties
0475 if (index == len)
0476 throw new MalformedObjectNameException(
0477 "Key properties cannot be empty");
0478
0479 // we have got the domain part, begins building of _canonicalName
0480 System.arraycopy(name_chars, 0, canonical_chars, 0,
0481 _domain_length);
0482 canonical_chars[_domain_length] = ':';
0483 cname_index = _domain_length + 1;
0484
0485 // parses property list
0486 Property prop;
0487 Map<String, Property> keys_map = new HashMap<String, Property>();
0488 String[] keys;
0489 String key_name;
0490 boolean quoted_value;
0491 int property_index = 0;
0492 int in_index;
0493 int key_index, key_length, value_index, value_length;
0494
0495 keys = new String[10];
0496 _kp_array = new Property[10];
0497 _property_list_pattern = false;
0498 _property_value_pattern = false;
0499
0500 while (index < len) {
0501 c = name_chars[index];
0502
0503 // case of pattern properties
0504 if (c == '*') {
0505 if (_property_list_pattern)
0506 throw new MalformedObjectNameException(
0507 "Cannot have several '*' characters in pattern "
0508 + "property list");
0509 else {
0510 _property_list_pattern = true;
0511 if ((++index < len) && (name_chars[index] != ','))
0512 throw new MalformedObjectNameException(
0513 "Invalid character found after '*': end of "
0514 + "name or ',' expected");
0515 else if (index == len) {
0516 if (property_index == 0) {
0517 // empty properties case
0518 _kp_array = _Empty_property_array;
0519 _ca_array = _Empty_property_array;
0520 _propertyList = Collections.emptyMap();
0521 }
0522 break;
0523 } else {
0524 // correct pattern spec in props, continue
0525 index++;
0526 continue;
0527 }
0528 }
0529 }
0530
0531 // standard property case, key part
0532 in_index = index;
0533 key_index = in_index;
0534 if (name_chars[in_index] == '=')
0535 throw new MalformedObjectNameException(
0536 "Invalid key (empty)");
0537 while ((in_index < len)
0538 && ((c1 = name_chars[in_index++]) != '='))
0539 switch (c1) {
0540 // '=' considered to introduce value part
0541 case '*':
0542 case '?':
0543 case ',':
0544 case ':':
0545 case '\n':
0546 final String ichar = ((c1 == '\n') ? "\\n" : ""
0547 + c1);
0548 throw new MalformedObjectNameException(
0549 "Invalid character '" + ichar
0550 + "' in key part of property");
0551 }
0552 if (name_chars[in_index - 1] != '=')
0553 throw new MalformedObjectNameException(
0554 "Unterminated key property part");
0555 value_index = in_index; // in_index pointing after '=' char
0556 key_length = value_index - key_index - 1; // found end of key
0557
0558 // standard property case, value part
0559 boolean value_pattern = false;
0560 if (in_index < len && name_chars[in_index] == '\"') {
0561 quoted_value = true;
0562 // the case of quoted value part
0563 quoted_value_parsing: while ((++in_index < len)
0564 && ((c1 = name_chars[in_index]) != '\"')) {
0565 // the case of an escaped character
0566 if (c1 == '\\') {
0567 if (++in_index == len)
0568 throw new MalformedObjectNameException(
0569 "Unterminated quoted value");
0570 switch (c1 = name_chars[in_index]) {
0571 case '\\':
0572 case '\"':
0573 case '?':
0574 case '*':
0575 case 'n':
0576 break; // valid character
0577 default:
0578 throw new MalformedObjectNameException(
0579 "Invalid escape sequence '\\" + c1
0580 + "' in quoted value");
0581 }
0582 } else if (c1 == '\n') {
0583 throw new MalformedObjectNameException(
0584 "Newline in quoted value");
0585 } else {
0586 switch (c1) {
0587 case '?':
0588 case '*':
0589 value_pattern = true;
0590 break;
0591 }
0592 }
0593 }
0594 if (in_index == len)
0595 throw new MalformedObjectNameException(
0596 "Unterminated quoted value");
0597 else
0598 value_length = ++in_index - value_index;
0599 } else {
0600 // the case of standard value part
0601 quoted_value = false;
0602 while ((in_index < len)
0603 && ((c1 = name_chars[in_index]) != ','))
0604 switch (c1) {
0605 // ',' considered to be the value separator
0606 case '*':
0607 case '?':
0608 value_pattern = true;
0609 in_index++;
0610 break;
0611 case '=':
0612 case ':':
0613 case '"':
0614 case '\n':
0615 final String ichar = ((c1 == '\n') ? "\\n" : ""
0616 + c1);
0617 throw new MalformedObjectNameException(
0618 "Invalid character '" + c1
0619 + "' in value part of property");
0620 default:
0621 in_index++;
0622 break;
0623 }
0624 value_length = in_index - value_index;
0625 }
0626
0627 // Parsed property, checks the end of name
0628 if (in_index == len - 1) {
0629 if (quoted_value)
0630 throw new MalformedObjectNameException(
0631 "Invalid ending character `"
0632 + name_chars[in_index] + "'");
0633 else
0634 throw new MalformedObjectNameException(
0635 "Invalid ending comma");
0636 } else
0637 in_index++;
0638
0639 // we got the key and value part, prepare a property for this
0640 if (!value_pattern) {
0641 prop = new Property(key_index, key_length, value_length);
0642 } else {
0643 _property_value_pattern = true;
0644 prop = new PatternProperty(key_index, key_length,
0645 value_length);
0646 }
0647 key_name = name
0648 .substring(key_index, key_index + key_length);
0649
0650 if (property_index == keys.length) {
0651 String[] tmp_string_array = new String[property_index + 10];
0652 System.arraycopy(keys, 0, tmp_string_array, 0,
0653 property_index);
0654 keys = tmp_string_array;
0655 }
0656 keys[property_index] = key_name;
0657
0658 addProperty(prop, property_index, keys_map, key_name);
0659 property_index++;
0660 index = in_index;
0661 }
0662
0663 // computes and set canonical name
0664 setCanonicalName(name_chars, canonical_chars, keys, keys_map,
0665 cname_index, property_index);
0666 }
0667
0668 /**
0669 * Construct an ObjectName from a domain and a Hashtable.
0670 *
0671 * @param domain Domain of the ObjectName.
0672 * @param props Map containing couples <i>key</i> -> <i>value</i>.
0673 *
0674 * @exception MalformedObjectNameException The <code>domain</code>
0675 * contains an illegal character, or one of the keys or values in
0676 * <code>table</code> contains an illegal character, or one of the
0677 * values in <code>table</code> does not follow the rules for quoting.
0678 * @exception NullPointerException One of the parameters is null.
0679 */
0680 private void construct(String domain, Map<String, String> props)
0681 throws MalformedObjectNameException, NullPointerException {
0682
0683 // The domain cannot be null
0684 if (domain == null)
0685 throw new NullPointerException("domain cannot be null");
0686
0687 // The key property list cannot be null
0688 if (props == null)
0689 throw new NullPointerException(
0690 "key property list cannot be null");
0691
0692 // The key property list cannot be empty
0693 if (props.isEmpty())
0694 throw new MalformedObjectNameException(
0695 "key property list cannot be empty");
0696
0697 // checks domain validity
0698 if (!isDomain(domain))
0699 throw new MalformedObjectNameException("Invalid domain: "
0700 + domain);
0701
0702 // init canonicalname
0703 final StringBuilder sb = new StringBuilder();
0704 sb.append(domain).append(':');
0705 _domain_length = domain.length();
0706
0707 // allocates the property array
0708 int nb_props = props.size();
0709 _kp_array = new Property[nb_props];
0710
0711 String[] keys = new String[nb_props];
0712 final Map<String, Property> keys_map = new HashMap<String, Property>();
0713 Property prop;
0714 int key_index;
0715 int i = 0;
0716 for (Map.Entry<String, String> entry : props.entrySet()) {
0717 if (sb.length() > 0)
0718 sb.append(",");
0719 String key = entry.getKey();
0720 String value;
0721 try {
0722 value = entry.getValue();
0723 } catch (ClassCastException e) {
0724 throw new MalformedObjectNameException(e.getMessage());
0725 }
0726 key_index = sb.length();
0727 checkKey(key);
0728 sb.append(key);
0729 keys[i] = key;
0730 sb.append("=");
0731 boolean value_pattern = checkValue(value);
0732 sb.append(value);
0733 if (!value_pattern) {
0734 prop = new Property(key_index, key.length(), value
0735 .length());
0736 } else {
0737 _property_value_pattern = true;
0738 prop = new PatternProperty(key_index, key.length(),
0739 value.length());
0740 }
0741 addProperty(prop, i, keys_map, key);
0742 i++;
0743 }
0744
0745 // initialize canonical name and data structure
0746 int len = sb.length();
0747 char[] initial_chars = new char[len];
0748 sb.getChars(0, len, initial_chars, 0);
0749 char[] canonical_chars = new char[len];
0750 System.arraycopy(initial_chars, 0, canonical_chars, 0,
0751 _domain_length + 1);
0752 setCanonicalName(initial_chars, canonical_chars, keys,
0753 keys_map, _domain_length + 1, _kp_array.length);
0754 }
0755
0756 // Category : Instance construction <==============================
0757
0758 // Category : Internal utilities ------------------------------>
0759
0760 /**
0761 * Add passed property to the list at the given index
0762 * for the passed key name
0763 */
0764 private void addProperty(Property prop, int index,
0765 Map<String, Property> keys_map, String key_name)
0766 throws MalformedObjectNameException {
0767
0768 if (keys_map.containsKey(key_name))
0769 throw new MalformedObjectNameException("key `" + key_name
0770 + "' already defined");
0771
0772 // if no more space for property arrays, have to increase it
0773 if (index == _kp_array.length) {
0774 Property[] tmp_prop_array = new Property[index + 10];
0775 System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
0776 _kp_array = tmp_prop_array;
0777 }
0778 _kp_array[index] = prop;
0779 keys_map.put(key_name, prop);
0780 }
0781
0782 /**
0783 * Sets the canonical name of receiver from input 'specified_chars'
0784 * array, by filling 'canonical_chars' array with found 'nb-props'
0785 * properties starting at position 'prop_index'.
0786 */
0787 private void setCanonicalName(char[] specified_chars,
0788 char[] canonical_chars, String[] keys,
0789 Map<String, Property> keys_map, int prop_index, int nb_props) {
0790
0791 // Sort the list of found properties
0792 if (_kp_array != _Empty_property_array) {
0793 String[] tmp_keys = new String[nb_props];
0794 Property[] tmp_props = new Property[nb_props];
0795
0796 System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
0797 Arrays.sort(tmp_keys);
0798 keys = tmp_keys;
0799 System.arraycopy(_kp_array, 0, tmp_props, 0, nb_props);
0800 _kp_array = tmp_props;
0801 _ca_array = new Property[nb_props];
0802
0803 // now assigns _ca_array to the sorted list of keys
0804 // (there cannot be two identical keys in an objectname.
0805 for (int i = 0; i < nb_props; i++)
0806 _ca_array[i] = keys_map.get(keys[i]);
0807
0808 // now we build the canonical name and set begin indexes of
0809 // properties to reflect canonical form
0810 int last_index = nb_props - 1;
0811 int prop_len;
0812 Property prop;
0813 for (int i = 0; i <= last_index; i++) {
0814 prop = _ca_array[i];
0815 // length of prop including '=' char
0816 prop_len = prop._key_length + prop._value_length + 1;
0817 System.arraycopy(specified_chars, prop._key_index,
0818 canonical_chars, prop_index, prop_len);
0819 prop.setKeyIndex(prop_index);
0820 prop_index += prop_len;
0821 if (i != last_index) {
0822 canonical_chars[prop_index] = ',';
0823 prop_index++;
0824 }
0825 }
0826 }
0827
0828 // terminate canonicalname with '*' in case of pattern
0829 if (_property_list_pattern) {
0830 if (_kp_array != _Empty_property_array)
0831 canonical_chars[prop_index++] = ',';
0832 canonical_chars[prop_index++] = '*';
0833 }
0834
0835 // we now build the canonicalname string
0836 _canonicalName = (new String(canonical_chars, 0, prop_index))
0837 .intern();
0838 }
0839
0840 /**
0841 * Parse a key.
0842 * <pre>final int endKey=parseKey(s,startKey);</pre>
0843 * <p>key starts at startKey (included), and ends at endKey (excluded).
0844 * If (startKey == endKey), then the key is empty.
0845 *
0846 * @param s The char array of the original string.
0847 * @param startKey index at which to begin parsing.
0848 * @return The index following the last character of the key.
0849 **/
0850 private static int parseKey(final char[] s, final int startKey)
0851 throws MalformedObjectNameException {
0852 int next = startKey;
0853 int endKey = startKey;
0854 final int len = s.length;
0855 while (next < len) {
0856 final char k = s[next++];
0857 switch (k) {
0858 case '*':
0859 case '?':
0860 case ',':
0861 case ':':
0862 case '\n':
0863 final String ichar = ((k == '\n') ? "\\n" : "" + k);
0864 throw new MalformedObjectNameException(
0865 "Invalid character in key: `" + ichar + "'");
0866 case '=':
0867 // we got the key.
0868 endKey = next - 1;
0869 break;
0870 default:
0871 if (next < len)
0872 continue;
0873 else
0874 endKey = next;
0875 }
0876 break;
0877 }
0878 return endKey;
0879 }
0880
0881 /**
0882 * Parse a value.
0883 * <pre>final int endVal=parseValue(s,startVal);</pre>
0884 * <p>value starts at startVal (included), and ends at endVal (excluded).
0885 * If (startVal == endVal), then the key is empty.
0886 *
0887 * @param s The char array of the original string.
0888 * @param startValue index at which to begin parsing.
0889 * @return The first element of the int array indicates the index
0890 * following the last character of the value. The second
0891 * element of the int array indicates that the value is
0892 * a pattern when its value equals 1.
0893 **/
0894 private static int[] parseValue(final char[] s, final int startValue)
0895 throws MalformedObjectNameException {
0896
0897 boolean value_pattern = false;
0898
0899 int next = startValue;
0900 int endValue = startValue;
0901
0902 final int len = s.length;
0903 final char q = s[startValue];
0904
0905 if (q == '"') {
0906 // quoted value
0907 if (++next == len)
0908 throw new MalformedObjectNameException("Invalid quote");
0909 while (next < len) {
0910 char last = s[next];
0911 if (last == '\\') {
0912 if (++next == len)
0913 throw new MalformedObjectNameException(
0914 "Invalid unterminated quoted character sequence");
0915 last = s[next];
0916 switch (last) {
0917 case '\\':
0918 case '?':
0919 case '*':
0920 case 'n':
0921 break;
0922 case '\"':
0923 // We have an escaped quote. If this escaped
0924 // quote is the last character, it does not
0925 // qualify as a valid termination quote.
0926 //
0927 if (next + 1 == len)
0928 throw new MalformedObjectNameException(
0929 "Missing termination quote");
0930 break;
0931 default:
0932 throw new MalformedObjectNameException(
0933 "Invalid quoted character sequence '\\"
0934 + last + "'");
0935 }
0936 } else if (last == '\n') {
0937 throw new MalformedObjectNameException(
0938 "Newline in quoted value");
0939 } else if (last == '\"') {
0940 next++;
0941 break;
0942 } else {
0943 switch (last) {
0944 case '?':
0945 case '*':
0946 value_pattern = true;
0947 break;
0948 }
0949 }
0950 next++;
0951
0952 // Check that last character is a termination quote.
0953 // We have already handled the case were the last
0954 // character is an escaped quote earlier.
0955 //
0956 if ((next >= len) && (last != '\"'))
0957 throw new MalformedObjectNameException(
0958 "Missing termination quote");
0959 }
0960 endValue = next;
0961 if (next < len) {
0962 if (s[next++] != ',')
0963 throw new MalformedObjectNameException(
0964 "Invalid quote");
0965 }
0966 } else {
0967 // Non quoted value.
0968 while (next < len) {
0969 final char v = s[next++];
0970 switch (v) {
0971 case '*':
0972 case '?':
0973 value_pattern = true;
0974 if (next < len)
0975 continue;
0976 else
0977 endValue = next;
0978 break;
0979 case '=':
0980 case ':':
0981 case '\n':
0982 final String ichar = ((v == '\n') ? "\\n" : "" + v);
0983 throw new MalformedObjectNameException(
0984 "Invalid character `" + ichar
0985 + "' in value");
0986 case ',':
0987 endValue = next - 1;
0988 break;
0989 default:
0990 if (next < len)
0991 continue;
0992 else
0993 endValue = next;
0994 }
0995 break;
0996 }
0997 }
0998 return new int[] { endValue, value_pattern ? 1 : 0 };
0999 }
1000
1001 /**
1002 * Check if the supplied value is a valid value.
1003 *
1004 * @return true if the value is a pattern, otherwise false.
1005 */
1006 private static boolean checkValue(String val)
1007 throws MalformedObjectNameException {
1008
1009 if (val == null)
1010 throw new NullPointerException("Invalid value (null)");
1011
1012 final int len = val.length();
1013 if (len == 0)
1014 return false;
1015
1016 final char[] s = val.toCharArray();
1017 final int[] result = parseValue(s, 0);
1018 final int endValue = result[0];
1019 final boolean value_pattern = result[1] == 1;
1020 if (endValue < len)
1021 throw new MalformedObjectNameException(
1022 "Invalid character in value: `" + s[endValue] + "'");
1023 return value_pattern;
1024 }
1025
1026 /**
1027 * Check if the supplied key is a valid key.
1028 */
1029 private static void checkKey(String key)
1030 throws MalformedObjectNameException, NullPointerException {
1031
1032 if (key == null)
1033 throw new NullPointerException("Invalid key (null)");
1034
1035 final int len = key.length();
1036 if (len == 0)
1037 throw new MalformedObjectNameException(
1038 "Invalid key (empty)");
1039 final char[] k = key.toCharArray();
1040 final int endKey = parseKey(k, 0);
1041 if (endKey < len)
1042 throw new MalformedObjectNameException(
1043 "Invalid character in value: `" + k[endKey] + "'");
1044 }
1045
1046 /*
1047 * Tests whether string s is matched by pattern p.
1048 * Supports "?", "*" each of which may be escaped with "\";
1049 * Not yet supported: internationalization; "\" inside brackets.<P>
1050 * Wildcard matching routine by Karl Heuer. Public Domain.<P>
1051 */
1052 private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
1053 char c;
1054 final int slen = s.length;
1055 final int plen = p.length;
1056
1057 while (pi < plen) { // While still string
1058 c = p[pi++];
1059 if (c == '?') {
1060 if (++si > slen)
1061 return false;
1062 } else if (c == '*') { // Wildcard
1063 if (pi >= plen)
1064 return true;
1065 do {
1066 if (wildmatch(s, p, si, pi))
1067 return true;
1068 } while (++si < slen);
1069 return false;
1070 } else {
1071 if (si >= slen || c != s[si++])
1072 return false;
1073 }
1074 }
1075 return (si == slen);
1076 }
1077
1078 // Category : Internal utilities <==============================
1079
1080 // Category : Internal accessors ------------------------------>
1081
1082 /**
1083 * Check if domain is a valid domain. Set _domain_pattern if appropriate.
1084 */
1085 private boolean isDomain(String domain) {
1086 if (domain == null)
1087 return true;
1088 final char[] d = domain.toCharArray();
1089 final int len = d.length;
1090 int next = 0;
1091 while (next < len) {
1092 final char c = d[next++];
1093 switch (c) {
1094 case ':':
1095 case '\n':
1096 return false;
1097 case '*':
1098 case '?':
1099 _domain_pattern = true;
1100 break;
1101 }
1102 }
1103 return true;
1104 }
1105
1106 // Category : Internal accessors <==============================
1107
1108 // Category : Serialization ----------------------------------->
1109
1110 /**
1111 * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
1112 * @serialData <ul>
1113 * <li>In the current serial form (value of property
1114 * <code>jmx.serial.form</code> differs from
1115 * <code>1.0</code>): the string
1116 * "<domain>:<properties><wild>",
1117 * where: <ul>
1118 * <li><domain> represents the domain part
1119 * of the {@link ObjectName}</li>
1120 * <li><properties> represents the list of
1121 * properties, as returned by
1122 * {@link #getKeyPropertyListString}
1123 * <li><wild> is empty if not
1124 * <code>isPropertyPattern</code>, or
1125 * is the character "<code>*</code>" if
1126 * <code>isPropertyPattern</code>
1127 * and <properties> is empty, or
1128 * is "<code>,*</code>" if
1129 * <code>isPropertyPattern</code> and
1130 * <properties> is not empty.
1131 * </li>
1132 * </ul>
1133 * The intent is that this string could be supplied
1134 * to the {@link #ObjectName(String)} constructor to
1135 * produce an equivalent {@link ObjectName}.
1136 * </li>
1137 * <li>In the old serial form (value of property
1138 * <code>jmx.serial.form</code> is
1139 * <code>1.0</code>): <domain> <propertyList>
1140 * <propertyListString> <canonicalName>
1141 * <pattern> <propertyPattern>,
1142 * where: <ul>
1143 * <li><domain> represents the domain part
1144 * of the {@link ObjectName}</li>
1145 * <li><propertyList> is the
1146 * {@link Hashtable} that contains all the
1147 * pairs (key,value) for this
1148 * {@link ObjectName}</li>
1149 * <li><propertyListString> is the
1150 * {@link String} representation of the
1151 * list of properties in any order (not
1152 * mandatorily a canonical representation)
1153 * </li>
1154 * <li><canonicalName> is the
1155 * {@link String} containing this
1156 * {@link ObjectName}'s canonical name</li>
1157 * <li><pattern> is a boolean which is
1158 * <code>true</code> if this
1159 * {@link ObjectName} contains a pattern</li>
1160 * <li><propertyPattern> is a boolean which
1161 * is <code>true</code> if this
1162 * {@link ObjectName} contains a pattern in
1163 * the list of properties</li>
1164 * </ul>
1165 * </li>
1166 * </ul>
1167 */
1168 private void readObject(ObjectInputStream in) throws IOException,
1169 ClassNotFoundException {
1170
1171 String cn;
1172 if (compat) {
1173 // Read an object serialized in the old serial form
1174 //
1175 //in.defaultReadObject();
1176 final ObjectInputStream.GetField fields = in.readFields();
1177 cn = (String) fields.get("domain", "default") + ":"
1178 + (String) fields.get("propertyListString", "");
1179 } else {
1180 // Read an object serialized in the new serial form
1181 //
1182 in.defaultReadObject();
1183 cn = (String) in.readObject();
1184 }
1185
1186 try {
1187 construct(cn);
1188 } catch (NullPointerException e) {
1189 throw new InvalidObjectException(e.toString());
1190 } catch (MalformedObjectNameException e) {
1191 throw new InvalidObjectException(e.toString());
1192 }
1193 }
1194
1195 /**
1196 * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
1197 * @serialData <ul>
1198 * <li>In the current serial form (value of property
1199 * <code>jmx.serial.form</code> differs from
1200 * <code>1.0</code>): the string
1201 * "<domain>:<properties><wild>",
1202 * where: <ul>
1203 * <li><domain> represents the domain part
1204 * of the {@link ObjectName}</li>
1205 * <li><properties> represents the list of
1206 * properties, as returned by
1207 * {@link #getKeyPropertyListString}
1208 * <li><wild> is empty if not
1209 * <code>isPropertyPattern</code>, or
1210 * is the character "<code>*</code>" if
1211 * this <code>isPropertyPattern</code>
1212 * and <properties> is empty, or
1213 * is "<code>,*</code>" if
1214 * <code>isPropertyPattern</code> and
1215 * <properties> is not empty.
1216 * </li>
1217 * </ul>
1218 * The intent is that this string could be supplied
1219 * to the {@link #ObjectName(String)} constructor to
1220 * produce an equivalent {@link ObjectName}.
1221 * </li>
1222 * <li>In the old serial form (value of property
1223 * <code>jmx.serial.form</code> is
1224 * <code>1.0</code>): <domain> <propertyList>
1225 * <propertyListString> <canonicalName>
1226 * <pattern> <propertyPattern>,
1227 * where: <ul>
1228 * <li><domain> represents the domain part
1229 * of the {@link ObjectName}</li>
1230 * <li><propertyList> is the
1231 * {@link Hashtable} that contains all the
1232 * pairs (key,value) for this
1233 * {@link ObjectName}</li>
1234 * <li><propertyListString> is the
1235 * {@link String} representation of the
1236 * list of properties in any order (not
1237 * mandatorily a canonical representation)
1238 * </li>
1239 * <li><canonicalName> is the
1240 * {@link String} containing this
1241 * {@link ObjectName}'s canonical name</li>
1242 * <li><pattern> is a boolean which is
1243 * <code>true</code> if this
1244 * {@link ObjectName} contains a pattern</li>
1245 * <li><propertyPattern> is a boolean which
1246 * is <code>true</code> if this
1247 * {@link ObjectName} contains a pattern in
1248 * the list of properties</li>
1249 * </ul>
1250 * </li>
1251 * </ul>
1252 */
1253 private void writeObject(ObjectOutputStream out) throws IOException {
1254
1255 if (compat) {
1256 // Serializes this instance in the old serial form
1257 // Read CR 6441274 before making any changes to this code
1258 ObjectOutputStream.PutField fields = out.putFields();
1259 fields.put("domain", _canonicalName.substring(0,
1260 _domain_length));
1261 fields.put("propertyList", getKeyPropertyList());
1262 fields
1263 .put("propertyListString",
1264 getKeyPropertyListString());
1265 fields.put("canonicalName", _canonicalName);
1266 fields.put("pattern",
1267 (_domain_pattern || _property_list_pattern));
1268 fields.put("propertyPattern", _property_list_pattern);
1269 out.writeFields();
1270 } else {
1271 // Serializes this instance in the new serial form
1272 //
1273 out.defaultWriteObject();
1274 out.writeObject(getSerializedNameString());
1275 }
1276 }
1277
1278 // Category : Serialization <===================================
1279
1280 // Private methods <========================================
1281
1282 // Public methods ---------------------------------------->
1283
1284 // Category : ObjectName Construction ------------------------------>
1285
1286 /**
1287 * <p>Return an instance of ObjectName that can be used anywhere
1288 * an object obtained with {@link #ObjectName(String) new
1289 * ObjectName(name)} can be used. The returned object may be of
1290 * a subclass of ObjectName. Calling this method twice with the
1291 * same parameters may return the same object or two equal but
1292 * not identical objects.</p>
1293 *
1294 * @param name A string representation of the object name.
1295 *
1296 * @return an ObjectName corresponding to the given String.
1297 *
1298 * @exception MalformedObjectNameException The string passed as a
1299 * parameter does not have the right format.
1300 * @exception NullPointerException The <code>name</code> parameter
1301 * is null.
1302 *
1303 */
1304 public static ObjectName getInstance(String name)
1305 throws MalformedObjectNameException, NullPointerException {
1306 return new ObjectName(name);
1307 }
1308
1309 /**
1310 * <p>Return an instance of ObjectName that can be used anywhere
1311 * an object obtained with {@link #ObjectName(String, String,
1312 * String) new ObjectName(domain, key, value)} can be used. The
1313 * returned object may be of a subclass of ObjectName. Calling
1314 * this method twice with the same parameters may return the same
1315 * object or two equal but not identical objects.</p>
1316 *
1317 * @param domain The domain part of the object name.
1318 * @param key The attribute in the key property of the object name.
1319 * @param value The value in the key property of the object name.
1320 *
1321 * @return an ObjectName corresponding to the given domain,
1322 * key, and value.
1323 *
1324 * @exception MalformedObjectNameException The
1325 * <code>domain</code>, <code>key</code>, or <code>value</code>
1326 * contains an illegal character, or <code>value</code> does not
1327 * follow the rules for quoting.
1328 * @exception NullPointerException One of the parameters is null.
1329 *
1330 */
1331 public static ObjectName getInstance(String domain, String key,
1332 String value) throws MalformedObjectNameException,
1333 NullPointerException {
1334 return new ObjectName(domain, key, value);
1335 }
1336
1337 /**
1338 * <p>Return an instance of ObjectName that can be used anywhere
1339 * an object obtained with {@link #ObjectName(String, Hashtable)
1340 * new ObjectName(domain, table)} can be used. The returned
1341 * object may be of a subclass of ObjectName. Calling this method
1342 * twice with the same parameters may return the same object or
1343 * two equal but not identical objects.</p>
1344 *
1345 * @param domain The domain part of the object name.
1346 * @param table A hash table containing one or more key
1347 * properties. The key of each entry in the table is the key of a
1348 * key property in the object name. The associated value in the
1349 * table is the associated value in the object name.
1350 *
1351 * @return an ObjectName corresponding to the given domain and
1352 * key mappings.
1353 *
1354 * @exception MalformedObjectNameException The <code>domain</code>
1355 * contains an illegal character, or one of the keys or values in
1356 * <code>table</code> contains an illegal character, or one of the
1357 * values in <code>table</code> does not follow the rules for
1358 * quoting.
1359 * @exception NullPointerException One of the parameters is null.
1360 *
1361 */
1362 public static ObjectName getInstance(String domain,
1363 Hashtable<String, String> table)
1364 throws MalformedObjectNameException, NullPointerException {
1365 return new ObjectName(domain, table);
1366 }
1367
1368 /**
1369 * <p>Return an instance of ObjectName that can be used anywhere
1370 * the given object can be used. The returned object may be of a
1371 * subclass of ObjectName. If <code>name</code> is of a subclass
1372 * of ObjectName, it is not guaranteed that the returned object
1373 * will be of the same class.</p>
1374 *
1375 * <p>The returned value may or may not be identical to
1376 * <code>name</code>. Calling this method twice with the same
1377 * parameters may return the same object or two equal but not
1378 * identical objects.</p>
1379 *
1380 * <p>Since ObjectName is immutable, it is not usually useful to
1381 * make a copy of an ObjectName. The principal use of this method
1382 * is to guard against a malicious caller who might pass an
1383 * instance of a subclass with surprising behavior to sensitive
1384 * code. Such code can call this method to obtain an ObjectName
1385 * that is known not to have surprising behavior.</p>
1386 *
1387 * @param name an instance of the ObjectName class or of a subclass
1388 *
1389 * @return an instance of ObjectName or a subclass that is known to
1390 * have the same semantics. If <code>name</code> respects the
1391 * semantics of ObjectName, then the returned object is equal
1392 * (though not necessarily identical) to <code>name</code>.
1393 *
1394 * @exception NullPointerException The <code>name</code> is null.
1395 *
1396 */
1397 public static ObjectName getInstance(ObjectName name)
1398 throws NullPointerException {
1399 if (name.getClass().equals(ObjectName.class))
1400 return name;
1401 try {
1402 return new ObjectName(name.getSerializedNameString());
1403 } catch (MalformedObjectNameException e) {
1404 throw new IllegalArgumentException("Unexpected: " + e);
1405 // can't happen
1406 }
1407 }
1408
1409 /**
1410 * Construct an object name from the given string.
1411 *
1412 * @param name A string representation of the object name.
1413 *
1414 * @exception MalformedObjectNameException The string passed as a
1415 * parameter does not have the right format.
1416 * @exception NullPointerException The <code>name</code> parameter
1417 * is null.
1418 */
1419 public ObjectName(String name) throws MalformedObjectNameException,
1420 NullPointerException {
1421 construct(name);
1422 }
1423
1424 /**
1425 * Construct an object name with exactly one key property.
1426 *
1427 * @param domain The domain part of the object name.
1428 * @param key The attribute in the key property of the object name.
1429 * @param value The value in the key property of the object name.
1430 *
1431 * @exception MalformedObjectNameException The
1432 * <code>domain</code>, <code>key</code>, or <code>value</code>
1433 * contains an illegal character, or <code>value</code> does not
1434 * follow the rules for quoting.
1435 * @exception NullPointerException One of the parameters is null.
1436 */
1437 public ObjectName(String domain, String key, String value)
1438 throws MalformedObjectNameException, NullPointerException {
1439 // If key or value are null a NullPointerException
1440 // will be thrown by the put method in Hashtable.
1441 //
1442 Map<String, String> table = Collections
1443 .singletonMap(key, value);
1444 construct(domain, table);
1445 }
1446
1447 /**
1448 * Construct an object name with several key properties from a Hashtable.
1449 *
1450 * @param domain The domain part of the object name.
1451 * @param table A hash table containing one or more key
1452 * properties. The key of each entry in the table is the key of a
1453 * key property in the object name. The associated value in the
1454 * table is the associated value in the object name.
1455 *
1456 * @exception MalformedObjectNameException The <code>domain</code>
1457 * contains an illegal character, or one of the keys or values in
1458 * <code>table</code> contains an illegal character, or one of the
1459 * values in <code>table</code> does not follow the rules for
1460 * quoting.
1461 * @exception NullPointerException One of the parameters is null.
1462 */
1463 public ObjectName(String domain, Hashtable<String, String> table)
1464 throws MalformedObjectNameException, NullPointerException {
1465 construct(domain, table);
1466 /* The exception for when a key or value in the table is not a
1467 String is now ClassCastException rather than
1468 MalformedObjectNameException. This was not previously
1469 specified. */
1470 }
1471
1472 // Category : ObjectName Construction <==============================
1473
1474 // Category : Getter methods ------------------------------>
1475
1476 /**
1477 * Checks whether the object name is a pattern.
1478 * <p>
1479 * An object name is a pattern if its domain contains a
1480 * wildcard or if the object name is a property pattern.
1481 *
1482 * @return True if the name is a pattern, otherwise false.
1483 */
1484 public boolean isPattern() {
1485 return (_domain_pattern || _property_list_pattern || _property_value_pattern);
1486 }
1487
1488 /**
1489 * Checks whether the object name is a pattern on the domain part.
1490 *
1491 * @return True if the name is a domain pattern, otherwise false.
1492 *
1493 */
1494 public boolean isDomainPattern() {
1495 return _domain_pattern;
1496 }
1497
1498 /**
1499 * Checks whether the object name is a pattern on the key properties.
1500 * <p>
1501 * An object name is a pattern on the key properties if it is a
1502 * pattern on the key property list (e.g. "d:k=v,*") or on the
1503 * property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*").
1504 *
1505 * @return True if the name is a property pattern, otherwise false.
1506 */
1507 public boolean isPropertyPattern() {
1508 return _property_list_pattern || _property_value_pattern;
1509 }
1510
1511 /**
1512 * Checks whether the object name is a pattern on the key property list.
1513 * <p>
1514 * For example, "d:k=v,*" and "d:k=*,*" are key property list patterns
1515 * whereas "d:k=*" is not.
1516 *
1517 * @return True if the name is a property list pattern, otherwise false.
1518 *
1519 * @since 1.6
1520 */
1521 public boolean isPropertyListPattern() {
1522 return _property_list_pattern;
1523 }
1524
1525 /**
1526 * Checks whether the object name is a pattern on the value part
1527 * of at least one of the key properties.
1528 * <p>
1529 * For example, "d:k=*" and "d:k=*,*" are property value patterns
1530 * whereas "d:k=v,*" is not.
1531 *
1532 * @return True if the name is a property value pattern, otherwise false.
1533 *
1534 * @since 1.6
1535 */
1536 public boolean isPropertyValuePattern() {
1537 return _property_value_pattern;
1538 }
1539
1540 /**
1541 * Checks whether the value associated with a key in a key
1542 * property is a pattern.
1543 *
1544 * @param property The property whose value is to be checked.
1545 *
1546 * @return True if the value associated with the given key property
1547 * is a pattern, otherwise false.
1548 *
1549 * @exception NullPointerException If <code>property</code> is null.
1550 * @exception IllegalArgumentException If <code>property</code> is not
1551 * a valid key property for this ObjectName.
1552 *
1553 * @since 1.6
1554 */
1555 public boolean isPropertyValuePattern(String property)
1556 throws NullPointerException, IllegalArgumentException {
1557 if (property == null)
1558 throw new NullPointerException("key property can't be null");
1559 for (int i = 0; i < _ca_array.length; i++) {
1560 Property prop = _ca_array[i];
1561 String key = prop.getKeyString(_canonicalName);
1562 if (key.equals(property))
1563 return (prop instanceof PatternProperty);
1564 }
1565 throw new IllegalArgumentException("key property not found");
1566 }
1567
1568 /**
1569 * <p>Returns the canonical form of the name; that is, a string
1570 * representation where the properties are sorted in lexical
1571 * order.</p>
1572 *
1573 * <p>More precisely, the canonical form of the name is a String
1574 * consisting of the <em>domain part</em>, a colon
1575 * (<code>:</code>), the <em>canonical key property list</em>, and
1576 * a <em>pattern indication</em>.</p>
1577 *
1578 * <p>The <em>canonical key property list</em> is the same string
1579 * as described for {@link #getCanonicalKeyPropertyListString()}.</p>
1580 *
1581 * <p>The <em>pattern indication</em> is:
1582 * <ul>
1583 * <li>empty for an ObjectName
1584 * that is not a property list pattern;
1585 * <li>an asterisk for an ObjectName
1586 * that is a property list pattern with no keys; or
1587 * <li>a comma and an
1588 * asterisk (<code>,*</code>) for an ObjectName that is a property
1589 * list pattern with at least one key.
1590 * </ul></p>
1591 *
1592 * @return The canonical form of the name.
1593 */
1594 public String getCanonicalName() {
1595 return _canonicalName;
1596 }
1597
1598 /**
1599 * Returns the domain part.
1600 *
1601 * @return The domain.
1602 */
1603 public String getDomain() {
1604 return _canonicalName.substring(0, _domain_length);
1605 }
1606
1607 /**
1608 * Obtains the value associated with a key in a key property.
1609 *
1610 * @param property The property whose value is to be obtained.
1611 *
1612 * @return The value of the property, or null if there is no such
1613 * property in this ObjectName.
1614 *
1615 * @exception NullPointerException If <code>property</code> is null.
1616 */
1617 public String getKeyProperty(String property)
1618 throws NullPointerException {
1619 return _getKeyPropertyList().get(property);
1620 }
1621
1622 /**
1623 * <p>Returns the key properties as a Map. The returned
1624 * value is a Map in which each key is a key in the
1625 * ObjectName's key property list and each value is the associated
1626 * value.</p>
1627 *
1628 * <p>The returned value must not be modified.</p>
1629 *
1630 * @return The table of key properties.
1631 */
1632 private Map<String, String> _getKeyPropertyList() {
1633 synchronized (this ) {
1634 if (_propertyList == null) {
1635 // build (lazy eval) the property list from the canonical
1636 // properties array
1637 _propertyList = new HashMap<String, String>();
1638 int len = _ca_array.length;
1639 Property prop;
1640 for (int i = len - 1; i >= 0; i--) {
1641 prop = _ca_array[i];
1642 _propertyList.put(
1643 prop.getKeyString(_canonicalName), prop
1644 .getValueString(_canonicalName));
1645 }
1646 }
1647 }
1648 return _propertyList;
1649 }
1650
1651 /**
1652 * <p>Returns the key properties as a Hashtable. The returned
1653 * value is a Hashtable in which each key is a key in the
1654 * ObjectName's key property list and each value is the associated
1655 * value.</p>
1656 *
1657 * <p>The returned value may be unmodifiable. If it is
1658 * modifiable, changing it has no effect on this ObjectName.</p>
1659 *
1660 * @return The table of key properties.
1661 */
1662 // CR 6441274 depends on the modification property defined above
1663 public Hashtable<String, String> getKeyPropertyList() {
1664 return new Hashtable<String, String>(_getKeyPropertyList());
1665 }
1666
1667 /**
1668 * <p>Returns a string representation of the list of key
1669 * properties specified at creation time. If this ObjectName was
1670 * constructed with the constructor {@link #ObjectName(String)},
1671 * the key properties in the returned String will be in the same
1672 * order as in the argument to the constructor.</p>
1673 *
1674 * @return The key property list string. This string is
1675 * independent of whether the ObjectName is a pattern.
1676 */
1677 public String getKeyPropertyListString() {
1678 // BEWARE : we rebuild the propertyliststring at each call !!
1679 if (_kp_array.length == 0)
1680 return "";
1681
1682 // the size of the string is the canonical one minus domain
1683 // part and pattern part
1684 final int total_size = _canonicalName.length() - _domain_length
1685 - 1 - (_property_list_pattern ? 2 : 0);
1686
1687 final char[] dest_chars = new char[total_size];
1688 final char[] value = _canonicalName.toCharArray();
1689 writeKeyPropertyListString(value, dest_chars, 0);
1690 return new String(dest_chars);
1691 }
1692
1693 /**
1694 * <p>Returns the serialized string of the ObjectName.
1695 * properties specified at creation time. If this ObjectName was
1696 * constructed with the constructor {@link #ObjectName(String)},
1697 * the key properties in the returned String will be in the same
1698 * order as in the argument to the constructor.</p>
1699 *
1700 * @return The key property list string. This string is
1701 * independent of whether the ObjectName is a pattern.
1702 */
1703 private String getSerializedNameString() {
1704
1705 // the size of the string is the canonical one
1706 final int total_size = _canonicalName.length();
1707 final char[] dest_chars = new char[total_size];
1708 final char[] value = _canonicalName.toCharArray();
1709 final int offset = _domain_length + 1;
1710
1711 // copy "domain:" into dest_chars
1712 //
1713 System.arraycopy(value, 0, dest_chars, 0, offset);
1714
1715 // Add property list string
1716 final int end = writeKeyPropertyListString(value, dest_chars,
1717 offset);
1718
1719 // Add ",*" if necessary
1720 if (_property_list_pattern) {
1721 if (end == offset) {
1722 // Property list string is empty.
1723 dest_chars[end] = '*';
1724 } else {
1725 // Property list string is not empty.
1726 dest_chars[end] = ',';
1727 dest_chars[end + 1] = '*';
1728 }
1729 }
1730
1731 return new String(dest_chars);
1732 }
1733
1734 /**
1735 * <p>Write a string representation of the list of key
1736 * properties specified at creation time in the given array, starting
1737 * at the specified offset. If this ObjectName was
1738 * constructed with the constructor {@link #ObjectName(String)},
1739 * the key properties in the returned String will be in the same
1740 * order as in the argument to the constructor.</p>
1741 *
1742 * @return offset + #of chars written
1743 */
1744 private int writeKeyPropertyListString(char[] canonicalChars,
1745 char[] data, int offset) {
1746 if (_kp_array.length == 0)
1747 return offset;
1748
1749 final char[] dest_chars = data;
1750 final char[] value = _canonicalName.toCharArray();
1751
1752 int index = offset;
1753 final int len = _kp_array.length;
1754 final int last = len - 1;
1755 for (int i = 0; i < len; i++) {
1756 final Property prop = _kp_array[i];
1757 final int prop_len = prop._key_length + prop._value_length
1758 + 1;
1759 System.arraycopy(value, prop._key_index, dest_chars, index,
1760 prop_len);
1761 index += prop_len;
1762 if (i < last)
1763 dest_chars[index++] = ',';
1764 }
1765 return index;
1766 }
1767
1768 /**
1769 * Returns a string representation of the list of key properties,
1770 * in which the key properties are sorted in lexical order. This
1771 * is used in lexicographic comparisons performed in order to
1772 * select MBeans based on their key property list. Lexical order
1773 * is the order implied by {@link String#compareTo(String)
1774 * String.compareTo(String)}.
1775 *
1776 * @return The canonical key property list string. This string is
1777 * independent of whether the ObjectName is a pattern.
1778 */
1779 public String getCanonicalKeyPropertyListString() {
1780 if (_ca_array.length == 0)
1781 return "";
1782
1783 int len = _canonicalName.length();
1784 if (_property_list_pattern)
1785 len -= 2;
1786 return _canonicalName.substring(_domain_length + 1, len);
1787 }
1788
1789 // Category : Getter methods <===================================
1790
1791 // Category : Utilities ---------------------------------------->
1792
1793 /**
1794 * <p>Returns a string representation of the object name. The
1795 * format of this string is not specified, but users can expect
1796 * that two ObjectNames return the same string if and only if they
1797 * are equal.</p>
1798 *
1799 * @return a string representation of this object name.
1800 */
1801 public String toString() {
1802 return getSerializedNameString();
1803 }
1804
1805 /**
1806 * Compares the current object name with another object name. Two
1807 * ObjectName instances are equal if and only if their canonical
1808 * forms are equal. The canonical form is the string described
1809 * for {@link #getCanonicalName()}.
1810 *
1811 * @param object The object name that the current object name is to be
1812 * compared with.
1813 *
1814 * @return True if <code>object</code> is an ObjectName whose
1815 * canonical form is equal to that of this ObjectName.
1816 */
1817 public boolean equals(Object object) {
1818
1819 // same object case
1820 if (this == object)
1821 return true;
1822
1823 // object is not an object name case
1824 if (!(object instanceof ObjectName))
1825 return false;
1826
1827 // equality when canonical names are the same
1828 // (because usage of intern())
1829 ObjectName on = (ObjectName) object;
1830 String on_string = on._canonicalName;
1831 if (_canonicalName == on_string)
1832 return true;
1833
1834 // Because we are sharing canonical form between object names,
1835 // we have finished the comparison at this stage ==> unequal
1836 return false;
1837 }
1838
1839 /**
1840 * Returns a hash code for this object name.
1841 *
1842 */
1843 public int hashCode() {
1844 return _canonicalName.hashCode();
1845 }
1846
1847 /**
1848 * <p>Returns a quoted form of the given String, suitable for
1849 * inclusion in an ObjectName. The returned value can be used as
1850 * the value associated with a key in an ObjectName. The String
1851 * <code>s</code> may contain any character. Appropriate quoting
1852 * ensures that the returned value is legal in an ObjectName.</p>
1853 *
1854 * <p>The returned value consists of a quote ('"'), a sequence of
1855 * characters corresponding to the characters of <code>s</code>,
1856 * and another quote. Characters in <code>s</code> appear
1857 * unchanged within the returned value except:</p>
1858 *
1859 * <ul>
1860 * <li>A quote ('"') is replaced by a backslash (\) followed by a quote.</li>
1861 * <li>An asterisk ('*') is replaced by a backslash (\) followed by an
1862 * asterisk.</li>
1863 * <li>A question mark ('?') is replaced by a backslash (\) followed by
1864 * a question mark.</li>
1865 * <li>A backslash ('\') is replaced by two backslashes.</li>
1866 * <li>A newline character (the character '\n' in Java) is replaced
1867 * by a backslash followed by the character '\n'.</li>
1868 * </ul>
1869 *
1870 * @param s the String to be quoted.
1871 *
1872 * @return the quoted String.
1873 *
1874 * @exception NullPointerException if <code>s</code> is null.
1875 *
1876 */
1877 public static String quote(String s) throws NullPointerException {
1878 final StringBuilder buf = new StringBuilder("\"");
1879 final int len = s.length();
1880 for (int i = 0; i < len; i++) {
1881 char c = s.charAt(i);
1882 switch (c) {
1883 case '\n':
1884 c = 'n';
1885 buf.append('\\');
1886 break;
1887 case '\\':
1888 case '\"':
1889 case '*':
1890 case '?':
1891 buf.append('\\');
1892 break;
1893 }
1894 buf.append(c);
1895 }
1896 buf.append('"');
1897 return buf.toString();
1898 }
1899
1900 /**
1901 * <p>Returns an unquoted form of the given String. If
1902 * <code>q</code> is a String returned by {@link #quote quote(s)},
1903 * then <code>unquote(q).equals(s)</code>. If there is no String
1904 * <code>s</code> for which <code>quote(s).equals(q)</code>, then
1905 * unquote(q) throws an IllegalArgumentException.</p>
1906 *
1907 * <p>These rules imply that there is a one-to-one mapping between
1908 * quoted and unquoted forms.</p>
1909 *
1910 * @param q the String to be unquoted.
1911 *
1912 * @return the unquoted String.
1913 *
1914 * @exception IllegalArgumentException if <code>q</code> could not
1915 * have been returned by the {@link #quote} method, for instance
1916 * if it does not begin and end with a quote (").
1917 *
1918 * @exception NullPointerException if <code>q</code> is null.
1919 *
1920 */
1921 public static String unquote(String q)
1922 throws IllegalArgumentException, NullPointerException {
1923 final StringBuilder buf = new StringBuilder();
1924 final int len = q.length();
1925 if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
1926 throw new IllegalArgumentException("Argument not quoted");
1927 for (int i = 1; i < len - 1; i++) {
1928 char c = q.charAt(i);
1929 if (c == '\\') {
1930 if (i == len - 2)
1931 throw new IllegalArgumentException(
1932 "Trailing backslash");
1933 c = q.charAt(++i);
1934 switch (c) {
1935 case 'n':
1936 c = '\n';
1937 break;
1938 case '\\':
1939 case '\"':
1940 case '*':
1941 case '?':
1942 break;
1943 default:
1944 throw new IllegalArgumentException(
1945 "Bad character '" + c + "' after backslash");
1946 }
1947 } else {
1948 switch (c) {
1949 case '*':
1950 case '?':
1951 case '\"':
1952 case '\n':
1953 throw new IllegalArgumentException(
1954 "Invalid unescaped character '" + c
1955 + "' in the string to unquote");
1956 }
1957 }
1958 buf.append(c);
1959 }
1960 return buf.toString();
1961 }
1962
1963 /**
1964 * Defines the wildcard "*:*" ObjectName.
1965 *
1966 * @since 1.6
1967 */
1968 public static final ObjectName WILDCARD;
1969 static {
1970 try {
1971 WILDCARD = new ObjectName("*:*");
1972 } catch (MalformedObjectNameException e) {
1973 throw new Error("Can't initialize wildcard name", e);
1974 }
1975 }
1976
1977 // Category : Utilities <===================================
1978
1979 // Category : QueryExp Interface ---------------------------------------->
1980
1981 /**
1982 * <p>Test whether this ObjectName, which may be a pattern,
1983 * matches another ObjectName. If <code>name</code> is a pattern,
1984 * the result is false. If this ObjectName is a pattern, the
1985 * result is true if and only if <code>name</code> matches the
1986 * pattern. If neither this ObjectName nor <code>name</code> is
1987 * a pattern, the result is true if and only if the two
1988 * ObjectNames are equal as described for the {@link
1989 * #equals(Object)} method.</p>
1990 *
1991 * @param name The name of the MBean to compare to.
1992 *
1993 * @return True if <code>name</code> matches this ObjectName.
1994 *
1995 * @exception NullPointerException if <code>name</code> is null.
1996 *
1997 */
1998 public boolean apply(ObjectName name) throws NullPointerException {
1999
2000 if (name == null)
2001 throw new NullPointerException();
2002
2003 if (name._domain_pattern || name._property_list_pattern
2004 || name._property_value_pattern)
2005 return false;
2006
2007 // No pattern
2008 if (!_domain_pattern && !_property_list_pattern
2009 && !_property_value_pattern)
2010 return _canonicalName.equals(name._canonicalName);
2011
2012 return matchDomains(name) && matchKeys(name);
2013 }
2014
2015 private final boolean matchDomains(ObjectName name) {
2016 if (_domain_pattern) {
2017 // wildmatch domains
2018 final char[] dom_pattern = getDomain().toCharArray();
2019 final char[] dom_string = name.getDomain().toCharArray();
2020 return wildmatch(dom_string, dom_pattern, 0, 0);
2021 }
2022 return getDomain().equals(name.getDomain());
2023 }
2024
2025 private final boolean matchKeys(ObjectName name) {
2026 // If key property value pattern but not key property list
2027 // pattern, then the number of key properties must be equal
2028 //
2029 if (_property_value_pattern && !_property_list_pattern
2030 && (name._ca_array.length != _ca_array.length))
2031 return false;
2032
2033 // If key property value pattern or key property list pattern,
2034 // then every property inside pattern should exist in name
2035 //
2036 if (_property_value_pattern || _property_list_pattern) {
2037 final Map<String, String> nameProps = name
2038 ._getKeyPropertyList();
2039 final Property[] props = _ca_array;
2040 final String cn = _canonicalName;
2041 for (int i = props.length - 1; i >= 0; i--) {
2042 // Find value in given object name for key at current
2043 // index in receiver
2044 //
2045 final Property p = props[i];
2046 final String k = p.getKeyString(cn);
2047 final String v = nameProps.get(k);
2048 // Did we find a value for this key ?
2049 //
2050 if (v == null)
2051 return false;
2052 // If this property is ok (same key, same value), go to next
2053 //
2054 if (_property_value_pattern
2055 && (p instanceof PatternProperty)) {
2056 // wildmatch key property values
2057 final char[] val_pattern = p.getValueString(cn)
2058 .toCharArray();
2059 final char[] val_string = v.toCharArray();
2060 if (wildmatch(val_string, val_pattern, 0, 0))
2061 continue;
2062 else
2063 return false;
2064 }
2065 if (v.equals(p.getValueString(cn)))
2066 continue;
2067 return false;
2068 }
2069 return true;
2070 }
2071
2072 // If no pattern, then canonical names must be equal
2073 //
2074 final String p1 = name.getCanonicalKeyPropertyListString();
2075 final String p2 = getCanonicalKeyPropertyListString();
2076 return (p1.equals(p2));
2077 }
2078
2079 /* Method inherited from QueryExp, no implementation needed here
2080 because ObjectName is not relative to an MBeanServer and does
2081 not contain a subquery.
2082 */
2083 public void setMBeanServer(MBeanServer mbs) {
2084 }
2085
2086 // Category : QueryExp Interface <=========================
2087
2088 // Category : Comparable Interface ---------------------------------------->
2089
2090 /**
2091 * <p>Compares two ObjectName instances. The ordering relation between
2092 * ObjectNames is not completely specified but is intended to be such
2093 * that a sorted list of ObjectNames will appear in an order that is
2094 * convenient for a person to read.</p>
2095 *
2096 * <p>In particular, if the two ObjectName instances have different
2097 * domains then their order is the lexicographical order of the domains.
2098 * The ordering of the key property list remains unspecified.</p>
2099 *
2100 * <p>For example, the ObjectName instances below:</p>
2101 * <ul>
2102 * <li>Shapes:type=Square,name=3</li>
2103 * <li>Colors:type=Red,name=2</li>
2104 * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2105 * <li>Colors:type=Red,name=1</li>
2106 * <li>Shapes:type=Square,name=1</li>
2107 * <li>Colors:type=Blue,name=1</li>
2108 * <li>Shapes:type=Square,name=2</li>
2109 * <li>JMImplementation:type=MBeanServerDelegate</li>
2110 * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2111 * </ul>
2112 * <p>could be ordered as follows:</p>
2113 * <ul>
2114 * <li>Colors:type=Blue,name=1</li>
2115 * <li>Colors:type=Red,name=1</li>
2116 * <li>Colors:type=Red,name=2</li>
2117 * <li>JMImplementation:type=MBeanServerDelegate</li>
2118 * <li>Shapes:type=Square,name=1</li>
2119 * <li>Shapes:type=Square,name=2</li>
2120 * <li>Shapes:type=Square,name=3</li>
2121 * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2122 * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2123 * </ul>
2124 *
2125 * @param name the ObjectName to be compared.
2126 *
2127 * @return a negative integer, zero, or a positive integer as this
2128 * ObjectName is less than, equal to, or greater than the
2129 * specified ObjectName.
2130 *
2131 * @since 1.6
2132 */
2133 public int compareTo(ObjectName name) {
2134 // (1) Compare domains
2135 //
2136 int domainValue = this .getDomain().compareTo(name.getDomain());
2137 if (domainValue != 0)
2138 return domainValue;
2139
2140 // (2) Compare "type=" keys
2141 //
2142 // Within a given domain, all names with missing or empty "type="
2143 // come before all names with non-empty type.
2144 //
2145 // When both types are missing or empty, canonical-name ordering
2146 // applies which is a total order.
2147 //
2148 String this TypeKey = this .getKeyProperty("type");
2149 String anotherTypeKey = name.getKeyProperty("type");
2150 if (this TypeKey == null)
2151 this TypeKey = "";
2152 if (anotherTypeKey == null)
2153 anotherTypeKey = "";
2154 int typeKeyValue = this TypeKey.compareTo(anotherTypeKey);
2155 if (typeKeyValue != 0)
2156 return typeKeyValue;
2157
2158 // (3) Compare canonical names
2159 //
2160 return this .getCanonicalName().compareTo(
2161 name.getCanonicalName());
2162 }
2163
2164 // Category : Comparable Interface <=========================
2165
2166 // Public methods <========================================
2167
2168 }
|