1 /*
   2  * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.management;
  27 
  28 import com.sun.jmx.mbeanserver.GetPropertyAction;
  29 import com.sun.jmx.mbeanserver.Util;
  30 import java.io.IOException;
  31 import java.io.InvalidObjectException;
  32 import java.io.ObjectInputStream;
  33 import java.io.ObjectOutputStream;
  34 import java.io.ObjectStreamField;
  35 import java.security.AccessController;
  36 import java.util.Arrays;
  37 import java.util.Collections;
  38 import java.util.HashMap;
  39 import java.util.Hashtable;
  40 import java.util.Map;
  41 
  42 /**
  43  * <p>Represents the object name of an MBean, or a pattern that can
  44  * match the names of several MBeans.  Instances of this class are
  45  * immutable.</p>
  46  *
  47  * <p>An instance of this class can be used to represent:</p>
  48  * <ul>
  49  * <li>An object name</li>
  50  * <li>An object name pattern, within the context of a query</li>
  51  * </ul>
  52  *
  53  * <p>An object name consists of two parts, the domain and the key
  54  * properties.</p>
  55  *
  56  * <p>The <em>domain</em> is a string of characters not including
  57  * the character colon (<code>:</code>).  It is recommended that the domain
  58  * should not contain the string "{@code //}", which is reserved for future use.
  59  *
  60  * <p>If the domain includes at least one occurrence of the wildcard
  61  * characters asterisk (<code>*</code>) or question mark
  62  * (<code>?</code>), then the object name is a pattern.  The asterisk
  63  * matches any sequence of zero or more characters, while the question
  64  * mark matches any single character.</p>
  65  *
  66  * <p>If the domain is empty, it will be replaced in certain contexts
  67  * by the <em>default domain</em> of the MBean server in which the
  68  * ObjectName is used.</p>
  69  *
  70  * <p>The <em>key properties</em> are an unordered set of keys and
  71  * associated values.</p>
  72  *
  73  * <p>Each <em>key</em> is a nonempty string of characters which may
  74  * not contain any of the characters comma (<code>,</code>), equals
  75  * (<code>=</code>), colon, asterisk, or question mark.  The same key
  76  * may not occur twice in a given ObjectName.</p>
  77  *
  78  * <p>Each <em>value</em> associated with a key is a string of
  79  * characters that is either unquoted or quoted.</p>
  80  *
  81  * <p>An <em>unquoted value</em> is a possibly empty string of
  82  * characters which may not contain any of the characters comma,
  83  * equals, colon, or quote.</p>
  84  *
  85  * <p>If the <em>unquoted value</em> contains at least one occurrence
  86  * of the wildcard characters asterisk or question mark, then the object
  87  * name is a <em>property value pattern</em>. The asterisk matches any
  88  * sequence of zero or more characters, while the question mark matches
  89  * any single character.</p>
  90  *
  91  * <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
  92  * followed by a possibly empty string of characters, followed by
  93  * another quote.  Within the string of characters, the backslash
  94  * (<code>\</code>) has a special meaning.  It must be followed by
  95  * one of the following characters:</p>
  96  *
  97  * <ul>
  98  * <li>Another backslash.  The second backslash has no special
  99  * meaning and the two characters represent a single backslash.</li>
 100  *
 101  * <li>The character 'n'.  The two characters represent a newline
 102  * ('\n' in Java).</li>
 103  *
 104  * <li>A quote.  The two characters represent a quote, and that quote
 105  * is not considered to terminate the quoted value. An ending closing
 106  * quote must be present for the quoted value to be valid.</li>
 107  *
 108  * <li>A question mark (?) or asterisk (*).  The two characters represent
 109  * a question mark or asterisk respectively.</li>
 110  * </ul>
 111  *
 112  * <p>A quote may not appear inside a quoted value except immediately
 113  * after an odd number of consecutive backslashes.</p>
 114  *
 115  * <p>The quotes surrounding a quoted value, and any backslashes
 116  * within that value, are considered to be part of the value.</p>
 117  *
 118  * <p>If the <em>quoted value</em> contains at least one occurrence of
 119  * the characters asterisk or question mark and they are not preceded
 120  * by a backslash, then they are considered as wildcard characters and
 121  * the object name is a <em>property value pattern</em>. The asterisk
 122  * matches any sequence of zero or more characters, while the question
 123  * mark matches any single character.</p>
 124  *
 125  * <p>An ObjectName may be a <em>property list pattern</em>. In this
 126  * case it may have zero or more keys and associated values. It matches
 127  * a nonpattern ObjectName whose domain matches and that contains the
 128  * same keys and associated values, as well as possibly other keys and
 129  * values.</p>
 130  *
 131  * <p>An ObjectName is a <em>property value pattern</em> when at least
 132  * one of its <em>quoted</em> or <em>unquoted</em> key property values
 133  * contains the wildcard characters asterisk or question mark as described
 134  * above. In this case it has one or more keys and associated values, with
 135  * at least one of the values containing wildcard characters. It matches a
 136  * nonpattern ObjectName whose domain matches and that contains the same
 137  * keys whose values match; if the property value pattern is also a
 138  * property list pattern then the nonpattern ObjectName can contain
 139  * other keys and values.</p>
 140  *
 141  * <p>An ObjectName is a <em>property pattern</em> if it is either a
 142  * <em>property list pattern</em> or a <em>property value pattern</em>
 143  * or both.</p>
 144  *
 145  * <p>An ObjectName is a pattern if its domain contains a wildcard or
 146  * if the ObjectName is a property pattern.</p>
 147  *
 148  * <p>If an ObjectName is not a pattern, it must contain at least one
 149  * key with its associated value.</p>
 150  *
 151  * <p>Examples of ObjectName patterns are:</p>
 152  *
 153  * <ul>
 154  * <li>{@code *:type=Foo,name=Bar} to match names in any domain whose
 155  *     exact set of keys is {@code type=Foo,name=Bar}.</li>
 156  * <li>{@code d:type=Foo,name=Bar,*} to match names in the domain
 157  *     {@code d} that have the keys {@code type=Foo,name=Bar} plus
 158  *     zero or more other keys.</li>
 159  * <li>{@code *:type=Foo,name=Bar,*} to match names in any domain
 160  *     that has the keys {@code type=Foo,name=Bar} plus zero or
 161  *     more other keys.</li>
 162  * <li>{@code d:type=F?o,name=Bar} will match e.g.
 163  *     {@code d:type=Foo,name=Bar} and {@code d:type=Fro,name=Bar}.</li>
 164  * <li>{@code d:type=F*o,name=Bar} will match e.g.
 165  *     {@code d:type=Fo,name=Bar} and {@code d:type=Frodo,name=Bar}.</li>
 166  * <li>{@code d:type=Foo,name="B*"} will match e.g.
 167  *     {@code d:type=Foo,name="Bling"}. Wildcards are recognized even
 168  *     inside quotes, and like other special characters can be escaped
 169  *     with {@code \}.</li>
 170  * </ul>
 171  *
 172  * <p>An ObjectName can be written as a String with the following
 173  * elements in order:</p>
 174  *
 175  * <ul>
 176  * <li>The domain.
 177  * <li>A colon (<code>:</code>).
 178  * <li>A key property list as defined below.
 179  * </ul>
 180  *
 181  * <p>A key property list written as a String is a comma-separated
 182  * list of elements.  Each element is either an asterisk or a key
 183  * property.  A key property consists of a key, an equals
 184  * (<code>=</code>), and the associated value.</p>
 185  *
 186  * <p>At most one element of a key property list may be an asterisk.
 187  * If the key property list contains an asterisk element, the
 188  * ObjectName is a property list pattern.</p>
 189  *
 190  * <p>Spaces have no special significance in a String representing an
 191  * ObjectName.  For example, the String:
 192  * <pre>
 193  * domain: key1 = value1 , key2 = value2
 194  * </pre>
 195  * represents an ObjectName with two keys.  The name of each key
 196  * contains six characters, of which the first and last are spaces.
 197  * The value associated with the key <code>"&nbsp;key1&nbsp;"</code>
 198  * also begins and ends with a space.</p>
 199  *
 200  * <p>In addition to the restrictions on characters spelt out above,
 201  * no part of an ObjectName may contain a newline character
 202  * (<code>'\n'</code>), whether the domain, a key, or a value, whether
 203  * quoted or unquoted.  The newline character can be represented in a
 204  * quoted value with the sequence <code>\n</code>.
 205  *
 206  * <p>The rules on special characters and quoting apply regardless of
 207  * which constructor is used to make an ObjectName.</p>
 208  *
 209  * <p>To avoid collisions between MBeans supplied by different
 210  * vendors, a useful convention is to begin the domain name with the
 211  * reverse DNS name of the organization that specifies the MBeans,
 212  * followed by a period and a string whose interpretation is
 213  * determined by that organization.  For example, MBeans specified by
 214  * <code>example.com</code>  would have
 215  * domains such as <code>com.example.MyDomain</code>.  This is essentially
 216  * the same convention as for Java-language package names.</p>
 217  *
 218  * <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
 219  *
 220  * @since 1.5
 221  */
 222 @SuppressWarnings("serial") // don't complain serialVersionUID not constant
 223 public class ObjectName implements Comparable<ObjectName>, QueryExp {
 224 
 225     /**
 226      * A structure recording property structure and
 227      * proposing minimal services
 228      */
 229     private static class Property {
 230 
 231         int _key_index;
 232         int _key_length;
 233         int _value_length;
 234 
 235         /**
 236          * Constructor.
 237          */
 238         Property(int key_index, int key_length, int value_length) {
 239             _key_index = key_index;
 240             _key_length = key_length;
 241             _value_length = value_length;
 242         }
 243 
 244         /**
 245          * Assigns the key index of property
 246          */
 247         void setKeyIndex(int key_index) {
 248             _key_index = key_index;
 249         }
 250 
 251         /**
 252          * Returns a key string for receiver key
 253          */
 254         String getKeyString(String name) {
 255             return name.substring(_key_index, _key_index + _key_length);
 256         }
 257 
 258         /**
 259          * Returns a value string for receiver key
 260          */
 261         String getValueString(String name) {
 262             int in_begin = _key_index + _key_length + 1;
 263             int out_end = in_begin + _value_length;
 264             return name.substring(in_begin, out_end);
 265         }
 266     }
 267 
 268     /**
 269      * Marker class for value pattern property.
 270      */
 271     private static class PatternProperty extends Property {
 272         /**
 273          * Constructor.
 274          */
 275         PatternProperty(int key_index, int key_length, int value_length) {
 276             super(key_index, key_length, value_length);
 277         }
 278     }
 279 
 280     // Inner classes <========================================
 281 
 282 
 283 
 284     // Private fields ---------------------------------------->
 285 
 286 
 287     // Serialization compatibility stuff -------------------->
 288 
 289     // Two serial forms are supported in this class. The selected form depends
 290     // on system property "jmx.serial.form":
 291     //  - "1.0" for JMX 1.0
 292     //  - any other value for JMX 1.1 and higher
 293     //
 294     // Serial version for old serial form
 295     private static final long oldSerialVersionUID = -5467795090068647408L;
 296     //
 297     // Serial version for new serial form
 298     private static final long newSerialVersionUID = 1081892073854801359L;
 299     //
 300     // Serializable fields in old serial form
 301     private static final ObjectStreamField[] oldSerialPersistentFields =
 302     {
 303         new ObjectStreamField("domain", String.class),
 304         new ObjectStreamField("propertyList", Hashtable.class),
 305         new ObjectStreamField("propertyListString", String.class),
 306         new ObjectStreamField("canonicalName", String.class),
 307         new ObjectStreamField("pattern", Boolean.TYPE),
 308         new ObjectStreamField("propertyPattern", Boolean.TYPE)
 309     };
 310     //
 311     // Serializable fields in new serial form
 312     private static final ObjectStreamField[] newSerialPersistentFields = { };
 313     //
 314     // Actual serial version and serial form
 315     private static final long serialVersionUID;
 316     private static final ObjectStreamField[] serialPersistentFields;
 317     private static boolean compat = false;
 318     static {
 319         try {
 320             GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
 321             String form = AccessController.doPrivileged(act);
 322             compat = (form != null && form.equals("1.0"));
 323         } catch (Exception e) {
 324             // OK: exception means no compat with 1.0, too bad
 325         }
 326         if (compat) {
 327             serialPersistentFields = oldSerialPersistentFields;
 328             serialVersionUID = oldSerialVersionUID;
 329         } else {
 330             serialPersistentFields = newSerialPersistentFields;
 331             serialVersionUID = newSerialVersionUID;
 332         }
 333     }
 334 
 335     //
 336     // Serialization compatibility stuff <==============================
 337 
 338     // Class private fields ----------------------------------->
 339 
 340     /**
 341      * a shared empty array for empty property lists
 342      */
 343     static final private Property[] _Empty_property_array = new Property[0];
 344 
 345 
 346     // Class private fields <==============================
 347 
 348     // Instance private fields ----------------------------------->
 349 
 350     /**
 351      * a String containing the canonical name
 352      */
 353     private transient String _canonicalName;
 354 
 355 
 356     /**
 357      * An array of properties in the same seq order as time creation
 358      */
 359     private transient Property[] _kp_array;
 360 
 361     /**
 362      * An array of properties in the same seq order as canonical order
 363      */
 364     private transient Property[] _ca_array;
 365 
 366 
 367     /**
 368      * The length of the domain part of built objectname
 369      */
 370     private transient int _domain_length = 0;
 371 
 372 
 373     /**
 374      * The propertyList of built object name. Initialized lazily.
 375      * Table that contains all the pairs (key,value) for this ObjectName.
 376      */
 377     private transient Map<String,String> _propertyList;
 378 
 379     /**
 380      * boolean that declares if this ObjectName domain part is a pattern
 381      */
 382     private transient boolean _domain_pattern = false;
 383 
 384     /**
 385      * boolean that declares if this ObjectName contains a pattern on the
 386      * key property list
 387      */
 388     private transient boolean _property_list_pattern = false;
 389 
 390     /**
 391      * boolean that declares if this ObjectName contains a pattern on the
 392      * value of at least one key property
 393      */
 394     private transient boolean _property_value_pattern = false;
 395 
 396     // Instance private fields <=======================================
 397 
 398     // Private fields <========================================
 399 
 400 
 401     //  Private methods ---------------------------------------->
 402 
 403     // Category : Instance construction ------------------------->
 404 
 405     /**
 406      * Initializes this {@link ObjectName} from the given string
 407      * representation.
 408      *
 409      * @param name A string representation of the {@link ObjectName}
 410      *
 411      * @exception MalformedObjectNameException The string passed as a
 412      * parameter does not have the right format.
 413      * @exception NullPointerException The <code>name</code> parameter
 414      * is null.
 415      */
 416     private void construct(String name)
 417         throws MalformedObjectNameException {
 418 
 419         // The name cannot be null
 420         if (name == null)
 421             throw new NullPointerException("name cannot be null");
 422 
 423         // Test if the name is empty
 424         if (name.length() == 0) {
 425             // this is equivalent to the whole word query object name.
 426             _canonicalName = "*:*";
 427             _kp_array = _Empty_property_array;
 428             _ca_array = _Empty_property_array;
 429             _domain_length = 1;
 430             _propertyList = null;
 431             _domain_pattern = true;
 432             _property_list_pattern = true;
 433             _property_value_pattern = false;
 434             return;
 435         }
 436 
 437         // initialize parsing of the string
 438         final char[] name_chars = name.toCharArray();
 439         final int len = name_chars.length;
 440         final char[] canonical_chars = new char[len]; // canonical form will
 441                                                       // be same length at most
 442         int cname_index = 0;
 443         int index = 0;
 444         char c, c1;
 445 
 446         // parses domain part
 447     domain_parsing:
 448         while (index < len) {
 449             switch (name_chars[index]) {
 450                 case ':' :
 451                     _domain_length = index++;
 452                     break domain_parsing;
 453                 case '=' :
 454                     // ":" omission check.
 455                     //
 456                     // Although "=" is a valid character in the domain part
 457                     // it is true that it is rarely used in the real world.
 458                     // So check straight away if the ":" has been omitted
 459                     // from the ObjectName. This allows us to provide a more
 460                     // accurate exception message.
 461                     int i = ++index;
 462                     while ((i < len) && (name_chars[i++] != ':'))
 463                         if (i == len)
 464                             throw new MalformedObjectNameException(
 465                                 "Domain part must be specified");
 466                     break;
 467                 case '\n' :
 468                     throw new MalformedObjectNameException(
 469                               "Invalid character '\\n' in domain name");
 470                 case '*' :
 471                 case '?' :
 472                     _domain_pattern = true;
 473                     index++;
 474                     break;
 475                 default :
 476                     index++;
 477                     break;
 478             }
 479         }
 480 
 481         // check for non-empty properties
 482         if (index == len)
 483             throw new MalformedObjectNameException(
 484                                          "Key properties cannot be empty");
 485 
 486         // we have got the domain part, begins building of _canonicalName
 487         System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length);
 488         canonical_chars[_domain_length] = ':';
 489         cname_index = _domain_length + 1;
 490 
 491         // parses property list
 492         Property prop;
 493         Map<String,Property> keys_map = new HashMap<String,Property>();
 494         String[] keys;
 495         String key_name;
 496         boolean quoted_value;
 497         int property_index = 0;
 498         int in_index;
 499         int key_index, key_length, value_index, value_length;
 500 
 501         keys = new String[10];
 502         _kp_array = new Property[10];
 503         _property_list_pattern = false;
 504         _property_value_pattern = false;
 505 
 506         while (index < len) {
 507             c = name_chars[index];
 508 
 509             // case of pattern properties
 510             if (c == '*') {
 511                 if (_property_list_pattern)
 512                     throw new MalformedObjectNameException(
 513                               "Cannot have several '*' characters in pattern " +
 514                               "property list");
 515                 else {
 516                     _property_list_pattern = true;
 517                     if ((++index < len ) && (name_chars[index] != ','))
 518                         throw new MalformedObjectNameException(
 519                                   "Invalid character found after '*': end of " +
 520                                   "name or ',' expected");
 521                     else if (index == len) {
 522                         if (property_index == 0) {
 523                             // empty properties case
 524                             _kp_array = _Empty_property_array;
 525                             _ca_array = _Empty_property_array;
 526                             _propertyList = Collections.emptyMap();
 527                         }
 528                         break;
 529                     } else {
 530                         // correct pattern spec in props, continue
 531                         index++;
 532                         continue;
 533                     }
 534                 }
 535             }
 536 
 537             // standard property case, key part
 538             in_index = index;
 539             key_index = in_index;
 540             if (name_chars[in_index] == '=')
 541                 throw new MalformedObjectNameException("Invalid key (empty)");
 542             while ((in_index < len) && ((c1 = name_chars[in_index++]) != '='))
 543                 switch (c1) {
 544                     // '=' considered to introduce value part
 545                     case  '*' :
 546                     case  '?' :
 547                     case  ',' :
 548                     case  ':' :
 549                     case  '\n' :
 550                         final String ichar = ((c1=='\n')?"\\n":""+c1);
 551                         throw new MalformedObjectNameException(
 552                                   "Invalid character '" + ichar +
 553                                   "' in key part of property");
 554                 }
 555             if (name_chars[in_index - 1] != '=')
 556                 throw new MalformedObjectNameException(
 557                                              "Unterminated key property part");
 558             value_index = in_index; // in_index pointing after '=' char
 559             key_length = value_index - key_index - 1; // found end of key
 560 
 561             // standard property case, value part
 562             boolean value_pattern = false;
 563             if (in_index < len && name_chars[in_index] == '\"') {
 564                 quoted_value = true;
 565                 // the case of quoted value part
 566             quoted_value_parsing:
 567                 while ((++in_index < len) &&
 568                        ((c1 = name_chars[in_index]) != '\"')) {
 569                     // the case of an escaped character
 570                     if (c1 == '\\') {
 571                         if (++in_index == len)
 572                             throw new MalformedObjectNameException(
 573                                                "Unterminated quoted value");
 574                         switch (c1 = name_chars[in_index]) {
 575                             case '\\' :
 576                             case '\"' :
 577                             case '?' :
 578                             case '*' :
 579                             case 'n' :
 580                                 break; // valid character
 581                             default :
 582                                 throw new MalformedObjectNameException(
 583                                           "Invalid escape sequence '\\" +
 584                                           c1 + "' in quoted value");
 585                         }
 586                     } else if (c1 == '\n') {
 587                         throw new MalformedObjectNameException(
 588                                                      "Newline in quoted value");
 589                     } else {
 590                         switch (c1) {
 591                             case '?' :
 592                             case '*' :
 593                                 value_pattern = true;
 594                                 break;
 595                         }
 596                     }
 597                 }
 598                 if (in_index == len)
 599                     throw new MalformedObjectNameException(
 600                                                  "Unterminated quoted value");
 601                 else value_length = ++in_index - value_index;
 602             } else {
 603                 // the case of standard value part
 604                 quoted_value = false;
 605                 while ((in_index < len) && ((c1 = name_chars[in_index]) != ','))
 606                 switch (c1) {
 607                     // ',' considered to be the value separator
 608                     case '*' :
 609                     case '?' :
 610                         value_pattern = true;
 611                         in_index++;
 612                         break;
 613                     case '=' :
 614                     case ':' :
 615                     case '"' :
 616                     case '\n' :
 617                         final String ichar = ((c1=='\n')?"\\n":""+c1);
 618                         throw new MalformedObjectNameException(
 619                                                  "Invalid character '" + ichar +
 620                                                  "' in value part of property");
 621                     default :
 622                         in_index++;
 623                         break;
 624                 }
 625                 value_length = in_index - value_index;
 626             }
 627 
 628             // Parsed property, checks the end of name
 629             if (in_index == len - 1) {
 630                 if (quoted_value)
 631                     throw new MalformedObjectNameException(
 632                                              "Invalid ending character `" +
 633                                              name_chars[in_index] + "'");
 634                 else throw new MalformedObjectNameException(
 635                                                   "Invalid ending comma");
 636             } else in_index++;
 637 
 638             // we got the key and value part, prepare a property for this
 639             if (!value_pattern) {
 640                 prop = new Property(key_index, key_length, value_length);
 641             } else {
 642                 _property_value_pattern = true;
 643                 prop = new PatternProperty(key_index, key_length, value_length);
 644             }
 645             key_name = name.substring(key_index, key_index + key_length);
 646 
 647             if (property_index == keys.length) {
 648                 String[] tmp_string_array = new String[property_index + 10];
 649                 System.arraycopy(keys, 0, tmp_string_array, 0, property_index);
 650                 keys = tmp_string_array;
 651             }
 652             keys[property_index] = key_name;
 653 
 654             addProperty(prop, property_index, keys_map, key_name);
 655             property_index++;
 656             index = in_index;
 657         }
 658 
 659         // computes and set canonical name
 660         setCanonicalName(name_chars, canonical_chars, keys,
 661                          keys_map, cname_index, property_index);
 662     }
 663 
 664     /**
 665      * Construct an ObjectName from a domain and a Hashtable.
 666      *
 667      * @param domain Domain of the ObjectName.
 668      * @param props  Map containing couples <i>key</i> -> <i>value</i>.
 669      *
 670      * @exception MalformedObjectNameException The <code>domain</code>
 671      * contains an illegal character, or one of the keys or values in
 672      * <code>table</code> contains an illegal character, or one of the
 673      * values in <code>table</code> does not follow the rules for quoting.
 674      * @exception NullPointerException One of the parameters is null.
 675      */
 676     private void construct(String domain, Map<String,String> props)
 677         throws MalformedObjectNameException {
 678 
 679         // The domain cannot be null
 680         if (domain == null)
 681             throw new NullPointerException("domain cannot be null");
 682 
 683         // The key property list cannot be null
 684         if (props == null)
 685             throw new NullPointerException("key property list cannot be null");
 686 
 687         // The key property list cannot be empty
 688         if (props.isEmpty())
 689             throw new MalformedObjectNameException(
 690                                          "key property list cannot be empty");
 691 
 692         // checks domain validity
 693         if (!isDomain(domain))
 694             throw new MalformedObjectNameException("Invalid domain: " + domain);
 695 
 696         // init canonicalname
 697         final StringBuilder sb = new StringBuilder();
 698         sb.append(domain).append(':');
 699         _domain_length = domain.length();
 700 
 701         // allocates the property array
 702         int nb_props = props.size();
 703         _kp_array = new Property[nb_props];
 704 
 705         String[] keys = new String[nb_props];
 706         final Map<String,Property> keys_map = new HashMap<String,Property>();
 707         Property prop;
 708         int key_index;
 709         int i = 0;
 710         for (Map.Entry<String,String> entry : props.entrySet()) {
 711             if (sb.length() > 0)
 712                 sb.append(",");
 713             String key = entry.getKey();
 714             String value;
 715             try {
 716                 value = entry.getValue();
 717             } catch (ClassCastException e) {
 718                 throw new MalformedObjectNameException(e.getMessage());
 719             }
 720             key_index = sb.length();
 721             checkKey(key);
 722             sb.append(key);
 723             keys[i] = key;
 724             sb.append("=");
 725             boolean value_pattern = checkValue(value);
 726             sb.append(value);
 727             if (!value_pattern) {
 728                 prop = new Property(key_index,
 729                                     key.length(),
 730                                     value.length());
 731             } else {
 732                 _property_value_pattern = true;
 733                 prop = new PatternProperty(key_index,
 734                                            key.length(),
 735                                            value.length());
 736             }
 737             addProperty(prop, i, keys_map, key);
 738             i++;
 739         }
 740 
 741         // initialize canonical name and data structure
 742         int len = sb.length();
 743         char[] initial_chars = new char[len];
 744         sb.getChars(0, len, initial_chars, 0);
 745         char[] canonical_chars = new char[len];
 746         System.arraycopy(initial_chars, 0, canonical_chars, 0,
 747                          _domain_length + 1);
 748         setCanonicalName(initial_chars, canonical_chars, keys, keys_map,
 749                          _domain_length + 1, _kp_array.length);
 750     }
 751     // Category : Instance construction <==============================
 752 
 753     // Category : Internal utilities ------------------------------>
 754 
 755     /**
 756      * Add passed property to the list at the given index
 757      * for the passed key name
 758      */
 759     private void addProperty(Property prop, int index,
 760                              Map<String,Property> keys_map, String key_name)
 761         throws MalformedObjectNameException {
 762 
 763         if (keys_map.containsKey(key_name)) throw new
 764                 MalformedObjectNameException("key `" +
 765                                          key_name +"' already defined");
 766 
 767         // if no more space for property arrays, have to increase it
 768         if (index == _kp_array.length) {
 769             Property[] tmp_prop_array = new Property[index + 10];
 770             System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
 771             _kp_array = tmp_prop_array;
 772         }
 773         _kp_array[index] = prop;
 774         keys_map.put(key_name, prop);
 775     }
 776 
 777     /**
 778      * Sets the canonical name of receiver from input 'specified_chars'
 779      * array, by filling 'canonical_chars' array with found 'nb-props'
 780      * properties starting at position 'prop_index'.
 781      */
 782     private void setCanonicalName(char[] specified_chars,
 783                                   char[] canonical_chars,
 784                                   String[] keys, Map<String,Property> keys_map,
 785                                   int prop_index, int nb_props) {
 786 
 787         // Sort the list of found properties
 788         if (_kp_array != _Empty_property_array) {
 789             String[] tmp_keys = new String[nb_props];
 790             Property[] tmp_props = new Property[nb_props];
 791 
 792             System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
 793             Arrays.sort(tmp_keys);
 794             keys = tmp_keys;
 795             System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props);
 796             _kp_array = tmp_props;
 797             _ca_array = new Property[nb_props];
 798 
 799             // now assigns _ca_array to the sorted list of keys
 800             // (there cannot be two identical keys in an objectname.
 801             for (int i = 0; i < nb_props; i++)
 802                 _ca_array[i] = keys_map.get(keys[i]);
 803 
 804             // now we build the canonical name and set begin indexes of
 805             // properties to reflect canonical form
 806             int last_index = nb_props - 1;
 807             int prop_len;
 808             Property prop;
 809             for (int i = 0; i <= last_index; i++) {
 810                 prop = _ca_array[i];
 811                 // length of prop including '=' char
 812                 prop_len = prop._key_length + prop._value_length + 1;
 813                 System.arraycopy(specified_chars, prop._key_index,
 814                                  canonical_chars, prop_index, prop_len);
 815                 prop.setKeyIndex(prop_index);
 816                 prop_index += prop_len;
 817                 if (i != last_index) {
 818                     canonical_chars[prop_index] = ',';
 819                     prop_index++;
 820                 }
 821             }
 822         }
 823 
 824         // terminate canonicalname with '*' in case of pattern
 825         if (_property_list_pattern) {
 826             if (_kp_array != _Empty_property_array)
 827                 canonical_chars[prop_index++] = ',';
 828             canonical_chars[prop_index++] = '*';
 829         }
 830 
 831         // we now build the canonicalname string
 832         _canonicalName = (new String(canonical_chars, 0, prop_index));
 833     }
 834 
 835     /**
 836      * Parse a key.
 837      * <pre>final int endKey=parseKey(s,startKey);</pre>
 838      * <p>key starts at startKey (included), and ends at endKey (excluded).
 839      * If (startKey == endKey), then the key is empty.
 840      *
 841      * @param s The char array of the original string.
 842      * @param startKey index at which to begin parsing.
 843      * @return The index following the last character of the key.
 844      **/
 845     private static int parseKey(final char[] s, final int startKey)
 846         throws MalformedObjectNameException {
 847         int next   = startKey;
 848         int endKey = startKey;
 849         final int len = s.length;
 850         while (next < len) {
 851             final char k = s[next++];
 852             switch (k) {
 853             case '*':
 854             case '?':
 855             case ',':
 856             case ':':
 857             case '\n':
 858                 final String ichar = ((k=='\n')?"\\n":""+k);
 859                 throw new
 860                     MalformedObjectNameException("Invalid character in key: `"
 861                                                  + ichar + "'");
 862             case '=':
 863                 // we got the key.
 864                 endKey = next-1;
 865                 break;
 866             default:
 867                 if (next < len) continue;
 868                 else endKey=next;
 869             }
 870             break;
 871         }
 872         return endKey;
 873     }
 874 
 875     /**
 876      * Parse a value.
 877      * <pre>final int endVal=parseValue(s,startVal);</pre>
 878      * <p>value starts at startVal (included), and ends at endVal (excluded).
 879      * If (startVal == endVal), then the key is empty.
 880      *
 881      * @param s The char array of the original string.
 882      * @param startValue index at which to begin parsing.
 883      * @return The first element of the int array indicates the index
 884      *         following the last character of the value. The second
 885      *         element of the int array indicates that the value is
 886      *         a pattern when its value equals 1.
 887      **/
 888     private static int[] parseValue(final char[] s, final int startValue)
 889         throws MalformedObjectNameException {
 890 
 891         boolean value_pattern = false;
 892 
 893         int next   = startValue;
 894         int endValue = startValue;
 895 
 896         final int len = s.length;
 897         final char q=s[startValue];
 898 
 899         if (q == '"') {
 900             // quoted value
 901             if (++next == len) throw new
 902                 MalformedObjectNameException("Invalid quote");
 903             while (next < len) {
 904                 char last = s[next];
 905                 if (last == '\\') {
 906                     if (++next == len) throw new
 907                         MalformedObjectNameException(
 908                            "Invalid unterminated quoted character sequence");
 909                     last = s[next];
 910                     switch (last) {
 911                         case '\\' :
 912                         case '?' :
 913                         case '*' :
 914                         case 'n' :
 915                             break;
 916                         case '\"' :
 917                             // We have an escaped quote. If this escaped
 918                             // quote is the last character, it does not
 919                             // qualify as a valid termination quote.
 920                             //
 921                             if (next+1 == len) throw new
 922                                 MalformedObjectNameException(
 923                                                  "Missing termination quote");
 924                             break;
 925                         default:
 926                             throw new
 927                                 MalformedObjectNameException(
 928                                 "Invalid quoted character sequence '\\" +
 929                                 last + "'");
 930                     }
 931                 } else if (last == '\n') {
 932                     throw new MalformedObjectNameException(
 933                                                  "Newline in quoted value");
 934                 } else if (last == '\"') {
 935                     next++;
 936                     break;
 937                 } else {
 938                     switch (last) {
 939                         case '?' :
 940                         case '*' :
 941                             value_pattern = true;
 942                             break;
 943                     }
 944                 }
 945                 next++;
 946 
 947                 // Check that last character is a termination quote.
 948                 // We have already handled the case were the last
 949                 // character is an escaped quote earlier.
 950                 //
 951                 if ((next >= len) && (last != '\"')) throw new
 952                     MalformedObjectNameException("Missing termination quote");
 953             }
 954             endValue = next;
 955             if (next < len) {
 956                 if (s[next++] != ',') throw new
 957                     MalformedObjectNameException("Invalid quote");
 958             }
 959         } else {
 960             // Non quoted value.
 961             while (next < len) {
 962                 final char v=s[next++];
 963                 switch(v) {
 964                     case '*':
 965                     case '?':
 966                         value_pattern = true;
 967                         if (next < len) continue;
 968                         else endValue=next;
 969                         break;
 970                     case '=':
 971                     case ':':
 972                     case '\n' :
 973                         final String ichar = ((v=='\n')?"\\n":""+v);
 974                         throw new
 975                             MalformedObjectNameException("Invalid character `" +
 976                                                          ichar + "' in value");
 977                     case ',':
 978                         endValue = next-1;
 979                         break;
 980                     default:
 981                         if (next < len) continue;
 982                         else endValue=next;
 983                 }
 984                 break;
 985             }
 986         }
 987         return new int[] { endValue, value_pattern ? 1 : 0 };
 988     }
 989 
 990     /**
 991      * Check if the supplied value is a valid value.
 992      *
 993      * @return true if the value is a pattern, otherwise false.
 994      */
 995     private static boolean checkValue(String val)
 996         throws MalformedObjectNameException {
 997 
 998         if (val == null) throw new
 999             NullPointerException("Invalid value (null)");
1000 
1001         final int len = val.length();
1002         if (len == 0)
1003             return false;
1004 
1005         final char[] s = val.toCharArray();
1006         final int[] result = parseValue(s,0);
1007         final int endValue = result[0];
1008         final boolean value_pattern = result[1] == 1;
1009         if (endValue < len) throw new
1010             MalformedObjectNameException("Invalid character in value: `" +
1011                                          s[endValue] + "'");
1012         return value_pattern;
1013     }
1014 
1015     /**
1016      * Check if the supplied key is a valid key.
1017      */
1018     private static void checkKey(String key)
1019         throws MalformedObjectNameException {
1020 
1021         if (key == null) throw new
1022             NullPointerException("Invalid key (null)");
1023 
1024         final int len = key.length();
1025         if (len == 0) throw new
1026             MalformedObjectNameException("Invalid key (empty)");
1027         final char[] k=key.toCharArray();
1028         final int endKey = parseKey(k,0);
1029         if (endKey < len) throw new
1030             MalformedObjectNameException("Invalid character in value: `" +
1031                                          k[endKey] + "'");
1032     }
1033 
1034 
1035     // Category : Internal utilities <==============================
1036 
1037     // Category : Internal accessors ------------------------------>
1038 
1039     /**
1040      * Check if domain is a valid domain.  Set _domain_pattern if appropriate.
1041      */
1042     private boolean isDomain(String domain) {
1043         if (domain == null) return true;
1044         final int len = domain.length();
1045         int next = 0;
1046         while (next < len) {
1047             final char c = domain.charAt(next++);
1048             switch (c) {
1049                 case ':' :
1050                 case '\n' :
1051                     return false;
1052                 case '*' :
1053                 case '?' :
1054                     _domain_pattern = true;
1055                     break;
1056             }
1057         }
1058         return true;
1059     }
1060 
1061     // Category : Internal accessors <==============================
1062 
1063     // Category : Serialization ----------------------------------->
1064 
1065     /**
1066      * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
1067      * @serialData <ul>
1068      *               <li>In the current serial form (value of property
1069      *                   <code>jmx.serial.form</code> differs from
1070      *                   <code>1.0</code>): the string
1071      *                   &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1072      *                   where: <ul>
1073      *                            <li>&lt;domain&gt; represents the domain part
1074      *                                of the {@link ObjectName}</li>
1075      *                            <li>&lt;properties&gt; represents the list of
1076      *                                properties, as returned by
1077      *                                {@link #getKeyPropertyListString}
1078      *                            <li>&lt;wild&gt; is empty if not
1079      *                                <code>isPropertyPattern</code>, or
1080      *                                is the character "<code>*</code>" if
1081      *                                <code>isPropertyPattern</code>
1082      *                                and &lt;properties&gt; is empty, or
1083      *                                is "<code>,*</code>" if
1084      *                                <code>isPropertyPattern</code> and
1085      *                                &lt;properties&gt; is not empty.
1086      *                            </li>
1087      *                          </ul>
1088      *                   The intent is that this string could be supplied
1089      *                   to the {@link #ObjectName(String)} constructor to
1090      *                   produce an equivalent {@link ObjectName}.
1091      *               </li>
1092      *               <li>In the old serial form (value of property
1093      *                   <code>jmx.serial.form</code> is
1094      *                   <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1095      *                   &lt;propertyListString&gt; &lt;canonicalName&gt;
1096      *                   &lt;pattern&gt; &lt;propertyPattern&gt;,
1097      *                   where: <ul>
1098      *                            <li>&lt;domain&gt; represents the domain part
1099      *                                of the {@link ObjectName}</li>
1100      *                            <li>&lt;propertyList&gt; is the
1101      *                                {@link Hashtable} that contains all the
1102      *                                pairs (key,value) for this
1103      *                                {@link ObjectName}</li>
1104      *                            <li>&lt;propertyListString&gt; is the
1105      *                                {@link String} representation of the
1106      *                                list of properties in any order (not
1107      *                                mandatorily a canonical representation)
1108      *                                </li>
1109      *                            <li>&lt;canonicalName&gt; is the
1110      *                                {@link String} containing this
1111      *                                {@link ObjectName}'s canonical name</li>
1112      *                            <li>&lt;pattern&gt; is a boolean which is
1113      *                                <code>true</code> if this
1114      *                                {@link ObjectName} contains a pattern</li>
1115      *                            <li>&lt;propertyPattern&gt; is a boolean which
1116      *                                is <code>true</code> if this
1117      *                                {@link ObjectName} contains a pattern in
1118      *                                the list of properties</li>
1119      *                          </ul>
1120      *               </li>
1121      *             </ul>
1122      */
1123     private void readObject(ObjectInputStream in)
1124         throws IOException, ClassNotFoundException {
1125 
1126         String cn;
1127         if (compat) {
1128             // Read an object serialized in the old serial form
1129             //
1130             //in.defaultReadObject();
1131             final ObjectInputStream.GetField fields = in.readFields();
1132             String propListString =
1133                     (String)fields.get("propertyListString", "");
1134 
1135             // 6616825: take care of property patterns
1136             final boolean propPattern =
1137                     fields.get("propertyPattern" , false);
1138             if (propPattern) {
1139                 propListString =
1140                         (propListString.length()==0?"*":(propListString+",*"));
1141             }
1142 
1143             cn = (String)fields.get("domain", "default")+
1144                 ":"+ propListString;
1145         } else {
1146             // Read an object serialized in the new serial form
1147             //
1148             in.defaultReadObject();
1149             cn = (String)in.readObject();
1150         }
1151 
1152         try {
1153             construct(cn);
1154         } catch (NullPointerException e) {
1155             throw new InvalidObjectException(e.toString());
1156         } catch (MalformedObjectNameException e) {
1157             throw new InvalidObjectException(e.toString());
1158         }
1159     }
1160 
1161 
1162     /**
1163      * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
1164      * @serialData <ul>
1165      *               <li>In the current serial form (value of property
1166      *                   <code>jmx.serial.form</code> differs from
1167      *                   <code>1.0</code>): the string
1168      *                   &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1169      *                   where: <ul>
1170      *                            <li>&lt;domain&gt; represents the domain part
1171      *                                of the {@link ObjectName}</li>
1172      *                            <li>&lt;properties&gt; represents the list of
1173      *                                properties, as returned by
1174      *                                {@link #getKeyPropertyListString}
1175      *                            <li>&lt;wild&gt; is empty if not
1176      *                                <code>isPropertyPattern</code>, or
1177      *                                is the character "<code>*</code>" if
1178      *                                this <code>isPropertyPattern</code>
1179      *                                and &lt;properties&gt; is empty, or
1180      *                                is "<code>,*</code>" if
1181      *                                <code>isPropertyPattern</code> and
1182      *                                &lt;properties&gt; is not empty.
1183      *                            </li>
1184      *                          </ul>
1185      *                   The intent is that this string could be supplied
1186      *                   to the {@link #ObjectName(String)} constructor to
1187      *                   produce an equivalent {@link ObjectName}.
1188      *               </li>
1189      *               <li>In the old serial form (value of property
1190      *                   <code>jmx.serial.form</code> is
1191      *                   <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1192      *                   &lt;propertyListString&gt; &lt;canonicalName&gt;
1193      *                   &lt;pattern&gt; &lt;propertyPattern&gt;,
1194      *                   where: <ul>
1195      *                            <li>&lt;domain&gt; represents the domain part
1196      *                                of the {@link ObjectName}</li>
1197      *                            <li>&lt;propertyList&gt; is the
1198      *                                {@link Hashtable} that contains all the
1199      *                                pairs (key,value) for this
1200      *                                {@link ObjectName}</li>
1201      *                            <li>&lt;propertyListString&gt; is the
1202      *                                {@link String} representation of the
1203      *                                list of properties in any order (not
1204      *                                mandatorily a canonical representation)
1205      *                                </li>
1206      *                            <li>&lt;canonicalName&gt; is the
1207      *                                {@link String} containing this
1208      *                                {@link ObjectName}'s canonical name</li>
1209      *                            <li>&lt;pattern&gt; is a boolean which is
1210      *                                <code>true</code> if this
1211      *                                {@link ObjectName} contains a pattern</li>
1212      *                            <li>&lt;propertyPattern&gt; is a boolean which
1213      *                                is <code>true</code> if this
1214      *                                {@link ObjectName} contains a pattern in
1215      *                                the list of properties</li>
1216      *                          </ul>
1217      *               </li>
1218      *             </ul>
1219      */
1220     private void writeObject(ObjectOutputStream out)
1221             throws IOException {
1222 
1223       if (compat)
1224       {
1225         // Serializes this instance in the old serial form
1226         // Read CR 6441274 before making any changes to this code
1227         ObjectOutputStream.PutField fields = out.putFields();
1228         fields.put("domain", _canonicalName.substring(0, _domain_length));
1229         fields.put("propertyList", getKeyPropertyList());
1230         fields.put("propertyListString", getKeyPropertyListString());
1231         fields.put("canonicalName", _canonicalName);
1232         fields.put("pattern", (_domain_pattern || _property_list_pattern));
1233         fields.put("propertyPattern", _property_list_pattern);
1234         out.writeFields();
1235       }
1236       else
1237       {
1238         // Serializes this instance in the new serial form
1239         //
1240         out.defaultWriteObject();
1241         out.writeObject(getSerializedNameString());
1242       }
1243     }
1244 
1245     //  Category : Serialization <===================================
1246 
1247     // Private methods <========================================
1248 
1249     // Public methods ---------------------------------------->
1250 
1251     // Category : ObjectName Construction ------------------------------>
1252 
1253     /**
1254      * <p>Return an instance of ObjectName that can be used anywhere
1255      * an object obtained with {@link #ObjectName(String) new
1256      * ObjectName(name)} can be used.  The returned object may be of
1257      * a subclass of ObjectName.  Calling this method twice with the
1258      * same parameters may return the same object or two equal but
1259      * not identical objects.</p>
1260      *
1261      * @param name  A string representation of the object name.
1262      *
1263      * @return an ObjectName corresponding to the given String.
1264      *
1265      * @exception MalformedObjectNameException The string passed as a
1266      * parameter does not have the right format.
1267      * @exception NullPointerException The <code>name</code> parameter
1268      * is null.
1269      *
1270      */
1271     public static ObjectName getInstance(String name)
1272             throws MalformedObjectNameException, NullPointerException {
1273         return new ObjectName(name);
1274     }
1275 
1276     /**
1277      * <p>Return an instance of ObjectName that can be used anywhere
1278      * an object obtained with {@link #ObjectName(String, String,
1279      * String) new ObjectName(domain, key, value)} can be used.  The
1280      * returned object may be of a subclass of ObjectName.  Calling
1281      * this method twice with the same parameters may return the same
1282      * object or two equal but not identical objects.</p>
1283      *
1284      * @param domain  The domain part of the object name.
1285      * @param key  The attribute in the key property of the object name.
1286      * @param value The value in the key property of the object name.
1287      *
1288      * @return an ObjectName corresponding to the given domain,
1289      * key, and value.
1290      *
1291      * @exception MalformedObjectNameException The
1292      * <code>domain</code>, <code>key</code>, or <code>value</code>
1293      * contains an illegal character, or <code>value</code> does not
1294      * follow the rules for quoting.
1295      * @exception NullPointerException One of the parameters is null.
1296      *
1297      */
1298     public static ObjectName getInstance(String domain, String key,
1299                                          String value)
1300             throws MalformedObjectNameException {
1301         return new ObjectName(domain, key, value);
1302     }
1303 
1304     /**
1305      * <p>Return an instance of ObjectName that can be used anywhere
1306      * an object obtained with {@link #ObjectName(String, Hashtable)
1307      * new ObjectName(domain, table)} can be used.  The returned
1308      * object may be of a subclass of ObjectName.  Calling this method
1309      * twice with the same parameters may return the same object or
1310      * two equal but not identical objects.</p>
1311      *
1312      * @param domain  The domain part of the object name.
1313      * @param table A hash table containing one or more key
1314      * properties.  The key of each entry in the table is the key of a
1315      * key property in the object name.  The associated value in the
1316      * table is the associated value in the object name.
1317      *
1318      * @return an ObjectName corresponding to the given domain and
1319      * key mappings.
1320      *
1321      * @exception MalformedObjectNameException The <code>domain</code>
1322      * contains an illegal character, or one of the keys or values in
1323      * <code>table</code> contains an illegal character, or one of the
1324      * values in <code>table</code> does not follow the rules for
1325      * quoting.
1326      * @exception NullPointerException One of the parameters is null.
1327      *
1328      */
1329     public static ObjectName getInstance(String domain,
1330                                          Hashtable<String,String> table)
1331         throws MalformedObjectNameException {
1332         return new ObjectName(domain, table);
1333     }
1334 
1335     /**
1336      * <p>Return an instance of ObjectName that can be used anywhere
1337      * the given object can be used.  The returned object may be of a
1338      * subclass of ObjectName.  If <code>name</code> is of a subclass
1339      * of ObjectName, it is not guaranteed that the returned object
1340      * will be of the same class.</p>
1341      *
1342      * <p>The returned value may or may not be identical to
1343      * <code>name</code>.  Calling this method twice with the same
1344      * parameters may return the same object or two equal but not
1345      * identical objects.</p>
1346      *
1347      * <p>Since ObjectName is immutable, it is not usually useful to
1348      * make a copy of an ObjectName.  The principal use of this method
1349      * is to guard against a malicious caller who might pass an
1350      * instance of a subclass with surprising behavior to sensitive
1351      * code.  Such code can call this method to obtain an ObjectName
1352      * that is known not to have surprising behavior.</p>
1353      *
1354      * @param name an instance of the ObjectName class or of a subclass
1355      *
1356      * @return an instance of ObjectName or a subclass that is known to
1357      * have the same semantics.  If <code>name</code> respects the
1358      * semantics of ObjectName, then the returned object is equal
1359      * (though not necessarily identical) to <code>name</code>.
1360      *
1361      * @exception NullPointerException The <code>name</code> is null.
1362      *
1363      */
1364     public static ObjectName getInstance(ObjectName name) {
1365         if (name.getClass().equals(ObjectName.class))
1366             return name;
1367         return Util.newObjectName(name.getSerializedNameString());
1368     }
1369 
1370     /**
1371      * Construct an object name from the given string.
1372      *
1373      * @param name  A string representation of the object name.
1374      *
1375      * @exception MalformedObjectNameException The string passed as a
1376      * parameter does not have the right format.
1377      * @exception NullPointerException The <code>name</code> parameter
1378      * is null.
1379      */
1380     public ObjectName(String name)
1381         throws MalformedObjectNameException {
1382         construct(name);
1383     }
1384 
1385     /**
1386      * Construct an object name with exactly one key property.
1387      *
1388      * @param domain  The domain part of the object name.
1389      * @param key  The attribute in the key property of the object name.
1390      * @param value The value in the key property of the object name.
1391      *
1392      * @exception MalformedObjectNameException The
1393      * <code>domain</code>, <code>key</code>, or <code>value</code>
1394      * contains an illegal character, or <code>value</code> does not
1395      * follow the rules for quoting.
1396      * @exception NullPointerException One of the parameters is null.
1397      */
1398     public ObjectName(String domain, String key, String value)
1399         throws MalformedObjectNameException {
1400         // If key or value are null a NullPointerException
1401         // will be thrown by the put method in Hashtable.
1402         //
1403         Map<String,String> table = Collections.singletonMap(key, value);
1404         construct(domain, table);
1405     }
1406 
1407     /**
1408      * Construct an object name with several key properties from a Hashtable.
1409      *
1410      * @param domain  The domain part of the object name.
1411      * @param table A hash table containing one or more key
1412      * properties.  The key of each entry in the table is the key of a
1413      * key property in the object name.  The associated value in the
1414      * table is the associated value in the object name.
1415      *
1416      * @exception MalformedObjectNameException The <code>domain</code>
1417      * contains an illegal character, or one of the keys or values in
1418      * <code>table</code> contains an illegal character, or one of the
1419      * values in <code>table</code> does not follow the rules for
1420      * quoting.
1421      * @exception NullPointerException One of the parameters is null.
1422      */
1423     public ObjectName(String domain, Hashtable<String,String> table)
1424             throws MalformedObjectNameException {
1425         construct(domain, table);
1426         /* The exception for when a key or value in the table is not a
1427            String is now ClassCastException rather than
1428            MalformedObjectNameException.  This was not previously
1429            specified.  */
1430     }
1431 
1432     // Category : ObjectName Construction <==============================
1433 
1434 
1435     // Category : Getter methods ------------------------------>
1436 
1437     /**
1438      * Checks whether the object name is a pattern.
1439      * <p>
1440      * An object name is a pattern if its domain contains a
1441      * wildcard or if the object name is a property pattern.
1442      *
1443      * @return  True if the name is a pattern, otherwise false.
1444      */
1445     public boolean isPattern() {
1446         return (_domain_pattern ||
1447                 _property_list_pattern ||
1448                 _property_value_pattern);
1449     }
1450 
1451     /**
1452      * Checks whether the object name is a pattern on the domain part.
1453      *
1454      * @return  True if the name is a domain pattern, otherwise false.
1455      *
1456      */
1457     public boolean isDomainPattern() {
1458         return _domain_pattern;
1459     }
1460 
1461     /**
1462      * Checks whether the object name is a pattern on the key properties.
1463      * <p>
1464      * An object name is a pattern on the key properties if it is a
1465      * pattern on the key property list (e.g. "d:k=v,*") or on the
1466      * property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*").
1467      *
1468      * @return  True if the name is a property pattern, otherwise false.
1469      */
1470     public boolean isPropertyPattern() {
1471         return _property_list_pattern || _property_value_pattern;
1472     }
1473 
1474     /**
1475      * Checks whether the object name is a pattern on the key property list.
1476      * <p>
1477      * For example, "d:k=v,*" and "d:k=*,*" are key property list patterns
1478      * whereas "d:k=*" is not.
1479      *
1480      * @return  True if the name is a property list pattern, otherwise false.
1481      *
1482      * @since 1.6
1483      */
1484     public boolean isPropertyListPattern() {
1485         return _property_list_pattern;
1486     }
1487 
1488     /**
1489      * Checks whether the object name is a pattern on the value part
1490      * of at least one of the key properties.
1491      * <p>
1492      * For example, "d:k=*" and "d:k=*,*" are property value patterns
1493      * whereas "d:k=v,*" is not.
1494      *
1495      * @return  True if the name is a property value pattern, otherwise false.
1496      *
1497      * @since 1.6
1498      */
1499     public boolean isPropertyValuePattern() {
1500         return _property_value_pattern;
1501     }
1502 
1503     /**
1504      * Checks whether the value associated with a key in a key
1505      * property is a pattern.
1506      *
1507      * @param property The property whose value is to be checked.
1508      *
1509      * @return True if the value associated with the given key property
1510      * is a pattern, otherwise false.
1511      *
1512      * @exception NullPointerException If <code>property</code> is null.
1513      * @exception IllegalArgumentException If <code>property</code> is not
1514      * a valid key property for this ObjectName.
1515      *
1516      * @since 1.6
1517      */
1518     public boolean isPropertyValuePattern(String property) {
1519         if (property == null)
1520             throw new NullPointerException("key property can't be null");
1521         for (int i = 0; i < _ca_array.length; i++) {
1522             Property prop = _ca_array[i];
1523             String key = prop.getKeyString(_canonicalName);
1524             if (key.equals(property))
1525                 return (prop instanceof PatternProperty);
1526         }
1527         throw new IllegalArgumentException("key property not found");
1528     }
1529 
1530     /**
1531      * <p>Returns the canonical form of the name; that is, a string
1532      * representation where the properties are sorted in lexical
1533      * order.</p>
1534      *
1535      * <p>More precisely, the canonical form of the name is a String
1536      * consisting of the <em>domain part</em>, a colon
1537      * (<code>:</code>), the <em>canonical key property list</em>, and
1538      * a <em>pattern indication</em>.</p>
1539      *
1540      * <p>The <em>canonical key property list</em> is the same string
1541      * as described for {@link #getCanonicalKeyPropertyListString()}.</p>
1542      *
1543      * <p>The <em>pattern indication</em> is:
1544      * <ul>
1545      * <li>empty for an ObjectName
1546      * that is not a property list pattern;
1547      * <li>an asterisk for an ObjectName
1548      * that is a property list pattern with no keys; or
1549      * <li>a comma and an
1550      * asterisk (<code>,*</code>) for an ObjectName that is a property
1551      * list pattern with at least one key.
1552      * </ul></p>
1553      *
1554      * @return The canonical form of the name.
1555      */
1556     public String getCanonicalName()  {
1557         return _canonicalName;
1558     }
1559 
1560     /**
1561      * Returns the domain part.
1562      *
1563      * @return The domain.
1564      */
1565     public String getDomain()  {
1566         return _canonicalName.substring(0, _domain_length);
1567     }
1568 
1569     /**
1570      * Obtains the value associated with a key in a key property.
1571      *
1572      * @param property The property whose value is to be obtained.
1573      *
1574      * @return The value of the property, or null if there is no such
1575      * property in this ObjectName.
1576      *
1577      * @exception NullPointerException If <code>property</code> is null.
1578      */
1579     public String getKeyProperty(String property) {
1580         return _getKeyPropertyList().get(property);
1581     }
1582 
1583     /**
1584      * <p>Returns the key properties as a Map.  The returned
1585      * value is a Map in which each key is a key in the
1586      * ObjectName's key property list and each value is the associated
1587      * value.</p>
1588      *
1589      * <p>The returned value must not be modified.</p>
1590      *
1591      * @return The table of key properties.
1592      */
1593     private Map<String,String> _getKeyPropertyList()  {
1594         synchronized (this) {
1595             if (_propertyList == null) {
1596                 // build (lazy eval) the property list from the canonical
1597                 // properties array
1598                 _propertyList = new HashMap<String,String>();
1599                 int len = _ca_array.length;
1600                 Property prop;
1601                 for (int i = len - 1; i >= 0; i--) {
1602                     prop = _ca_array[i];
1603                     _propertyList.put(prop.getKeyString(_canonicalName),
1604                                       prop.getValueString(_canonicalName));
1605                 }
1606             }
1607         }
1608         return _propertyList;
1609     }
1610 
1611     /**
1612      * <p>Returns the key properties as a Hashtable.  The returned
1613      * value is a Hashtable in which each key is a key in the
1614      * ObjectName's key property list and each value is the associated
1615      * value.</p>
1616      *
1617      * <p>The returned value may be unmodifiable.  If it is
1618      * modifiable, changing it has no effect on this ObjectName.</p>
1619      *
1620      * @return The table of key properties.
1621      */
1622     // CR 6441274 depends on the modification property defined above
1623     public Hashtable<String,String> getKeyPropertyList()  {
1624         return new Hashtable<String,String>(_getKeyPropertyList());
1625     }
1626 
1627     /**
1628      * <p>Returns a string representation of the list of key
1629      * properties specified at creation time.  If this ObjectName was
1630      * constructed with the constructor {@link #ObjectName(String)},
1631      * the key properties in the returned String will be in the same
1632      * order as in the argument to the constructor.</p>
1633      *
1634      * @return The key property list string.  This string is
1635      * independent of whether the ObjectName is a pattern.
1636      */
1637     public String getKeyPropertyListString()  {
1638         // BEWARE : we rebuild the propertyliststring at each call !!
1639         if (_kp_array.length == 0) return "";
1640 
1641         // the size of the string is the canonical one minus domain
1642         // part and pattern part
1643         final int total_size = _canonicalName.length() - _domain_length - 1
1644             - (_property_list_pattern?2:0);
1645 
1646         final char[] dest_chars = new char[total_size];
1647         final char[] value = _canonicalName.toCharArray();
1648         writeKeyPropertyListString(value,dest_chars,0);
1649         return new String(dest_chars);
1650     }
1651 
1652     /**
1653      * <p>Returns the serialized string of the ObjectName.
1654      * properties specified at creation time.  If this ObjectName was
1655      * constructed with the constructor {@link #ObjectName(String)},
1656      * the key properties in the returned String will be in the same
1657      * order as in the argument to the constructor.</p>
1658      *
1659      * @return The key property list string.  This string is
1660      * independent of whether the ObjectName is a pattern.
1661      */
1662     private String getSerializedNameString()  {
1663 
1664         // the size of the string is the canonical one
1665         final int total_size = _canonicalName.length();
1666         final char[] dest_chars = new char[total_size];
1667         final char[] value = _canonicalName.toCharArray();
1668         final int offset = _domain_length+1;
1669 
1670         // copy "domain:" into dest_chars
1671         //
1672         System.arraycopy(value, 0, dest_chars, 0, offset);
1673 
1674         // Add property list string
1675         final int end = writeKeyPropertyListString(value,dest_chars,offset);
1676 
1677         // Add ",*" if necessary
1678         if (_property_list_pattern) {
1679             if (end == offset)  {
1680                 // Property list string is empty.
1681                 dest_chars[end] = '*';
1682             } else {
1683                 // Property list string is not empty.
1684                 dest_chars[end]   = ',';
1685                 dest_chars[end+1] = '*';
1686             }
1687         }
1688 
1689         return new String(dest_chars);
1690     }
1691 
1692     /**
1693      * <p>Write a string representation of the list of key
1694      * properties specified at creation time in the given array, starting
1695      * at the specified offset.  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 offset + #of chars written
1701      */
1702     private int writeKeyPropertyListString(char[] canonicalChars,
1703                                            char[] data, int offset)  {
1704         if (_kp_array.length == 0) return offset;
1705 
1706         final char[] dest_chars = data;
1707         final char[] value = canonicalChars;
1708 
1709         int index = offset;
1710         final int len = _kp_array.length;
1711         final int last = len - 1;
1712         for (int i = 0; i < len; i++) {
1713             final Property prop = _kp_array[i];
1714             final int prop_len = prop._key_length + prop._value_length + 1;
1715             System.arraycopy(value, prop._key_index, dest_chars, index,
1716                              prop_len);
1717             index += prop_len;
1718             if (i < last ) dest_chars[index++] = ',';
1719         }
1720         return index;
1721     }
1722 
1723 
1724 
1725     /**
1726      * Returns a string representation of the list of key properties,
1727      * in which the key properties are sorted in lexical order. This
1728      * is used in lexicographic comparisons performed in order to
1729      * select MBeans based on their key property list.  Lexical order
1730      * is the order implied by {@link String#compareTo(String)
1731      * String.compareTo(String)}.
1732      *
1733      * @return The canonical key property list string.  This string is
1734      * independent of whether the ObjectName is a pattern.
1735      */
1736     public String getCanonicalKeyPropertyListString()  {
1737         if (_ca_array.length == 0) return "";
1738 
1739         int len = _canonicalName.length();
1740         if (_property_list_pattern) len -= 2;
1741         return _canonicalName.substring(_domain_length +1, len);
1742     }
1743     // Category : Getter methods <===================================
1744 
1745     // Category : Utilities ---------------------------------------->
1746 
1747     /**
1748      * <p>Returns a string representation of the object name.  The
1749      * format of this string is not specified, but users can expect
1750      * that two ObjectNames return the same string if and only if they
1751      * are equal.</p>
1752      *
1753      * @return a string representation of this object name.
1754      */
1755     @Override
1756     public String toString()  {
1757         return getSerializedNameString();
1758     }
1759 
1760     /**
1761      * Compares the current object name with another object name.  Two
1762      * ObjectName instances are equal if and only if their canonical
1763      * forms are equal.  The canonical form is the string described
1764      * for {@link #getCanonicalName()}.
1765      *
1766      * @param object  The object name that the current object name is to be
1767      *        compared with.
1768      *
1769      * @return True if <code>object</code> is an ObjectName whose
1770      * canonical form is equal to that of this ObjectName.
1771      */
1772     @Override
1773     public boolean equals(Object object)  {
1774 
1775         // same object case
1776         if (this == object) return true;
1777 
1778         // object is not an object name case
1779         if (!(object instanceof ObjectName)) return false;
1780 


1781         ObjectName on = (ObjectName) object;
1782         String on_string = on._canonicalName;
1783         if(_canonicalName.equals(on_string)) return true;
1784 


1785         return false;
1786    }
1787 
1788     /**
1789      * Returns a hash code for this object name.
1790      *
1791      */
1792     @Override
1793     public int hashCode() {
1794         return _canonicalName.hashCode();
1795     }
1796 
1797     /**
1798      * <p>Returns a quoted form of the given String, suitable for
1799      * inclusion in an ObjectName.  The returned value can be used as
1800      * the value associated with a key in an ObjectName.  The String
1801      * <code>s</code> may contain any character.  Appropriate quoting
1802      * ensures that the returned value is legal in an ObjectName.</p>
1803      *
1804      * <p>The returned value consists of a quote ('"'), a sequence of
1805      * characters corresponding to the characters of <code>s</code>,
1806      * and another quote.  Characters in <code>s</code> appear
1807      * unchanged within the returned value except:</p>
1808      *
1809      * <ul>
1810      * <li>A quote ('"') is replaced by a backslash (\) followed by a quote.</li>
1811      * <li>An asterisk ('*') is replaced by a backslash (\) followed by an
1812      * asterisk.</li>
1813      * <li>A question mark ('?') is replaced by a backslash (\) followed by
1814      * a question mark.</li>
1815      * <li>A backslash ('\') is replaced by two backslashes.</li>
1816      * <li>A newline character (the character '\n' in Java) is replaced
1817      * by a backslash followed by the character '\n'.</li>
1818      * </ul>
1819      *
1820      * @param s the String to be quoted.
1821      *
1822      * @return the quoted String.
1823      *
1824      * @exception NullPointerException if <code>s</code> is null.
1825      *
1826      */
1827     public static String quote(String s) {
1828         final StringBuilder buf = new StringBuilder("\"");
1829         final int len = s.length();
1830         for (int i = 0; i < len; i++) {
1831             char c = s.charAt(i);
1832             switch (c) {
1833             case '\n':
1834                 c = 'n';
1835                 buf.append('\\');
1836                 break;
1837             case '\\':
1838             case '\"':
1839             case '*':
1840             case '?':
1841                 buf.append('\\');
1842                 break;
1843             }
1844             buf.append(c);
1845         }
1846         buf.append('"');
1847         return buf.toString();
1848     }
1849 
1850     /**
1851      * <p>Returns an unquoted form of the given String.  If
1852      * <code>q</code> is a String returned by {@link #quote quote(s)},
1853      * then <code>unquote(q).equals(s)</code>.  If there is no String
1854      * <code>s</code> for which <code>quote(s).equals(q)</code>, then
1855      * unquote(q) throws an IllegalArgumentException.</p>
1856      *
1857      * <p>These rules imply that there is a one-to-one mapping between
1858      * quoted and unquoted forms.</p>
1859      *
1860      * @param q the String to be unquoted.
1861      *
1862      * @return the unquoted String.
1863      *
1864      * @exception IllegalArgumentException if <code>q</code> could not
1865      * have been returned by the {@link #quote} method, for instance
1866      * if it does not begin and end with a quote (").
1867      *
1868      * @exception NullPointerException if <code>q</code> is null.
1869      *
1870      */
1871     public static String unquote(String q) {
1872         final StringBuilder buf = new StringBuilder();
1873         final int len = q.length();
1874         if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
1875             throw new IllegalArgumentException("Argument not quoted");
1876         for (int i = 1; i < len - 1; i++) {
1877             char c = q.charAt(i);
1878             if (c == '\\') {
1879                 if (i == len - 2)
1880                     throw new IllegalArgumentException("Trailing backslash");
1881                 c = q.charAt(++i);
1882                 switch (c) {
1883                 case 'n':
1884                     c = '\n';
1885                     break;
1886                 case '\\':
1887                 case '\"':
1888                 case '*':
1889                 case '?':
1890                     break;
1891                 default:
1892                   throw new IllegalArgumentException(
1893                                    "Bad character '" + c + "' after backslash");
1894                 }
1895             } else {
1896                 switch (c) {
1897                     case '*' :
1898                     case '?' :
1899                     case '\"':
1900                     case '\n':
1901                          throw new IllegalArgumentException(
1902                                           "Invalid unescaped character '" + c +
1903                                           "' in the string to unquote");
1904                 }
1905             }
1906             buf.append(c);
1907         }
1908         return buf.toString();
1909     }
1910 
1911     /**
1912      * Defines the wildcard "*:*" ObjectName.
1913      *
1914      * @since 1.6
1915      */
1916     public static final ObjectName WILDCARD = Util.newObjectName("*:*");
1917 
1918     // Category : Utilities <===================================
1919 
1920     // Category : QueryExp Interface ---------------------------------------->
1921 
1922     /**
1923      * <p>Test whether this ObjectName, which may be a pattern,
1924      * matches another ObjectName.  If <code>name</code> is a pattern,
1925      * the result is false.  If this ObjectName is a pattern, the
1926      * result is true if and only if <code>name</code> matches the
1927      * pattern.  If neither this ObjectName nor <code>name</code> is
1928      * a pattern, the result is true if and only if the two
1929      * ObjectNames are equal as described for the {@link
1930      * #equals(Object)} method.</p>
1931      *
1932      * @param name The name of the MBean to compare to.
1933      *
1934      * @return True if <code>name</code> matches this ObjectName.
1935      *
1936      * @exception NullPointerException if <code>name</code> is null.
1937      *
1938      */
1939     public boolean apply(ObjectName name) {
1940 
1941         if (name == null) throw new NullPointerException();
1942 
1943         if (name._domain_pattern ||
1944             name._property_list_pattern ||
1945             name._property_value_pattern)
1946             return false;
1947 
1948         // No pattern
1949         if (!_domain_pattern &&
1950             !_property_list_pattern &&
1951             !_property_value_pattern)
1952             return _canonicalName.equals(name._canonicalName);
1953 
1954         return matchDomains(name) && matchKeys(name);
1955     }
1956 
1957     private final boolean matchDomains(ObjectName name) {
1958         if (_domain_pattern) {
1959             // wildmatch domains
1960             // This ObjectName is the pattern
1961             // The other ObjectName is the string.
1962             return Util.wildmatch(name.getDomain(),getDomain());
1963         }
1964         return getDomain().equals(name.getDomain());
1965     }
1966 
1967     private final boolean matchKeys(ObjectName name) {
1968         // If key property value pattern but not key property list
1969         // pattern, then the number of key properties must be equal
1970         //
1971         if (_property_value_pattern &&
1972             !_property_list_pattern &&
1973             (name._ca_array.length != _ca_array.length))
1974                 return false;
1975 
1976         // If key property value pattern or key property list pattern,
1977         // then every property inside pattern should exist in name
1978         //
1979         if (_property_value_pattern || _property_list_pattern) {
1980             final Map<String,String> nameProps = name._getKeyPropertyList();
1981             final Property[] props = _ca_array;
1982             final String cn = _canonicalName;
1983             for (int i = props.length - 1; i >= 0 ; i--) {
1984                 // Find value in given object name for key at current
1985                 // index in receiver
1986                 //
1987                 final Property p = props[i];
1988                 final String   k = p.getKeyString(cn);
1989                 final String   v = nameProps.get(k);
1990                 // Did we find a value for this key ?
1991                 //
1992                 if (v == null) return false;
1993                 // If this property is ok (same key, same value), go to next
1994                 //
1995                 if (_property_value_pattern && (p instanceof PatternProperty)) {
1996                     // wildmatch key property values
1997                     // p is the property pattern, v is the string
1998                     if (Util.wildmatch(v,p.getValueString(cn)))
1999                         continue;
2000                     else
2001                         return false;
2002                 }
2003                 if (v.equals(p.getValueString(cn))) continue;
2004                 return false;
2005             }
2006             return true;
2007         }
2008 
2009         // If no pattern, then canonical names must be equal
2010         //
2011         final String p1 = name.getCanonicalKeyPropertyListString();
2012         final String p2 = getCanonicalKeyPropertyListString();
2013         return (p1.equals(p2));
2014     }
2015 
2016     /* Method inherited from QueryExp, no implementation needed here
2017        because ObjectName is not relative to an MBeanServer and does
2018        not contain a subquery.
2019     */
2020     public void setMBeanServer(MBeanServer mbs) { }
2021 
2022     // Category : QueryExp Interface <=========================
2023 
2024     // Category : Comparable Interface ---------------------------------------->
2025 
2026     /**
2027      * <p>Compares two ObjectName instances. The ordering relation between
2028      * ObjectNames is not completely specified but is intended to be such
2029      * that a sorted list of ObjectNames will appear in an order that is
2030      * convenient for a person to read.</p>
2031      *
2032      * <p>In particular, if the two ObjectName instances have different
2033      * domains then their order is the lexicographical order of the domains.
2034      * The ordering of the key property list remains unspecified.</p>
2035      *
2036      * <p>For example, the ObjectName instances below:</p>
2037      * <ul>
2038      * <li>Shapes:type=Square,name=3</li>
2039      * <li>Colors:type=Red,name=2</li>
2040      * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2041      * <li>Colors:type=Red,name=1</li>
2042      * <li>Shapes:type=Square,name=1</li>
2043      * <li>Colors:type=Blue,name=1</li>
2044      * <li>Shapes:type=Square,name=2</li>
2045      * <li>JMImplementation:type=MBeanServerDelegate</li>
2046      * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2047      * </ul>
2048      * <p>could be ordered as follows:</p>
2049      * <ul>
2050      * <li>Colors:type=Blue,name=1</li>
2051      * <li>Colors:type=Red,name=1</li>
2052      * <li>Colors:type=Red,name=2</li>
2053      * <li>JMImplementation:type=MBeanServerDelegate</li>
2054      * <li>Shapes:type=Square,name=1</li>
2055      * <li>Shapes:type=Square,name=2</li>
2056      * <li>Shapes:type=Square,name=3</li>
2057      * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2058      * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2059      * </ul>
2060      *
2061      * @param name the ObjectName to be compared.
2062      *
2063      * @return a negative integer, zero, or a positive integer as this
2064      *         ObjectName is less than, equal to, or greater than the
2065      *         specified ObjectName.
2066      *
2067      * @since 1.6
2068      */
2069     public int compareTo(ObjectName name) {
2070         // Quick optimization:
2071         //
2072         if (name == this) return 0;
2073 
2074         // (1) Compare domains
2075         //
2076         int domainValue = this.getDomain().compareTo(name.getDomain());
2077         if (domainValue != 0)
2078             return domainValue;
2079 
2080         // (2) Compare "type=" keys
2081         //
2082         // Within a given domain, all names with missing or empty "type="
2083         // come before all names with non-empty type.
2084         //
2085         // When both types are missing or empty, canonical-name ordering
2086         // applies which is a total order.
2087         //
2088         String thisTypeKey = this.getKeyProperty("type");
2089         String anotherTypeKey = name.getKeyProperty("type");
2090         if (thisTypeKey == null)
2091             thisTypeKey = "";
2092         if (anotherTypeKey == null)
2093             anotherTypeKey = "";
2094         int typeKeyValue = thisTypeKey.compareTo(anotherTypeKey);
2095         if (typeKeyValue != 0)
2096             return typeKeyValue;
2097 
2098         // (3) Compare canonical names
2099         //
2100         return this.getCanonicalName().compareTo(name.getCanonicalName());
2101     }
2102 
2103     // Category : Comparable Interface <=========================
2104 
2105     // Public methods <========================================
2106 
2107 }
--- EOF ---