1 /*
2  * Copyright (c) 1999, 2015, 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.
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  * @implNote The maximum allowed length of the domain name in this implementation
223  *           is {@code Integer.MAX_VALUE/4}
224  */

225 @SuppressWarnings("serial"// don't complain serialVersionUID not constant
226 public class ObjectName implements Comparable<ObjectName>, QueryExp {
227     private static final int DOMAIN_PATTERN = 0x8000_0000;
228     private static final int PROPLIST_PATTERN = 0x4000_0000;
229     private static final int PROPVAL_PATTERN = 0x2000_0000;
230
231     private static final int FLAG_MASK = DOMAIN_PATTERN | PROPLIST_PATTERN |
232                                          PROPVAL_PATTERN;
233     private static final int DOMAIN_LENGTH_MASK = ~FLAG_MASK;
234
235     /**
236      * A structure recording property structure and
237      * proposing minimal services
238      */

239     private static class Property {
240
241         int _key_index;
242         int _key_length;
243         int _value_length;
244
245         /**
246          * Constructor.
247          */

248         Property(int key_index, int key_length, int value_length) {
249             _key_index = key_index;
250             _key_length = key_length;
251             _value_length = value_length;
252         }
253
254         /**
255          * Assigns the key index of property
256          */

257         void setKeyIndex(int key_index) {
258             _key_index = key_index;
259         }
260
261         /**
262          * Returns a key string for receiver key
263          */

264         String getKeyString(String name) {
265             return name.substring(_key_index, _key_index + _key_length);
266         }
267
268         /**
269          * Returns a value string for receiver key
270          */

271         String getValueString(String name) {
272             int in_begin = _key_index + _key_length + 1;
273             int out_end = in_begin + _value_length;
274             return name.substring(in_begin, out_end);
275         }
276     }
277
278     /**
279      * Marker class for value pattern property.
280      */

281     private static class PatternProperty extends Property {
282         /**
283          * Constructor.
284          */

285         PatternProperty(int key_index, int key_length, int value_length) {
286             super(key_index, key_length, value_length);
287         }
288     }
289
290     // Inner classes <========================================
291
292
293
294     // Private fields ---------------------------------------->
295
296
297     // Serialization compatibility stuff -------------------->
298
299     // Two serial forms are supported in this class. The selected form depends
300     // on system property "jmx.serial.form":
301     //  - "1.0" for JMX 1.0
302     //  - any other value for JMX 1.1 and higher
303     //
304     // Serial version for old serial form
305     private static final long oldSerialVersionUID = -5467795090068647408L;
306     //
307     // Serial version for new serial form
308     private static final long newSerialVersionUID = 1081892073854801359L;
309     //
310     // Serializable fields in old serial form
311     private static final ObjectStreamField[] oldSerialPersistentFields =
312     {
313         new ObjectStreamField("domain", String.class),
314         new ObjectStreamField("propertyList", Hashtable.class),
315         new ObjectStreamField("propertyListString", String.class),
316         new ObjectStreamField("canonicalName", String.class),
317         new ObjectStreamField("pattern", Boolean.TYPE),
318         new ObjectStreamField("propertyPattern", Boolean.TYPE)
319     };
320     //
321     // Serializable fields in new serial form
322     private static final ObjectStreamField[] newSerialPersistentFields = { };
323     //
324     // Actual serial version and serial form
325     private static final long serialVersionUID;
326     private static final ObjectStreamField[] serialPersistentFields;
327     private static boolean compat = false;
328     static {
329         try {
330             GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
331             String form = AccessController.doPrivileged(act);
332             compat = (form != null && form.equals("1.0"));
333         } catch (Exception e) {
334             // OK: exception means no compat with 1.0, too bad
335         }
336         if (compat) {
337             serialPersistentFields = oldSerialPersistentFields;
338             serialVersionUID = oldSerialVersionUID;
339         } else {
340             serialPersistentFields = newSerialPersistentFields;
341             serialVersionUID = newSerialVersionUID;
342         }
343     }
344
345     //
346     // Serialization compatibility stuff <==============================
347
348     // Class private fields ----------------------------------->
349
350     /**
351      * a shared empty array for empty property lists
352      */

353     static final private Property[] _Empty_property_array = new Property[0];
354
355
356     // Class private fields <==============================
357
358     // Instance private fields ----------------------------------->
359
360     /**
361      * a String containing the canonical name
362      */

363     private transient String _canonicalName;
364
365
366     /**
367      * An array of properties in the same seq order as time creation
368      */

369     private transient Property[] _kp_array;
370
371     /**
372      * An array of properties in the same seq order as canonical order
373      */

374     private transient Property[] _ca_array;
375
376
377     /**
378      * The propertyList of built object name. Initialized lazily.
379      * Table that contains all the pairs (key,value) for this ObjectName.
380      */

381     private transient Map<String,String> _propertyList;
382
383     /**
384      * This field encodes _domain_pattern, _property_list_pattern and
385      * _property_value_pattern booleans and _domain_length integer.
386      * <p>
387      * The following masks can be used to extract the value:
388      * <ul>
389      * <li>{@linkplain ObjectName#DOMAIN_PATTERN}</li>
390      * <li>{@linkplain ObjectName#PROPLIST_PATTERN}</li>
391      * <li>{@linkplain ObjectName#PROPVAL_PATTERN}</li>
392      * <li>{@linkplain ObjectName#DOMAIN_LENGTH_MASK}</li>
393      * </ul>
394      * </p>.
395      */

396     private transient int _compressed_storage = 0x0;
397
398     // Instance private fields <=======================================
399
400     // Private fields <========================================
401
402
403     //  Private methods ---------------------------------------->
404
405     // Category : Instance construction ------------------------->
406
407     /**
408      * Initializes this {@link ObjectName} from the given string
409      * representation.
410      *
411      * @param name A string representation of the {@link ObjectName}
412      *
413      * @exception MalformedObjectNameException The string passed as a
414      * parameter does not have the right format.
415      * @exception NullPointerException The <code>name</code> parameter
416      * is null.
417      */

418     private void construct(String name)
419         throws MalformedObjectNameException {
420
421         // The name cannot be null
422         if (name == null)
423             throw new NullPointerException("name cannot be null");
424
425         // Test if the name is empty
426         if (name.length() == 0) {
427             // this is equivalent to the whole word query object name.
428             _canonicalName = "*:*";
429             _kp_array = _Empty_property_array;
430             _ca_array = _Empty_property_array;
431             setDomainLength(1);
432             _propertyList = null;
433             setDomainPattern(true);
434             setPropertyListPattern(true);
435             setPropertyValuePattern(false);
436             return;
437         }
438
439         // initialize parsing of the string
440         final char[] name_chars = name.toCharArray();
441         final int len = name_chars.length;
442         final char[] canonical_chars = new char[len]; // canonical form will
443                                                       // be same length at most
444         int cname_index = 0;
445         int index = 0;
446         char c, c1;
447
448         // parses domain part
449     domain_parsing:
450         while (index < len) {
451             switch (name_chars[index]) {
452                 case ':' :
453                     setDomainLength(index++);
454                     break domain_parsing;
455                 case '=' :
456                     // ":" omission check.
457                     //
458                     // Although "=" is a valid character in the domain part
459                     // it is true that it is rarely used in the real world.
460                     // So check straight away if the ":" has been omitted
461                     // from the ObjectName. This allows us to provide a more
462                     // accurate exception message.
463                     int i = ++index;
464                     while ((i < len) && (name_chars[i++] != ':'))
465                         if (i == len)
466                             throw new MalformedObjectNameException(
467                                 "Domain part must be specified");
468                     break;
469                 case '\n' :
470                     throw new MalformedObjectNameException(
471                               "Invalid character '\\n' in domain name");
472                 case '*' :
473                 case '?' :
474                     setDomainPattern(true);
475                     index++;
476                     break;
477                 default :
478                     index++;
479                     break;
480             }
481         }
482
483         // check for non-empty properties
484         if (index == len)
485             throw new MalformedObjectNameException(
486                                          "Key properties cannot be empty");
487
488         // we have got the domain part, begins building of _canonicalName
489         int _domain_length = getDomainLength();
490         System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length);
491         canonical_chars[_domain_length] = ':';
492         cname_index = _domain_length + 1;
493
494         // parses property list
495         Property prop;
496         Map<String,Property> keys_map = new HashMap<String,Property>();
497         String[] keys;
498         String key_name;
499         boolean quoted_value;
500         int property_index = 0;
501         int in_index;
502         int key_index, key_length, value_index, value_length;
503
504         keys = new String[10];
505         _kp_array = new Property[10];
506         setPropertyListPattern(false);
507         setPropertyValuePattern(false);
508
509         while (index < len) {
510             c = name_chars[index];
511
512             // case of pattern properties
513             if (c == '*') {
514                 if (isPropertyListPattern())
515                     throw new MalformedObjectNameException(
516                               "Cannot have several '*' characters in pattern " +
517                               "property list");
518                 else {
519                     setPropertyListPattern(true);
520                     if ((++index < len ) && (name_chars[index] != ','))
521                         throw new MalformedObjectNameException(
522                                   "Invalid character found after '*': end of " +
523                                   "name or ',' expected");
524                     else if (index == len) {
525                         if (property_index == 0) {
526                             // empty properties case
527                             _kp_array = _Empty_property_array;
528                             _ca_array = _Empty_property_array;
529                             _propertyList = Collections.emptyMap();
530                         }
531                         break;
532                     } else {
533                         // correct pattern spec in props, continue
534                         index++;
535                         continue;
536                     }
537                 }
538             }
539
540             // standard property case, key part
541             in_index = index;
542             key_index = in_index;
543             if (name_chars[in_index] == '=')
544                 throw new MalformedObjectNameException("Invalid key (empty)");
545             while ((in_index < len) && ((c1 = name_chars[in_index++]) != '='))
546                 switch (c1) {
547                     // '=' considered to introduce value part
548                     case  '*' :
549                     case  '?' :
550                     case  ',' :
551                     case  ':' :
552                     case  '\n' :
553                         final String ichar = ((c1=='\n')?"\\n":""+c1);
554                         throw new MalformedObjectNameException(
555                                   "Invalid character '" + ichar +
556                                   "' in key part of property");
557                 }
558             if (name_chars[in_index - 1] != '=')
559                 throw new MalformedObjectNameException(
560                                              "Unterminated key property part");
561             value_index = in_index; // in_index pointing after '=' char
562             key_length = value_index - key_index - 1; // found end of key
563
564             // standard property case, value part
565             boolean value_pattern = false;
566             if (in_index < len && name_chars[in_index] == '\"') {
567                 quoted_value = true;
568                 // the case of quoted value part
569             quoted_value_parsing:
570                 while ((++in_index < len) &&
571                        ((c1 = name_chars[in_index]) != '\"')) {
572                     // the case of an escaped character
573                     if (c1 == '\\') {
574                         if (++in_index == len)
575                             throw new MalformedObjectNameException(
576                                                "Unterminated quoted value");
577                         switch (c1 = name_chars[in_index]) {
578                             case '\\' :
579                             case '\"' :
580                             case '?' :
581                             case '*' :
582                             case 'n' :
583                                 break// valid character
584                             default :
585                                 throw new MalformedObjectNameException(
586                                           "Invalid escape sequence '\\" +
587                                           c1 + "' in quoted value");
588                         }
589                     } else if (c1 == '\n') {
590                         throw new MalformedObjectNameException(
591                                                      "Newline in quoted value");
592                     } else {
593                         switch (c1) {
594                             case '?' :
595                             case '*' :
596                                 value_pattern = true;
597                                 break;
598                         }
599                     }
600                 }
601                 if (in_index == len)
602                     throw new MalformedObjectNameException(
603                                                  "Unterminated quoted value");
604                 else value_length = ++in_index - value_index;
605             } else {
606                 // the case of standard value part
607                 quoted_value = false;
608                 while ((in_index < len) && ((c1 = name_chars[in_index]) != ','))
609                 switch (c1) {
610                     // ',' considered to be the value separator
611                     case '*' :
612                     case '?' :
613                         value_pattern = true;
614                         in_index++;
615                         break;
616                     case '=' :
617                     case ':' :
618                     case '"' :
619                     case '\n' :
620                         final String ichar = ((c1=='\n')?"\\n":""+c1);
621                         throw new MalformedObjectNameException(
622                                                  "Invalid character '" + ichar +
623                                                  "' in value part of property");
624                     default :
625                         in_index++;
626                         break;
627                 }
628                 value_length = in_index - value_index;
629             }
630
631             // Parsed property, checks the end of name
632             if (in_index == len - 1) {
633                 if (quoted_value)
634                     throw new MalformedObjectNameException(
635                                              "Invalid ending character `" +
636                                              name_chars[in_index] + "'");
637                 else throw new MalformedObjectNameException(
638                                                   "Invalid ending comma");
639             } else in_index++;
640
641             // we got the key and value part, prepare a property for this
642             if (!value_pattern) {
643                 prop = new Property(key_index, key_length, value_length);
644             } else {
645                 setPropertyValuePattern(true);
646                 prop = new PatternProperty(key_index, key_length, value_length);
647             }
648             key_name = name.substring(key_index, key_index + key_length);
649
650             if (property_index == keys.length) {
651                 String[] tmp_string_array = new String[property_index + 10];
652                 System.arraycopy(keys, 0, tmp_string_array, 0, property_index);
653                 keys = tmp_string_array;
654             }
655             keys[property_index] = key_name;
656
657             addProperty(prop, property_index, keys_map, key_name);
658             property_index++;
659             index = in_index;
660         }
661
662         // computes and set canonical name
663         setCanonicalName(name_chars, canonical_chars, keys,
664                          keys_map, cname_index, property_index);
665     }
666
667     /**
668      * Construct an ObjectName from a domain and a Hashtable.
669      *
670      * @param domain Domain of the ObjectName.
671      * @param props  Map containing couples <i>key</i> {@literal ->} <i>value</i>.
672      *
673      * @exception MalformedObjectNameException The <code>domain</code>
674      * contains an illegal character, or one of the keys or values in
675      * <code>table</code> contains an illegal character, or one of the
676      * values in <code>table</code> does not follow the rules for quoting,
677      * or the domain's length exceeds the maximum allowed length.
678      * @exception NullPointerException One of the parameters is null.
679      */

680     private void construct(String domain, Map<String,String> props)
681         throws MalformedObjectNameException {
682
683         // The domain cannot be null
684         if (domain == null)
685             throw new NullPointerException("domain cannot be null");
686
687         // The key property list cannot be null
688         if (props == null)
689             throw new NullPointerException("key property list cannot be null");
690
691         // The key property list cannot be empty
692         if (props.isEmpty())
693             throw new MalformedObjectNameException(
694                                          "key property list cannot be empty");
695
696         // checks domain validity
697         if (!isDomain(domain))
698             throw new MalformedObjectNameException("Invalid domain: " + domain);
699
700         // init canonicalname
701         final StringBuilder sb = new StringBuilder();
702         sb.append(domain).append(':');
703         setDomainLength(domain.length());
704
705         // allocates the property array
706         int nb_props = props.size();
707         _kp_array = new Property[nb_props];
708
709         String[] keys = new String[nb_props];
710         final Map<String,Property> keys_map = new HashMap<String,Property>();
711         Property prop;
712         int key_index;
713         int i = 0;
714         for (Map.Entry<String,String> entry : props.entrySet()) {
715             if (sb.length() > 0)
716                 sb.append(",");
717             String key = entry.getKey();
718             String value;
719             try {
720                 value = entry.getValue();
721             } catch (ClassCastException e) {
722                 throw new MalformedObjectNameException(e.getMessage());
723             }
724             key_index = sb.length();
725             checkKey(key);
726             sb.append(key);
727             keys[i] = key;
728             sb.append("=");
729             boolean value_pattern = checkValue(value);
730             sb.append(value);
731             if (!value_pattern) {
732                 prop = new Property(key_index,
733                                     key.length(),
734                                     value.length());
735             } else {
736                 setPropertyValuePattern(true);
737                 prop = new PatternProperty(key_index,
738                                            key.length(),
739                                            value.length());
740             }
741             addProperty(prop, i, keys_map, key);
742             i++;
743         }
744
745         // initialize canonical name and data structure
746         int len = sb.length();
747         char[] initial_chars = new char[len];
748         sb.getChars(0, len, initial_chars, 0);
749         char[] canonical_chars = new char[len];
750         int copyLen = getDomainLength() + 1;
751         System.arraycopy(initial_chars, 0, canonical_chars, 0, copyLen);
752         setCanonicalName(initial_chars, canonical_chars, keys, keys_map,
753                          copyLen, _kp_array.length);
754     }
755     // Category : Instance construction <==============================
756
757     // Category : Internal utilities ------------------------------>
758
759     /**
760      * Add passed property to the list at the given index
761      * for the passed key name
762      */

763     private void addProperty(Property prop, int index,
764                              Map<String,Property> keys_map, String key_name)
765         throws MalformedObjectNameException {
766
767         if (keys_map.containsKey(key_name)) throw new
768                 MalformedObjectNameException("key `" +
769                                          key_name +"' already defined");
770
771         // if no more space for property arrays, have to increase it
772         if (index == _kp_array.length) {
773             Property[] tmp_prop_array = new Property[index + 10];
774             System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
775             _kp_array = tmp_prop_array;
776         }
777         _kp_array[index] = prop;
778         keys_map.put(key_name, prop);
779     }
780
781     /**
782      * Sets the canonical name of receiver from input 'specified_chars'
783      * array, by filling 'canonical_chars' array with found 'nb-props'
784      * properties starting at position 'prop_index'.
785      */

786     private void setCanonicalName(char[] specified_chars,
787                                   char[] canonical_chars,
788                                   String[] keys, Map<String,Property> keys_map,
789                                   int prop_index, int nb_props) {
790
791         // Sort the list of found properties
792         if (_kp_array != _Empty_property_array) {
793             String[] tmp_keys = new String[nb_props];
794             Property[] tmp_props = new Property[nb_props];
795
796             System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
797             Arrays.sort(tmp_keys);
798             keys = tmp_keys;
799             System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props);
800             _kp_array = tmp_props;
801             _ca_array = new Property[nb_props];
802
803             // now assigns _ca_array to the sorted list of keys
804             // (there cannot be two identical keys in an objectname.
805             for (int i = 0; i < nb_props; i++)
806                 _ca_array[i] = keys_map.get(keys[i]);
807
808             // now we build the canonical name and set begin indexes of
809             // properties to reflect canonical form
810             int last_index = nb_props - 1;
811             int prop_len;
812             Property prop;
813             for (int i = 0; i <= last_index; i++) {
814                 prop = _ca_array[i];
815                 // length of prop including '=' char
816                 prop_len = prop._key_length + prop._value_length + 1;
817                 System.arraycopy(specified_chars, prop._key_index,
818                                  canonical_chars, prop_index, prop_len);
819                 prop.setKeyIndex(prop_index);
820                 prop_index += prop_len;
821                 if (i != last_index) {
822                     canonical_chars[prop_index] = ',';
823                     prop_index++;
824                 }
825             }
826         }
827
828         // terminate canonicalname with '*' in case of pattern
829         if (isPropertyListPattern()) {
830             if (_kp_array != _Empty_property_array)
831                 canonical_chars[prop_index++] = ',';
832             canonical_chars[prop_index++] = '*';
833         }
834
835         // we now build the canonicalname string
836         _canonicalName = (new String(canonical_chars, 0, prop_index)).intern();
837     }
838
839     /**
840      * Parse a key.
841      * <pre>final int endKey=parseKey(s,startKey);</pre>
842      * <p>key starts at startKey (included), and ends at endKey (excluded).
843      * If (startKey == endKey), then the key is empty.
844      *
845      * @param s The char array of the original string.
846      * @param startKey index at which to begin parsing.
847      * @return The index following the last character of the key.
848      **/

849     private static int parseKey(final char[] s, final int startKey)
850         throws MalformedObjectNameException {
851         int next   = startKey;
852         int endKey = startKey;
853         final int len = s.length;
854         while (next < len) {
855             final char k = s[next++];
856             switch (k) {
857             case '*':
858             case '?':
859             case ',':
860             case ':':
861             case '\n':
862                 final String ichar = ((k=='\n')?"\\n":""+k);
863                 throw new
864                     MalformedObjectNameException("Invalid character in key: `"
865                                                  + ichar + "'");
866             case '=':
867                 // we got the key.
868                 endKey = next-1;
869                 break;
870             default:
871                 if (next < len) continue;
872                 else endKey=next;
873             }
874             break;
875         }
876         return endKey;
877     }
878
879     /**
880      * Parse a value.
881      * <pre>final int endVal=parseValue(s,startVal);</pre>
882      * <p>value starts at startVal (included), and ends at endVal (excluded).
883      * If (startVal == endVal), then the key is empty.
884      *
885      * @param s The char array of the original string.
886      * @param startValue index at which to begin parsing.
887      * @return The first element of the int array indicates the index
888      *         following the last character of the value. The second
889      *         element of the int array indicates that the value is
890      *         a pattern when its value equals 1.
891      **/

892     private static int[] parseValue(final char[] s, final int startValue)
893         throws MalformedObjectNameException {
894
895         boolean value_pattern = false;
896
897         int next   = startValue;
898         int endValue = startValue;
899
900         final int len = s.length;
901         final char q=s[startValue];
902
903         if (q == '"') {
904             // quoted value
905             if (++next == len) throw new
906                 MalformedObjectNameException("Invalid quote");
907             while (next < len) {
908                 char last = s[next];
909                 if (last == '\\') {
910                     if (++next == len) throw new
911                         MalformedObjectNameException(
912                            "Invalid unterminated quoted character sequence");
913                     last = s[next];
914                     switch (last) {
915                         case '\\' :
916                         case '?' :
917                         case '*' :
918                         case 'n' :
919                             break;
920                         case '\"' :
921                             // We have an escaped quote. If this escaped
922                             // quote is the last character, it does not
923                             // qualify as a valid termination quote.
924                             //
925                             if (next+1 == len) throw new
926                                 MalformedObjectNameException(
927                                                  "Missing termination quote");
928                             break;
929                         default:
930                             throw new
931                                 MalformedObjectNameException(
932                                 "Invalid quoted character sequence '\\" +
933                                 last + "'");
934                     }
935                 } else if (last == '\n') {
936                     throw new MalformedObjectNameException(
937                                                  "Newline in quoted value");
938                 } else if (last == '\"') {
939                     next++;
940                     break;
941                 } else {
942                     switch (last) {
943                         case '?' :
944                         case '*' :
945                             value_pattern = true;
946                             break;
947                     }
948                 }
949                 next++;
950
951                 // Check that last character is a termination quote.
952                 // We have already handled the case were the last
953                 // character is an escaped quote earlier.
954                 //
955                 if ((next >= len) && (last != '\"')) throw new
956                     MalformedObjectNameException("Missing termination quote");
957             }
958             endValue = next;
959             if (next < len) {
960                 if (s[next++] != ',') throw new
961                     MalformedObjectNameException("Invalid quote");
962             }
963         } else {
964             // Non quoted value.
965             while (next < len) {
966                 final char v=s[next++];
967                 switch(v) {
968                     case '*':
969                     case '?':
970                         value_pattern = true;
971                         if (next < len) continue;
972                         else endValue=next;
973                         break;
974                     case '=':
975                     case ':':
976                     case '\n' :
977                         final String ichar = ((v=='\n')?"\\n":""+v);
978                         throw new
979                             MalformedObjectNameException("Invalid character `" +
980                                                          ichar + "' in value");
981                     case ',':
982                         endValue = next-1;
983                         break;
984                     default:
985                         if (next < len) continue;
986                         else endValue=next;
987                 }
988                 break;
989             }
990         }
991         return new int[] { endValue, value_pattern ? 1 : 0 };
992     }
993
994     /**
995      * Check if the supplied value is a valid value.
996      *
997      * @return true if the value is a pattern, otherwise false.
998      */

999     private static boolean checkValue(String val)
1000         throws MalformedObjectNameException {
1001
1002         if (val == nullthrow new
1003             NullPointerException("Invalid value (null)");
1004
1005         final int len = val.length();
1006         if (len == 0)
1007             return false;
1008
1009         final char[] s = val.toCharArray();
1010         final int[] result = parseValue(s,0);
1011         final int endValue = result[0];
1012         final boolean value_pattern = result[1] == 1;
1013         if (endValue < len) throw new
1014             MalformedObjectNameException("Invalid character in value: `" +
1015                                          s[endValue] + "'");
1016         return value_pattern;
1017     }
1018
1019     /**
1020      * Check if the supplied key is a valid key.
1021      */

1022     private static void checkKey(String key)
1023         throws MalformedObjectNameException {
1024
1025         if (key == nullthrow new
1026             NullPointerException("Invalid key (null)");
1027
1028         final int len = key.length();
1029         if (len == 0) throw new
1030             MalformedObjectNameException("Invalid key (empty)");
1031         final char[] k=key.toCharArray();
1032         final int endKey = parseKey(k,0);
1033         if (endKey < len) throw new
1034             MalformedObjectNameException("Invalid character in value: `" +
1035                                          k[endKey] + "'");
1036     }
1037
1038
1039     // Category : Internal utilities <==============================
1040
1041     // Category : Internal accessors ------------------------------>
1042
1043     /**
1044      * Check if domain is a valid domain.  Set _domain_pattern if appropriate.
1045      */

1046     private boolean isDomain(String domain) {
1047         if (domain == nullreturn true;
1048         final int len = domain.length();
1049         int next = 0;
1050         while (next < len) {
1051             final char c = domain.charAt(next++);
1052             switch (c) {
1053                 case ':' :
1054                 case '\n' :
1055                     return false;
1056                 case '*' :
1057                 case '?' :
1058                     setDomainPattern(true);
1059                     break;
1060             }
1061         }
1062         return true;
1063     }
1064
1065     private int getDomainLength() {
1066         return _compressed_storage & DOMAIN_LENGTH_MASK;
1067     }
1068
1069     /**
1070      * Validates and sets the domain length
1071      * @param length The domain length
1072      * @throws MalformedObjectNameException
1073      *    When the given domain length exceeds the maximum allowed length
1074      */

1075     private void setDomainLength(int length) throws MalformedObjectNameException {
1076         if ((length & FLAG_MASK) != 0 ) {
1077             throw new MalformedObjectNameException(
1078                 "Domain name too long. Maximum allowed domain name length is:" +
1079                 DOMAIN_LENGTH_MASK);
1080         }
1081         _compressed_storage = (_compressed_storage & FLAG_MASK) | length;
1082     }
1083
1084     // Category : Internal accessors <==============================
1085
1086     // Category : Serialization ----------------------------------->
1087
1088     /**
1089      * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
1090      * @serialData <ul>
1091      *               <li>In the current serial form (value of property
1092      *                   <code>jmx.serial.form</code> differs from
1093      *                   <code>1.0</code>): the string
1094      *                   &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1095      *                   where: <ul>
1096      *                            <li>&lt;domain&gt; represents the domain part
1097      *                                of the {@link ObjectName}</li>
1098      *                            <li>&lt;properties&gt; represents the list of
1099      *                                properties, as returned by
1100      *                                {@link #getKeyPropertyListString}
1101      *                            <li>&lt;wild&gt; is empty if not
1102      *                                <code>isPropertyPattern</code>, or
1103      *                                is the character "<code>*</code>" if
1104      *                                <code>isPropertyPattern</code>
1105      *                                and &lt;properties&gt; is empty, or
1106      *                                is "<code>,*</code>" if
1107      *                                <code>isPropertyPattern</code> and
1108      *                                &lt;properties&gt; is not empty.
1109      *                            </li>
1110      *                          </ul>
1111      *                   The intent is that this string could be supplied
1112      *                   to the {@link #ObjectName(String)} constructor to
1113      *                   produce an equivalent {@link ObjectName}.
1114      *               </li>
1115      *               <li>In the old serial form (value of property
1116      *                   <code>jmx.serial.form</code> is
1117      *                   <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1118      *                   &lt;propertyListString&gt; &lt;canonicalName&gt;
1119      *                   &lt;pattern&gt; &lt;propertyPattern&gt;,
1120      *                   where: <ul>
1121      *                            <li>&lt;domain&gt; represents the domain part
1122      *                                of the {@link ObjectName}</li>
1123      *                            <li>&lt;propertyList&gt; is the
1124      *                                {@link Hashtable} that contains all the
1125      *                                pairs (key,value) for this
1126      *                                {@link ObjectName}</li>
1127      *                            <li>&lt;propertyListString&gt; is the
1128      *                                {@link String} representation of the
1129      *                                list of properties in any order (not
1130      *                                mandatorily a canonical representation)
1131      *                                </li>
1132      *                            <li>&lt;canonicalName&gt; is the
1133      *                                {@link String} containing this
1134      *                                {@link ObjectName}'s canonical name</li>
1135      *                            <li>&lt;pattern&gt; is a boolean which is
1136      *                                <code>true</code> if this
1137      *                                {@link ObjectName} contains a pattern</li>
1138      *                            <li>&lt;propertyPattern&gt; is a boolean which
1139      *                                is <code>true</code> if this
1140      *                                {@link ObjectName} contains a pattern in
1141      *                                the list of properties</li>
1142      *                          </ul>
1143      *               </li>
1144      *             </ul>
1145      */

1146     private void readObject(ObjectInputStream in)
1147         throws IOException, ClassNotFoundException {
1148
1149         String cn;
1150         if (compat) {
1151             // Read an object serialized in the old serial form
1152             //
1153             //in.defaultReadObject();
1154             final ObjectInputStream.GetField fields = in.readFields();
1155             String propListString =
1156                     (String)fields.get("propertyListString""");
1157
1158             // 6616825: take care of property patterns
1159             final boolean propPattern =
1160                     fields.get("propertyPattern" , false);
1161             if (propPattern) {
1162                 propListString =
1163                         (propListString.length()==0?"*":(propListString+",*"));
1164             }
1165
1166             cn = (String)fields.get("domain""default")+
1167                 ":"+ propListString;
1168         } else {
1169             // Read an object serialized in the new serial form
1170             //
1171             in.defaultReadObject();
1172             cn = (String)in.readObject();
1173         }
1174
1175         try {
1176             construct(cn);
1177         } catch (NullPointerException e) {
1178             throw new InvalidObjectException(e.toString());
1179         } catch (MalformedObjectNameException e) {
1180             throw new InvalidObjectException(e.toString());
1181         }
1182     }
1183
1184
1185     /**
1186      * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
1187      * @serialData <ul>
1188      *               <li>In the current serial form (value of property
1189      *                   <code>jmx.serial.form</code> differs from
1190      *                   <code>1.0</code>): the string
1191      *                   &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1192      *                   where: <ul>
1193      *                            <li>&lt;domain&gt; represents the domain part
1194      *                                of the {@link ObjectName}</li>
1195      *                            <li>&lt;properties&gt; represents the list of
1196      *                                properties, as returned by
1197      *                                {@link #getKeyPropertyListString}
1198      *                            <li>&lt;wild&gt; is empty if not
1199      *                                <code>isPropertyPattern</code>, or
1200      *                                is the character "<code>*</code>" if
1201      *                                this <code>isPropertyPattern</code>
1202      *                                and &lt;properties&gt; is empty, or
1203      *                                is "<code>,*</code>" if
1204      *                                <code>isPropertyPattern</code> and
1205      *                                &lt;properties&gt; is not empty.
1206      *                            </li>
1207      *                          </ul>
1208      *                   The intent is that this string could be supplied
1209      *                   to the {@link #ObjectName(String)} constructor to
1210      *                   produce an equivalent {@link ObjectName}.
1211      *               </li>
1212      *               <li>In the old serial form (value of property
1213      *                   <code>jmx.serial.form</code> is
1214      *                   <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1215      *                   &lt;propertyListString&gt; &lt;canonicalName&gt;
1216      *                   &lt;pattern&gt; &lt;propertyPattern&gt;,
1217      *                   where: <ul>
1218      *                            <li>&lt;domain&gt; represents the domain part
1219      *                                of the {@link ObjectName}</li>
1220      *                            <li>&lt;propertyList&gt; is the
1221      *                                {@link Hashtable} that contains all the
1222      *                                pairs (key,value) for this
1223      *                                {@link ObjectName}</li>
1224      *                            <li>&lt;propertyListString&gt; is the
1225      *                                {@link String} representation of the
1226      *                                list of properties in any order (not
1227      *                                mandatorily a canonical representation)
1228      *                                </li>
1229      *                            <li>&lt;canonicalName&gt; is the
1230      *                                {@link String} containing this
1231      *                                {@link ObjectName}'s canonical name</li>
1232      *                            <li>&lt;pattern&gt; is a boolean which is
1233      *                                <code>true</code> if this
1234      *                                {@link ObjectName} contains a pattern</li>
1235      *                            <li>&lt;propertyPattern&gt; is a boolean which
1236      *                                is <code>true</code> if this
1237      *                                {@link ObjectName} contains a pattern in
1238      *                                the list of properties</li>
1239      *                          </ul>
1240      *               </li>
1241      *             </ul>
1242      */

1243     private void writeObject(ObjectOutputStream out)
1244             throws IOException {
1245
1246       if (compat)
1247       {
1248         // Serializes this instance in the old serial form
1249         // Read CR 6441274 before making any changes to this code
1250         ObjectOutputStream.PutField fields = out.putFields();
1251         fields.put("domain", _canonicalName.substring(0, getDomainLength()));
1252         fields.put("propertyList", getKeyPropertyList());
1253         fields.put("propertyListString", getKeyPropertyListString());
1254         fields.put("canonicalName", _canonicalName);
1255         fields.put("pattern", (_compressed_storage & (DOMAIN_PATTERN | PROPLIST_PATTERN)) != 0);
1256         fields.put("propertyPattern", isPropertyListPattern());
1257         out.writeFields();
1258       }
1259       else
1260       {
1261         // Serializes this instance in the new serial form
1262         //
1263         out.defaultWriteObject();
1264         out.writeObject(getSerializedNameString());
1265       }
1266     }
1267
1268     //  Category : Serialization <===================================
1269
1270     // Private methods <========================================
1271
1272     // Public methods ---------------------------------------->
1273
1274     // Category : ObjectName Construction ------------------------------>
1275
1276     /**
1277      * <p>Return an instance of ObjectName that can be used anywhere
1278      * an object obtained with {@link #ObjectName(String) new
1279      * ObjectName(name)} can be used.  The returned object may be of
1280      * a subclass of ObjectName.  Calling this method twice with the
1281      * same parameters may return the same object or two equal but
1282      * not identical objects.</p>
1283      *
1284      * @param name  A string representation of the object name.
1285      *
1286      * @return an ObjectName corresponding to the given String.
1287      *
1288      * @exception MalformedObjectNameException The string passed as a
1289      * parameter does not have the right format.
1290      * @exception NullPointerException The <code>name</code> parameter
1291      * is null.
1292      *
1293      */

1294     public static ObjectName getInstance(String name)
1295             throws MalformedObjectNameException, NullPointerException {
1296         return new ObjectName(name);
1297     }
1298
1299     /**
1300      * <p>Return an instance of ObjectName that can be used anywhere
1301      * an object obtained with {@link #ObjectName(String, String,
1302      * String) new ObjectName(domain, key, value)} can be used.  The
1303      * returned object may be of a subclass of ObjectName.  Calling
1304      * this method twice with the same parameters may return the same
1305      * object or two equal but not identical objects.</p>
1306      *
1307      * @param domain  The domain part of the object name.
1308      * @param key  The attribute in the key property of the object name.
1309      * @param value The value in the key property of the object name.
1310      *
1311      * @return an ObjectName corresponding to the given domain,
1312      * key, and value.
1313      *
1314      * @exception MalformedObjectNameException The
1315      * <code>domain</code>, <code>key</code>, or <code>value</code>
1316      * contains an illegal character, or <code>value</code> does not
1317      * follow the rules for quoting, or the domain's length exceeds
1318      * the maximum allowed length.
1319      * @exception NullPointerException One of the parameters is null.
1320      *
1321      */

1322     public static ObjectName getInstance(String domain, String key,
1323                                          String value)
1324             throws MalformedObjectNameException {
1325         return new ObjectName(domain, key, value);
1326     }
1327
1328     /**
1329      * <p>Return an instance of ObjectName that can be used anywhere
1330      * an object obtained with {@link #ObjectName(String, Hashtable)
1331      * new ObjectName(domain, table)} can be used.  The returned
1332      * object may be of a subclass of ObjectName.  Calling this method
1333      * twice with the same parameters may return the same object or
1334      * two equal but not identical objects.</p>
1335      *
1336      * @param domain  The domain part of the object name.
1337      * @param table A hash table containing one or more key
1338      * properties.  The key of each entry in the table is the key of a
1339      * key property in the object name.  The associated value in the
1340      * table is the associated value in the object name.
1341      *
1342      * @return an ObjectName corresponding to the given domain and
1343      * key mappings.
1344      *
1345      * @exception MalformedObjectNameException The <code>domain</code>
1346      * contains an illegal character, or one of the keys or values in
1347      * <code>table</code> contains an illegal character, or one of the
1348      * values in <code>table</code> does not follow the rules for
1349      * quoting, or the domain's length exceeds the maximum allowed length.
1350      * @exception NullPointerException One of the parameters is null.
1351      *
1352      */

1353     public static ObjectName getInstance(String domain,
1354                                          Hashtable<String,String> table)
1355         throws MalformedObjectNameException {
1356         return new ObjectName(domain, table);
1357     }
1358
1359     /**
1360      * <p>Return an instance of ObjectName that can be used anywhere
1361      * the given object can be used.  The returned object may be of a
1362      * subclass of ObjectName.  If <code>name</code> is of a subclass
1363      * of ObjectName, it is not guaranteed that the returned object
1364      * will be of the same class.</p>
1365      *
1366      * <p>The returned value may or may not be identical to
1367      * <code>name</code>.  Calling this method twice with the same
1368      * parameters may return the same object or two equal but not
1369      * identical objects.</p>
1370      *
1371      * <p>Since ObjectName is immutable, it is not usually useful to
1372      * make a copy of an ObjectName.  The principal use of this method
1373      * is to guard against a malicious caller who might pass an
1374      * instance of a subclass with surprising behavior to sensitive
1375      * code.  Such code can call this method to obtain an ObjectName
1376      * that is known not to have surprising behavior.</p>
1377      *
1378      * @param name an instance of the ObjectName class or of a subclass
1379      *
1380      * @return an instance of ObjectName or a subclass that is known to
1381      * have the same semantics.  If <code>name</code> respects the
1382      * semantics of ObjectName, then the returned object is equal
1383      * (though not necessarily identical) to <code>name</code>.
1384      *
1385      * @exception NullPointerException The <code>name</code> is null.
1386      *
1387      */

1388     public static ObjectName getInstance(ObjectName name) {
1389         if (name.getClass().equals(ObjectName.class))
1390             return name;
1391         return Util.newObjectName(name.getSerializedNameString());
1392     }
1393
1394     /**
1395      * Construct an object name from the given string.
1396      *
1397      * @param name  A string representation of the object name.
1398      *
1399      * @exception MalformedObjectNameException The string passed as a
1400      * parameter does not have the right format.
1401      * @exception NullPointerException The <code>name</code> parameter
1402      * is null.
1403      */

1404     public ObjectName(String name)
1405         throws MalformedObjectNameException {
1406         construct(name);
1407     }
1408
1409     /**
1410      * Construct an object name with exactly one key property.
1411      *
1412      * @param domain  The domain part of the object name.
1413      * @param key  The attribute in the key property of the object name.
1414      * @param value The value in the key property of the object name.
1415      *
1416      * @exception MalformedObjectNameException The
1417      * <code>domain</code>, <code>key</code>, or <code>value</code>
1418      * contains an illegal character, or <code>value</code> does not
1419      * follow the rules for quoting, or the domain's length exceeds
1420      * the maximum allowed length.
1421      * @exception NullPointerException One of the parameters is null.
1422      */

1423     public ObjectName(String domain, String key, String value)
1424         throws MalformedObjectNameException {
1425         // If key or value are null a NullPointerException
1426         // will be thrown by the put method in Hashtable.
1427         //
1428         Map<String,String> table = Collections.singletonMap(key, value);
1429         construct(domain, table);
1430     }
1431
1432     /**
1433      * Construct an object name with several key properties from a Hashtable.
1434      *
1435      * @param domain  The domain part of the object name.
1436      * @param table A hash table containing one or more key
1437      * properties.  The key of each entry in the table is the key of a
1438      * key property in the object name.  The associated value in the
1439      * table is the associated value in the object name.
1440      *
1441      * @exception MalformedObjectNameException The <code>domain</code>
1442      * contains an illegal character, or one of the keys or values in
1443      * <code>table</code> contains an illegal character, or one of the
1444      * values in <code>table</code> does not follow the rules for
1445      * quoting, or the domain's length exceeds the maximum allowed length.
1446      * @exception NullPointerException One of the parameters is null.
1447      */

1448     public ObjectName(String domain, Hashtable<String,String> table)
1449             throws MalformedObjectNameException {
1450         construct(domain, table);
1451         /* The exception for when a key or value in the table is not a
1452            String is now ClassCastException rather than
1453            MalformedObjectNameException.  This was not previously
1454            specified.  */

1455     }
1456
1457     // Category : ObjectName Construction <==============================
1458
1459
1460     // Category : Getter methods ------------------------------>
1461
1462     /**
1463      * Checks whether the object name is a pattern.
1464      * <p>
1465      * An object name is a pattern if its domain contains a
1466      * wildcard or if the object name is a property pattern.
1467      *
1468      * @return  True if the name is a pattern, otherwise false.
1469      */

1470     public boolean isPattern() {
1471         return (_compressed_storage & FLAG_MASK) != 0;
1472     }
1473
1474     /**
1475      * Checks whether the object name is a pattern on the domain part.
1476      *
1477      * @return  True if the name is a domain pattern, otherwise false.
1478      *
1479      */

1480     public boolean isDomainPattern() {
1481         return (_compressed_storage & DOMAIN_PATTERN) != 0;
1482     }
1483
1484     /**
1485      * Marks the object name as representing a pattern on the domain part.
1486      * @param value {@code trueif the domain name is a pattern,
1487      *              {@code false} otherwise
1488      */

1489     private void setDomainPattern(boolean value) {
1490         if (value) {
1491             _compressed_storage |= DOMAIN_PATTERN;
1492         } else {
1493             _compressed_storage &= ~DOMAIN_PATTERN;
1494         }
1495     }
1496
1497     /**
1498      * Checks whether the object name is a pattern on the key properties.
1499      * <p>
1500      * An object name is a pattern on the key properties if it is a
1501      * pattern on the key property list (e.g. "d:k=v,*") or on the
1502      * property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*").
1503      *
1504      * @return  True if the name is a property pattern, otherwise false.
1505      */

1506     public boolean isPropertyPattern() {
1507         return (_compressed_storage & (PROPVAL_PATTERN | PROPLIST_PATTERN)) != 0;
1508     }
1509
1510     /**
1511      * Checks whether the object name is a pattern on the key property list.
1512      * <p>
1513      * For example, "d:k=v,*" and "d:k=*,*" are key property list patterns
1514      * whereas "d:k=*" is not.
1515      *
1516      * @return  True if the name is a property list pattern, otherwise false.
1517      *
1518      * @since 1.6
1519      */

1520     public boolean isPropertyListPattern() {
1521         return (_compressed_storage & PROPLIST_PATTERN) != 0;
1522     }
1523
1524     /**
1525      * Marks the object name as representing a pattern on the key property list.
1526      * @param value {@code trueif the key property list is a pattern,
1527      *              {@code false} otherwise
1528      */

1529     private void setPropertyListPattern(boolean value) {
1530         if (value) {
1531             _compressed_storage |= PROPLIST_PATTERN;
1532         } else {
1533             _compressed_storage &= ~PROPLIST_PATTERN;
1534         }
1535     }
1536
1537     /**
1538      * Checks whether the object name is a pattern on the value part
1539      * of at least one of the key properties.
1540      * <p>
1541      * For example, "d:k=*" and "d:k=*,*" are property value patterns
1542      * whereas "d:k=v,*" is not.
1543      *
1544      * @return  True if the name is a property value pattern, otherwise false.
1545      *
1546      * @since 1.6
1547      */

1548     public boolean isPropertyValuePattern() {
1549         return (_compressed_storage & PROPVAL_PATTERN) != 0;
1550     }
1551
1552     /**
1553      * Marks the object name as representing a pattern on the value part.
1554      * @param value {@code trueif the value part of at least one of the
1555      *              key properties is a pattern, {@code false} otherwise
1556      */

1557     private void setPropertyValuePattern(boolean value) {
1558         if (value) {
1559             _compressed_storage |= PROPVAL_PATTERN;
1560         } else {
1561             _compressed_storage &= ~PROPVAL_PATTERN;
1562         }
1563     }
1564
1565     /**
1566      * Checks whether the value associated with a key in a key
1567      * property is a pattern.
1568      *
1569      * @param property The property whose value is to be checked.
1570      *
1571      * @return True if the value associated with the given key property
1572      * is a pattern, otherwise false.
1573      *
1574      * @exception NullPointerException If <code>property</code> is null.
1575      * @exception IllegalArgumentException If <code>property</code> is not
1576      * a valid key property for this ObjectName.
1577      *
1578      * @since 1.6
1579      */

1580     public boolean isPropertyValuePattern(String property) {
1581         if (property == null)
1582             throw new NullPointerException("key property can't be null");
1583         for (int i = 0; i < _ca_array.length; i++) {
1584             Property prop = _ca_array[i];
1585             String key = prop.getKeyString(_canonicalName);
1586             if (key.equals(property))
1587                 return (prop instanceof PatternProperty);
1588         }
1589         throw new IllegalArgumentException("key property not found");
1590     }
1591
1592     /**
1593      * <p>Returns the canonical form of the name; that is, a string
1594      * representation where the properties are sorted in lexical
1595      * order.</p>
1596      *
1597      * <p>More precisely, the canonical form of the name is a String
1598      * consisting of the <em>domain part</em>, a colon
1599      * (<code>:</code>), the <em>canonical key property list</em>, and
1600      * a <em>pattern indication</em>.</p>
1601      *
1602      * <p>The <em>canonical key property list</em> is the same string
1603      * as described for {@link #getCanonicalKeyPropertyListString()}.</p>
1604      *
1605      * <p>The <em>pattern indication</em> is:
1606      * <ul>
1607      * <li>empty for an ObjectName
1608      * that is not a property list pattern;
1609      * <li>an asterisk for an ObjectName
1610      * that is a property list pattern with no keys; or
1611      * <li>a comma and an
1612      * asterisk (<code>,*</code>) for an ObjectName that is a property
1613      * list pattern with at least one key.
1614      * </ul>
1615      *
1616      * @return The canonical form of the name.
1617      */

1618     public String getCanonicalName()  {
1619         return _canonicalName;
1620     }
1621
1622     /**
1623      * Returns the domain part.
1624      *
1625      * @return The domain.
1626      */

1627     public String getDomain()  {
1628         return _canonicalName.substring(0, getDomainLength());
1629     }
1630
1631     /**
1632      * Obtains the value associated with a key in a key property.
1633      *
1634      * @param property The property whose value is to be obtained.
1635      *
1636      * @return The value of the property, or null if there is no such
1637      * property in this ObjectName.
1638      *
1639      * @exception NullPointerException If <code>property</code> is null.
1640      */

1641     public String getKeyProperty(String property) {
1642         return _getKeyPropertyList().get(property);
1643     }
1644
1645     /**
1646      * <p>Returns the key properties as a Map.  The returned
1647      * value is a Map in which each key is a key in the
1648      * ObjectName's key property list and each value is the associated
1649      * value.</p>
1650      *
1651      * <p>The returned value must not be modified.</p>
1652      *
1653      * @return The table of key properties.
1654      */

1655     private Map<String,String> _getKeyPropertyList()  {
1656         synchronized (this) {
1657             if (_propertyList == null) {
1658                 // build (lazy eval) the property list from the canonical
1659                 // properties array
1660                 _propertyList = new HashMap<String,String>();
1661                 int len = _ca_array.length;
1662                 Property prop;
1663                 for (int i = len - 1; i >= 0; i--) {
1664                     prop = _ca_array[i];
1665                     _propertyList.put(prop.getKeyString(_canonicalName),
1666                                       prop.getValueString(_canonicalName));
1667                 }
1668             }
1669         }
1670         return _propertyList;
1671     }
1672
1673     /**
1674      * <p>Returns the key properties as a Hashtable.  The returned
1675      * value is a Hashtable in which each key is a key in the
1676      * ObjectName's key property list and each value is the associated
1677      * value.</p>
1678      *
1679      * <p>The returned value may be unmodifiable.  If it is
1680      * modifiable, changing it has no effect on this ObjectName.</p>
1681      *
1682      * @return The table of key properties.
1683      */

1684     // CR 6441274 depends on the modification property defined above
1685     public Hashtable<String,String> getKeyPropertyList()  {
1686         return new Hashtable<String,String>(_getKeyPropertyList());
1687     }
1688
1689     /**
1690      * <p>Returns a string representation of the list of key
1691      * properties specified at creation time.  If this ObjectName was
1692      * constructed with the constructor {@link #ObjectName(String)},
1693      * the key properties in the returned String will be in the same
1694      * order as in the argument to the constructor.</p>
1695      *
1696      * @return The key property list string.  This string is
1697      * independent of whether the ObjectName is a pattern.
1698      */

1699     public String getKeyPropertyListString()  {
1700         // BEWARE : we rebuild the propertyliststring at each call !!
1701         if (_kp_array.length == 0) return "";
1702
1703         // the size of the string is the canonical one minus domain
1704         // part and pattern part
1705         final int total_size = _canonicalName.length() - getDomainLength() - 1
1706             - (isPropertyListPattern()?2:0);
1707
1708         final char[] dest_chars = new char[total_size];
1709         final char[] value = _canonicalName.toCharArray();
1710         writeKeyPropertyListString(value,dest_chars,0);
1711         return new String(dest_chars);
1712     }
1713
1714     /**
1715      * <p>Returns the serialized string of the ObjectName.
1716      * properties specified at creation time.  If this ObjectName was
1717      * constructed with the constructor {@link #ObjectName(String)},
1718      * the key properties in the returned String will be in the same
1719      * order as in the argument to the constructor.</p>
1720      *
1721      * @return The key property list string.  This string is
1722      * independent of whether the ObjectName is a pattern.
1723      */

1724     private String getSerializedNameString()  {
1725
1726         // the size of the string is the canonical one
1727         final int total_size = _canonicalName.length();
1728         final char[] dest_chars = new char[total_size];
1729         final char[] value = _canonicalName.toCharArray();
1730         final int offset = getDomainLength() + 1;
1731
1732         // copy "domain:" into dest_chars
1733         //
1734         System.arraycopy(value, 0, dest_chars, 0, offset);
1735
1736         // Add property list string
1737         final int end = writeKeyPropertyListString(value,dest_chars,offset);
1738
1739         // Add ",*" if necessary
1740         if (isPropertyListPattern()) {
1741             if (end == offset)  {
1742                 // Property list string is empty.
1743                 dest_chars[end] = '*';
1744             } else {
1745                 // Property list string is not empty.
1746                 dest_chars[end]   = ',';
1747                 dest_chars[end+1] = '*';
1748             }
1749         }
1750
1751         return new String(dest_chars);
1752     }
1753
1754     /**
1755      * <p>Write a string representation of the list of key
1756      * properties specified at creation time in the given array, starting
1757      * at the specified offset.  If this ObjectName was
1758      * constructed with the constructor {@link #ObjectName(String)},
1759      * the key properties in the returned String will be in the same
1760      * order as in the argument to the constructor.</p>
1761      *
1762      * @return offset + #of chars written
1763      */

1764     private int writeKeyPropertyListString(char[] canonicalChars,
1765                                            char[] data, int offset)  {
1766         if (_kp_array.length == 0) return offset;
1767
1768         final char[] dest_chars = data;
1769         final char[] value = canonicalChars;
1770
1771         int index = offset;
1772         final int len = _kp_array.length;
1773         final int last = len - 1;
1774         for (int i = 0; i < len; i++) {
1775             final Property prop = _kp_array[i];
1776             final int prop_len = prop._key_length + prop._value_length + 1;
1777             System.arraycopy(value, prop._key_index, dest_chars, index,
1778                              prop_len);
1779             index += prop_len;
1780             if (i < last ) dest_chars[index++] = ',';
1781         }
1782         return index;
1783     }
1784
1785
1786
1787     /**
1788      * Returns a string representation of the list of key properties,
1789      * in which the key properties are sorted in lexical order. This
1790      * is used in lexicographic comparisons performed in order to
1791      * select MBeans based on their key property list.  Lexical order
1792      * is the order implied by {@link String#compareTo(String)
1793      * String.compareTo(String)}.
1794      *
1795      * @return The canonical key property list string.  This string is
1796      * independent of whether the ObjectName is a pattern.
1797      */

1798     public String getCanonicalKeyPropertyListString()  {
1799         if (_ca_array.length == 0) return "";
1800
1801         int len = _canonicalName.length();
1802         if (isPropertyListPattern()) len -= 2;
1803         return _canonicalName.substring(getDomainLength() + 1, len);
1804     }
1805     // Category : Getter methods <===================================
1806
1807     // Category : Utilities ---------------------------------------->
1808
1809     /**
1810      * <p>Returns a string representation of the object name.  The
1811      * format of this string is not specified, but users can expect
1812      * that two ObjectNames return the same string if and only if they
1813      * are equal.</p>
1814      *
1815      * @return a string representation of this object name.
1816      */

1817     @Override
1818     public String toString()  {
1819         return getSerializedNameString();
1820     }
1821
1822     /**
1823      * Compares the current object name with another object name.  Two
1824      * ObjectName instances are equal if and only if their canonical
1825      * forms are equal.  The canonical form is the string described
1826      * for {@link #getCanonicalName()}.
1827      *
1828      * @param object  The object name that the current object name is to be
1829      *        compared with.
1830      *
1831      * @return True if <code>object</code> is an ObjectName whose
1832      * canonical form is equal to that of this ObjectName.
1833      */

1834     @Override
1835     public boolean equals(Object object)  {
1836
1837         // same object case
1838         if (this == object) return true;
1839
1840         // object is not an object name case
1841         if (!(object instanceof ObjectName)) return false;
1842
1843         // equality when canonical names are the same
1844         // (because usage of intern())
1845         ObjectName on = (ObjectName) object;
1846         String on_string = on._canonicalName;
1847         if (_canonicalName == on_string) return true;  // ES: OK
1848
1849         // Because we are sharing canonical form between object names,
1850         // we have finished the comparison at this stage ==> unequal
1851         return false;
1852    }
1853
1854     /**
1855      * Returns a hash code for this object name.
1856      *
1857      */

1858     @Override
1859     public int hashCode() {
1860         return _canonicalName.hashCode();
1861     }
1862
1863     /**
1864      * <p>Returns a quoted form of the given String, suitable for
1865      * inclusion in an ObjectName.  The returned value can be used as
1866      * the value associated with a key in an ObjectName.  The String
1867      * <code>s</code> may contain any character.  Appropriate quoting
1868      * ensures that the returned value is legal in an ObjectName.</p>
1869      *
1870      * <p>The returned value consists of a quote ('"'), a sequence of
1871      * characters corresponding to the characters of <code>s</code>,
1872      * and another quote.  Characters in <code>s</code> appear
1873      * unchanged within the returned value except:</p>
1874      *
1875      * <ul>
1876      * <li>A quote ('"') is replaced by a backslash (\) followed by a quote.</li>
1877      * <li>An asterisk ('*') is replaced by a backslash (\) followed by an
1878      * asterisk.</li>
1879      * <li>A question mark ('?') is replaced by a backslash (\) followed by
1880      * a question mark.</li>
1881      * <li>A backslash ('\') is replaced by two backslashes.</li>
1882      * <li>A newline character (the character '\n' in Java) is replaced
1883      * by a backslash followed by the character '\n'.</li>
1884      * </ul>
1885      *
1886      * @param s the String to be quoted.
1887      *
1888      * @return the quoted String.
1889      *
1890      * @exception NullPointerException if <code>s</code> is null.
1891      *
1892      */

1893     public static String quote(String s) {
1894         final StringBuilder buf = new StringBuilder("\"");
1895         final int len = s.length();
1896         for (int i = 0; i < len; i++) {
1897             char c = s.charAt(i);
1898             switch (c) {
1899             case '\n':
1900                 c = 'n';
1901                 buf.append('\\');
1902                 break;
1903             case '\\':
1904             case '\"':
1905             case '*':
1906             case '?':
1907                 buf.append('\\');
1908                 break;
1909             }
1910             buf.append(c);
1911         }
1912         buf.append('"');
1913         return buf.toString();
1914     }
1915
1916     /**
1917      * <p>Returns an unquoted form of the given String.  If
1918      * <code>q</code> is a String returned by {@link #quote quote(s)},
1919      * then <code>unquote(q).equals(s)</code>.  If there is no String
1920      * <code>s</code> for which <code>quote(s).equals(q)</code>, then
1921      * unquote(q) throws an IllegalArgumentException.</p>
1922      *
1923      * <p>These rules imply that there is a one-to-one mapping between
1924      * quoted and unquoted forms.</p>
1925      *
1926      * @param q the String to be unquoted.
1927      *
1928      * @return the unquoted String.
1929      *
1930      * @exception IllegalArgumentException if <code>q</code> could not
1931      * have been returned by the {@link #quote} method, for instance
1932      * if it does not begin and end with a quote (").
1933      *
1934      * @exception NullPointerException if <code>q</code> is null.
1935      *
1936      */

1937     public static String unquote(String q) {
1938         final StringBuilder buf = new StringBuilder();
1939         final int len = q.length();
1940         if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
1941             throw new IllegalArgumentException("Argument not quoted");
1942         for (int i = 1; i < len - 1; i++) {
1943             char c = q.charAt(i);
1944             if (c == '\\') {
1945                 if (i == len - 2)
1946                     throw new IllegalArgumentException("Trailing backslash");
1947                 c = q.charAt(++i);
1948                 switch (c) {
1949                 case 'n':
1950                     c = '\n';
1951                     break;
1952                 case '\\':
1953                 case '\"':
1954                 case '*':
1955                 case '?':
1956                     break;
1957                 default:
1958                   throw new IllegalArgumentException(
1959                                    "Bad character '" + c + "' after backslash");
1960                 }
1961             } else {
1962                 switch (c) {
1963                     case '*' :
1964                     case '?' :
1965                     case '\"':
1966                     case '\n':
1967                          throw new IllegalArgumentException(
1968                                           "Invalid unescaped character '" + c +
1969                                           "' in the string to unquote");
1970                 }
1971             }
1972             buf.append(c);
1973         }
1974         return buf.toString();
1975     }
1976
1977     /**
1978      * Defines the wildcard "*:*" ObjectName.
1979      *
1980      * @since 1.6
1981      */

1982     public static final ObjectName WILDCARD = Util.newObjectName("*:*");
1983
1984     // Category : Utilities <===================================
1985
1986     // Category : QueryExp Interface ---------------------------------------->
1987
1988     /**
1989      * <p>Test whether this ObjectName, which may be a pattern,
1990      * matches another ObjectName.  If <code>name</code> is a pattern,
1991      * the result is false.  If this ObjectName is a pattern, the
1992      * result is true if and only if <code>name</code> matches the
1993      * pattern.  If neither this ObjectName nor <code>name</code> is
1994      * a pattern, the result is true if and only if the two
1995      * ObjectNames are equal as described for the {@link
1996      * #equals(Object)} method.</p>
1997      *
1998      * @param name The name of the MBean to compare to.
1999      *
2000      * @return True if <code>name</code> matches this ObjectName.
2001      *
2002      * @exception NullPointerException if <code>name</code> is null.
2003      *
2004      */

2005     public boolean apply(ObjectName name) {
2006
2007         if (name == nullthrow new NullPointerException();
2008
2009         if (name.isPattern())
2010             return false;
2011
2012         // No pattern
2013         if (!isPattern())
2014             return _canonicalName.equals(name._canonicalName);
2015
2016         return matchDomains(name) && matchKeys(name);
2017     }
2018
2019     private final boolean matchDomains(ObjectName name) {
2020         if (isDomainPattern()) {
2021             // wildmatch domains
2022             // This ObjectName is the pattern
2023             // The other ObjectName is the string.
2024             return Util.wildmatch(name.getDomain(),getDomain());
2025         }
2026         return getDomain().equals(name.getDomain());
2027     }
2028
2029     private final boolean matchKeys(ObjectName name) {
2030         // If key property value pattern but not key property list
2031         // pattern, then the number of key properties must be equal
2032         //
2033         if (isPropertyValuePattern() &&
2034             !isPropertyListPattern() &&
2035             (name._ca_array.length != _ca_array.length))
2036                 return false;
2037
2038         // If key property value pattern or key property list pattern,
2039         // then every property inside pattern should exist in name
2040         //
2041         if (isPropertyPattern()) {
2042             final Map<String,String> nameProps = name._getKeyPropertyList();
2043             final Property[] props = _ca_array;
2044             final String cn = _canonicalName;
2045             for (int i = props.length - 1; i >= 0 ; i--) {
2046                 // Find value in given object name for key at current
2047                 // index in receiver
2048                 //
2049                 final Property p = props[i];
2050                 final String   k = p.getKeyString(cn);
2051                 final String   v = nameProps.get(k);
2052                 // Did we find a value for this key ?
2053                 //
2054                 if (v == nullreturn false;
2055                 // If this property is ok (same key, same value), go to next
2056                 //
2057                 if (isPropertyValuePattern() && (p instanceof PatternProperty)) {
2058                     // wildmatch key property values
2059                     // p is the property pattern, v is the string
2060                     if (Util.wildmatch(v,p.getValueString(cn)))
2061                         continue;
2062                     else
2063                         return false;
2064                 }
2065                 if (v.equals(p.getValueString(cn))) continue;
2066                 return false;
2067             }
2068             return true;
2069         }
2070
2071         // If no pattern, then canonical names must be equal
2072         //
2073         final String p1 = name.getCanonicalKeyPropertyListString();
2074         final String p2 = getCanonicalKeyPropertyListString();
2075         return (p1.equals(p2));
2076     }
2077
2078     /* Method inherited from QueryExp, no implementation needed here
2079        because ObjectName is not relative to an MBeanServer and does
2080        not contain a subquery.
2081     */

2082     public void setMBeanServer(MBeanServer mbs) { }
2083
2084     // Category : QueryExp Interface <=========================
2085
2086     // Category : Comparable Interface ---------------------------------------->
2087
2088     /**
2089      * <p>Compares two ObjectName instances. The ordering relation between
2090      * ObjectNames is not completely specified but is intended to be such
2091      * that a sorted list of ObjectNames will appear in an order that is
2092      * convenient for a person to read.</p>
2093      *
2094      * <p>In particular, if the two ObjectName instances have different
2095      * domains then their order is the lexicographical order of the domains.
2096      * The ordering of the key property list remains unspecified.</p>
2097      *
2098      * <p>For example, the ObjectName instances below:</p>
2099      * <ul>
2100      * <li>Shapes:type=Square,name=3</li>
2101      * <li>Colors:type=Red,name=2</li>
2102      * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2103      * <li>Colors:type=Red,name=1</li>
2104      * <li>Shapes:type=Square,name=1</li>
2105      * <li>Colors:type=Blue,name=1</li>
2106      * <li>Shapes:type=Square,name=2</li>
2107      * <li>JMImplementation:type=MBeanServerDelegate</li>
2108      * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2109      * </ul>
2110      * <p>could be ordered as follows:</p>
2111      * <ul>
2112      * <li>Colors:type=Blue,name=1</li>
2113      * <li>Colors:type=Red,name=1</li>
2114      * <li>Colors:type=Red,name=2</li>
2115      * <li>JMImplementation:type=MBeanServerDelegate</li>
2116      * <li>Shapes:type=Square,name=1</li>
2117      * <li>Shapes:type=Square,name=2</li>
2118      * <li>Shapes:type=Square,name=3</li>
2119      * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2120      * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2121      * </ul>
2122      *
2123      * @param name the ObjectName to be compared.
2124      *
2125      * @return a negative integer, zero, or a positive integer as this
2126      *         ObjectName is less than, equal to, or greater than the
2127      *         specified ObjectName.
2128      *
2129      * @since 1.6
2130      */

2131     public int compareTo(ObjectName name) {
2132         // Quick optimization:
2133         //
2134         if (name == thisreturn 0;
2135
2136         // (1) Compare domains
2137         //
2138         int domainValue = this.getDomain().compareTo(name.getDomain());
2139         if (domainValue != 0)
2140             return domainValue;
2141
2142         // (2) Compare "type=" keys
2143         //
2144         // Within a given domain, all names with missing or empty "type="
2145         // come before all names with non-empty type.
2146         //
2147         // When both types are missing or empty, canonical-name ordering
2148         // applies which is a total order.
2149         //
2150         String thisTypeKey = this.getKeyProperty("type");
2151         String anotherTypeKey = name.getKeyProperty("type");
2152         if (thisTypeKey == null)
2153             thisTypeKey = "";
2154         if (anotherTypeKey == null)
2155             anotherTypeKey = "";
2156         int typeKeyValue = thisTypeKey.compareTo(anotherTypeKey);
2157         if (typeKeyValue != 0)
2158             return typeKeyValue;
2159
2160         // (3) Compare canonical names
2161         //
2162         return this.getCanonicalName().compareTo(name.getCanonicalName());
2163     }
2164
2165     // Category : Comparable Interface <=========================
2166
2167     // Public methods <========================================
2168
2169 }
2170