001    /* ObjectName.java -- Represent the name of a bean, or a pattern for a name.
002       Copyright (C) 2006, 2007 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.management;
039    
040    import java.io.Serializable;
041    
042    import java.util.Collections;
043    import java.util.Hashtable;
044    import java.util.Iterator;
045    import java.util.Map;
046    import java.util.TreeMap;
047    
048    import java.io.IOException;
049    import java.io.InvalidObjectException;
050    import java.io.ObjectInputStream;
051    import java.io.ObjectOutputStream;
052    
053    /**
054     * <p>
055     * An {@link ObjectName} instance represents the name of a management
056     * bean, or a pattern which may match the name of one or more
057     * management beans.  Patterns are distinguished from names by the
058     * presence of the '?' and '*' characters (which match a single
059     * character and a series of zero or more characters, respectively).
060     * </p>
061     * <p>
062     * Each name begins with a domain element, which is terminated by
063     * a ':' character.  The domain may be empty.  If so, it will be
064     * replaced by the default domain of the bean server in certain
065     * contexts.  The domain is a pattern, if it contains either '?'
066     * or '*'.  To avoid collisions, it is usual to use reverse
067     * DNS names for the domain, as in Java package and property names.
068     * </p>
069     * <p>
070     * Following the ':' character is a series of properties.  The list
071     * is separated by commas, and largely consists of unordered key-value
072     * pairs, separated by an equals sign ('=').  At most one element may
073     * be an asterisk ('*'), which turns the {@link ObjectName} instance
074     * into a <emph>property list pattern</emph>.  In this situation, the pattern
075     * matches a name if the name contains at least those key-value pairs
076     * given and has the same domain.
077     * </p>
078     * <p>
079     * A <emph>key</emph> is a string of characters which doesn't include
080     * any of those used as delimiters or in patterns (':', '=', ',', '?'
081     * and '*').  Keys must be unique.
082     * </p>
083     * <p>
084     * A value may be <emph>quoted</emph> or <emph>unquoted</emph>.  Unquoted
085     * values obey the same rules as given for keys above.  Quoted values are
086     * surrounded by quotation marks ("), and use a backslash ('\') character
087     * to include quotes ('\"'), backslashes ('\\'), newlines ('\n'), and
088     * the pattern characters ('\?' and '\*').  The quotes and backslashes
089     * (after expansion) are considered part of the value.
090     * </p>
091     * <p>
092     * Both quoted and unquoted values may contain the wildcard characters
093     * '?' and '*'.  A name with at least one value containing a wildcard
094     * character is known as a <emph>property value pattern</emph>.  A
095     * name is generally a <emph>property pattern</emph> if it is either
096     * a <emph>property list pattern</emph> or <emph>property value pattern</emph>.
097     * </p>
098     * <p>
099     * Spaces are maintained within the different parts of the name.  Thus,
100     * '<code>domain: key1 = value1 </code>' has a key ' key1 ' with value
101     * ' value1 '.  Newlines are disallowed, except where escaped in quoted
102     * values.
103     * </p> 
104     *
105     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
106     * @since 1.5
107     */
108    public class ObjectName
109      implements Serializable, QueryExp
110    {
111    
112      private static final long serialVersionUID = 1081892073854801359L;
113    
114      /**
115       * The wildcard {@link ObjectName} {@code "*:*"}
116       *
117       * @since 1.6
118       */
119      public static final ObjectName WILDCARD;
120    
121      /**
122       * The domain of the name.
123       */
124      private transient String domain;
125    
126      /**
127       * The properties, as key-value pairs.
128       */
129      private transient TreeMap<String,String> properties;
130    
131      /**
132       * The properties as a string (stored for ordering).
133       */
134      private transient String propertyListString;
135    
136      /**
137       * True if this object name is a property list pattern.
138       */
139      private transient boolean propertyListPattern;
140    
141      /**
142       * True if this object name is a property value pattern.
143       */
144      private transient boolean propertyValuePattern;
145    
146      /**
147       * The management server associated with this object name.
148       */
149      private transient MBeanServer server;
150    
151      /**
152       * Static initializer to set up the wildcard.
153       */
154      static
155      {
156        try
157          {
158            WILDCARD = new ObjectName("");
159          }
160        catch (MalformedObjectNameException e)
161          {
162            throw (InternalError) (new InternalError("A problem occurred " +
163                                                     "initializing the ObjectName " +
164                                                     "wildcard.").initCause(e));
165          }
166      }
167    
168      /**
169       * Constructs an {@link ObjectName} instance from the given string,
170       * which should be of the form
171       * &lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;.  &lt;domain&gt;
172       * represents the domain section of the name.  &lt;properties&gt;
173       * represents the key-value pairs, as returned by {@link
174       * #getKeyPropertyListString()}.  &lt;wild&gt; is the optional
175       * asterisk present in the property list.  If the string doesn't
176       * represent a property pattern, it will be empty.  If it does,
177       * it will be either ',*' or '*', depending on whether other
178       * properties are present or not, respectively.
179       *
180       * @param name the string to use to construct this instance.
181       * @throws MalformedObjectNameException if the string is of the
182       *                                      wrong format.
183       * @throws NullPointerException if <code>name</code> is
184       *                              <code>null</code>.
185       */
186      public ObjectName(String name)
187        throws MalformedObjectNameException
188      {
189        if (name.length() == 0)
190          name = "*:*";
191        parse(name);
192      }
193    
194      /**
195       * Parse the name in the same form as the constructor.  Used by
196       * readObject().
197       */
198      private void parse(String name)
199        throws MalformedObjectNameException
200      {
201        int domainSep = name.indexOf(':');
202        if (domainSep == -1)
203          throw new MalformedObjectNameException("No domain separator was found.");
204        domain = name.substring(0, domainSep);
205        String rest = name.substring(domainSep + 1);
206        properties = new TreeMap<String,String>();
207        String[] pairs = rest.split(",");
208        if (pairs.length == 0 && !isPattern())
209          throw new MalformedObjectNameException("A name that is not a " +
210                                                 "pattern must contain at " +
211                                                 "least one key-value pair.");
212        propertyListString = "";
213        for (int a = 0; a < pairs.length; ++a)
214          {
215            if (pairs[a].equals("*"))
216              {
217                if (propertyListPattern)
218                  throw new MalformedObjectNameException("Multiple wildcards " +
219                                                         "in properties.");
220                propertyListPattern = true;
221                continue;
222              }
223            int sep = pairs[a].indexOf('=');
224            if (sep == -1)
225              throw new MalformedObjectNameException("A key must be " +
226                                                     "followed by a value.");
227            String key = pairs[a].substring(0, sep);
228            if (properties.containsKey(key))
229              throw new MalformedObjectNameException("The same key occurs " +
230                                                     "more than once.");
231            String value = pairs[a].substring(sep+1);
232            properties.put(key, value);
233            propertyListString += key + "=" + value + ",";
234          }
235        if (propertyListString.length() > 0)
236          propertyListString =
237            propertyListString.substring(0, propertyListString.length() - 1);
238        checkComponents();
239      }
240    
241      /**
242       * Constructs an {@link ObjectName} instance using the given
243       * domain and the one specified property.
244       *
245       * @param domain the domain part of the object name.
246       * @param key the key of the property.
247       * @param value the value of the property.
248       * @throws MalformedObjectNameException the domain, key or value
249       *                                      contains an illegal
250       *                                      character or the value
251       *                                      does not follow the quoting
252       *                                      specifications.
253       * @throws NullPointerException if one of the parameters is
254       *                              <code>null</code>.
255       */
256      public ObjectName(String domain, String key, String value)
257        throws MalformedObjectNameException
258      {
259        this.domain = domain;
260        properties = new TreeMap<String,String>();
261        properties.put(key, value);
262        checkComponents();
263      }
264    
265      /**
266       * Constructs an {@link ObjectName} instance using the given
267       * domain and properties.
268       *
269       * @param domain the domain part of the object name.
270       * @param properties the key-value property pairs.
271       * @throws MalformedObjectNameException the domain, a key or a value
272       *                                      contains an illegal
273       *                                      character or a value
274       *                                      does not follow the quoting
275       *                                      specifications.
276       * @throws NullPointerException if one of the parameters is
277       *                              <code>null</code>.
278       */
279      public ObjectName(String domain, Hashtable<String,String> properties)
280        throws MalformedObjectNameException
281      {
282        this.domain = domain;
283        this.properties = new TreeMap<String,String>();
284        this.properties.putAll(properties);
285        checkComponents();
286      }
287    
288      /**
289       * Checks the legality of the domain and the properties.
290       *
291       * @throws MalformedObjectNameException the domain, a key or a value
292       *                                      contains an illegal
293       *                                      character or a value
294       *                                      does not follow the quoting
295       *                                      specifications.
296       */
297      private void checkComponents()
298        throws MalformedObjectNameException
299      {
300        if (domain.indexOf(':') != -1)
301          throw new MalformedObjectNameException("The domain includes a ':' " +
302                                                 "character.");
303        if (domain.indexOf('\n') != -1)
304          throw new MalformedObjectNameException("The domain includes a newline " +
305                                                 "character.");
306        char[] keychars = new char[] { '\n', ':', ',', '*', '?', '=' };
307        char[] valchars = new char[] { '\n', ':', ',', '=' };
308        Iterator i = properties.entrySet().iterator();
309        while (i.hasNext())
310          {
311            Map.Entry entry = (Map.Entry) i.next();
312            String key = (String) entry.getKey();
313            for (int a = 0; a < keychars.length; ++a)
314              if (key.indexOf(keychars[a]) != -1)
315                throw new MalformedObjectNameException("A key contains a '" +
316                                                       keychars[a] + "' " +
317                                                       "character.");
318            String value = (String) entry.getValue();
319            int quote = value.indexOf('"');
320            if (quote == 0)
321              {
322                try
323                  {
324                    unquote(value);
325                  }
326                catch (IllegalArgumentException e)
327                  {
328                    throw (MalformedObjectNameException)
329                      new MalformedObjectNameException("The quoted value is " +
330                                                       "invalid.").initCause(e);
331                  }
332              }
333            else if (quote != -1)
334              throw new MalformedObjectNameException("A value contains " +
335                                                     "a '\"' character.");
336            else
337              {
338                for (int a = 0; a < valchars.length; ++a)
339                  if (value.indexOf(valchars[a]) != -1)
340                    throw new MalformedObjectNameException("A value contains " +
341                                                           "a '" + valchars[a] + "' " +
342                                                           "character.");
343                
344              }
345            if (value.indexOf('*') != -1 || value.indexOf('?') != -1)
346              propertyValuePattern = true;
347          }
348      }
349    
350      /**
351       * <p>
352       * Attempts to find a match between this name and the one supplied.
353       * The following criteria are used:
354       * </p>
355       * <ul>
356       * <li>If the supplied name is a pattern, <code>false</code> is
357       * returned.</li>
358       * <li>If this name is a pattern, this method returns <code>true</code>
359       * if the supplied name matches the pattern.</li>
360       * <li>If this name is not a pattern, the result of
361       * <code>equals(name)</code> is returned.
362       * </ul>
363       *
364       * @param name the name to find a match with.
365       * @return true if the name either matches this pattern or is
366       *         equivalent to this name under the criteria of
367       *         {@link #equals(java.lang.Object)}
368       * @throws NullPointerException if <code>name</code> is <code>null</code>.
369       */
370      public boolean apply(ObjectName name)
371      {
372        if (name.isPattern())
373          return false;
374    
375        if (!isPattern())
376          return equals(name);
377    
378        if (isDomainPattern())
379          {
380            if (!domainMatches(domain, 0, name.getDomain(), 0))
381              return false;
382          }
383        else
384          {
385            if (!domain.equals(name.getDomain()))
386              return false;
387          }
388    
389        if (isPropertyPattern())
390          {
391            Hashtable oProps = name.getKeyPropertyList();
392            Iterator i = properties.entrySet().iterator();
393            while (i.hasNext())
394              {
395                Map.Entry entry = (Map.Entry) i.next();
396                String key = (String) entry.getKey();
397                if (!(oProps.containsKey(key)))
398                  return false;
399                String val = (String) entry.getValue();
400                if (!(val.equals(oProps.get(key))))
401                  return false;
402              }
403          }
404        else
405          {
406            if (!getCanonicalKeyPropertyListString().equals
407                (name.getCanonicalKeyPropertyListString()))
408              return false;
409          }
410        return true;
411      }
412    
413      /**
414       * Returns true if the domain matches the pattern.
415       *
416       * @param pattern the pattern to match against.
417       * @param patternindex the index into the pattern to start matching.
418       * @param domain the domain to match.
419       * @param domainindex the index into the domain to start matching.
420       * @return true if the domain matches the pattern.
421       */
422      private static boolean domainMatches(String pattern, int patternindex,
423                                           String domain, int domainindex)
424      {
425        while (patternindex < pattern.length())
426          {
427            char c = pattern.charAt(patternindex++);
428            
429            if (c == '*')
430              {
431                for (int i = domain.length(); i >= domainindex; i--)
432                  {
433                    if (domainMatches(pattern, patternindex, domain, i))
434                      return true;
435                  }
436                return false;
437              }
438    
439            if (domainindex >= domain.length())
440              return false;
441            
442            if (c != '?' && c != domain.charAt(domainindex))
443              return false;
444    
445            domainindex++;
446          }
447        return true;
448      }
449    
450      /**
451       * Compares the specified object with this one.  The two
452       * are judged to be equivalent if the given object is an
453       * instance of {@link ObjectName} and has an equal canonical
454       * form (as returned by {@link #getCanonicalName()}).
455       *
456       * @param obj the object to compare with this.
457       * @return true if the object is also an {@link ObjectName}
458       *         with an equivalent canonical form.
459       */
460      public boolean equals(Object obj)
461      {
462        if (obj instanceof ObjectName)
463          {
464            ObjectName o = (ObjectName) obj;
465            return getCanonicalName().equals(o.getCanonicalName());
466          }
467        return false;
468      }
469    
470      /**
471       * Returns the property list in canonical form.  The keys
472       * are ordered using the lexicographic ordering used by
473       * {@link java.lang.String#compareTo(java.lang.Object)}.
474       * 
475       * @return the property list, with the keys in lexicographic
476       *         order.
477       */
478      public String getCanonicalKeyPropertyListString()
479      {
480        StringBuilder builder = new StringBuilder();
481        Iterator i = properties.entrySet().iterator();
482        while (i.hasNext())
483          {
484            Map.Entry entry = (Map.Entry) i.next();
485            builder.append(entry.getKey() + "=" + entry.getValue());
486            if (i.hasNext())
487              builder.append(",");
488          }
489        return builder.toString();
490      }
491    
492      /**
493       * <p>
494       * Returns the name as a string in canonical form.  More precisely,
495       * this returns a string of the format 
496       * &lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;.  &lt;properties&gt;
497       * is the same value as returned by
498       * {@link #getCanonicalKeyPropertyListString()}.  &lt;wild&gt;
499       * is:
500       * </p>
501       * <ul>
502       * <li>an empty string, if the object name is not a property pattern.</li>
503       * <li>'*' if &lt;properties&gt; is empty.</li>
504       * <li>',*' if there is at least one key-value pair.</li>
505       * </ul>
506       * 
507       * @return the canonical string form of the object name, as specified
508       *         above.
509       */
510      public String getCanonicalName()
511      {
512        return domain + ":" +
513          getCanonicalKeyPropertyListString() +
514          (isPropertyPattern() ? (properties.isEmpty() ? "*" : ",*") : "");
515      }
516    
517      /**
518       * Returns the domain part of the object name.
519       *
520       * @return the domain.
521       */
522      public String getDomain()
523      {
524        return domain;
525      }
526    
527      /**
528       * Returns an {@link ObjectName} instance that is substitutable for the
529       * one given.  The instance returned may be a subclass of {@link ObjectName},
530       * but is not guaranteed to be of the same type as the given name, if that
531       * should also turn out to be a subclass.  The returned instance may or may
532       * not be equivalent to the one given.  The purpose of this method is to provide
533       * an instance of {@link ObjectName} with a well-defined semantics, such as may
534       * be used in cases where the given name is not trustworthy.
535       *
536       * @param name the {@link ObjectName} to provide a substitute for.
537       * @return a substitute for the given name, which may or may not be a subclass
538       *         of {@link ObjectName}.  In either case, the returned object is
539       *         guaranteed to have the semantics defined here.
540       * @throws NullPointerException if <code>name</code> is <code>null</code>.
541       */
542      public static ObjectName getInstance(ObjectName name)
543      {
544        try
545          {
546            return new ObjectName(name.getCanonicalName());
547          }
548        catch (MalformedObjectNameException e)
549          {
550            throw (InternalError)
551              (new InternalError("The canonical name of " +
552                                 "the given name is invalid.").initCause(e));
553          }
554      }
555    
556      /**
557       * Returns an {@link ObjectName} instance for the specified name, represented
558       * as a {@link java.lang.String}.  The instance returned may be a subclass of
559       * {@link ObjectName} and may or may not be equivalent to earlier instances
560       * returned by this method for the same string.
561       *
562       * @param name the {@link ObjectName} to provide an instance of.
563       * @return a instance for the given name, which may or may not be a subclass
564       *         of {@link ObjectName}.  
565       * @throws MalformedObjectNameException the domain, a key or a value
566       *                                      contains an illegal
567       *                                      character or a value
568       *                                      does not follow the quoting
569       *                                      specifications.
570       * @throws NullPointerException if <code>name</code> is <code>null</code>.
571       */
572      public static ObjectName getInstance(String name)
573        throws MalformedObjectNameException
574      {
575        return new ObjectName(name);
576      }
577    
578      /**
579       * Returns an {@link ObjectName} instance for the specified name, represented
580       * as a series of {@link java.lang.String} objects for the domain and a single
581       * property, as a key-value pair.  The instance returned may be a subclass of
582       * {@link ObjectName} and may or may not be equivalent to earlier instances
583       * returned by this method for the same parameters.
584       *
585       * @param domain the domain part of the object name.
586       * @param key the key of the property.
587       * @param value the value of the property.
588       * @return a instance for the given name, which may or may not be a subclass
589       *         of {@link ObjectName}.  
590       * @throws MalformedObjectNameException the domain, a key or a value
591       *                                      contains an illegal
592       *                                      character or a value
593       *                                      does not follow the quoting
594       *                                      specifications.
595       * @throws NullPointerException if <code>name</code> is <code>null</code>.
596       */
597      public static ObjectName getInstance(String domain, String key, String value)
598        throws MalformedObjectNameException
599      {
600        return new ObjectName(domain, key, value);
601      }
602    
603      /**
604       * Returns an {@link ObjectName} instance for the specified name, represented
605       * as a domain {@link java.lang.String} and a table of properties.  The
606       * instance returned may be a subclass of {@link ObjectName} and may or may
607       * not be equivalent to earlier instances returned by this method for the
608       * same string.
609       *
610       * @param domain the domain part of the object name.
611       * @param properties the key-value property pairs.
612       * @return a instance for the given name, which may or may not be a subclass
613       *         of {@link ObjectName}.  
614       * @throws MalformedObjectNameException the domain, a key or a value
615       *                                      contains an illegal
616       *                                      character or a value
617       *                                      does not follow the quoting
618       *                                      specifications.
619       * @throws NullPointerException if <code>name</code> is <code>null</code>.
620       */
621      public static ObjectName getInstance(String domain,
622                                           Hashtable<String,String> properties)
623        throws MalformedObjectNameException
624      {
625        return new ObjectName(domain, properties);
626      }
627    
628      /**
629       * Returns the property value corresponding to the given key.
630       *
631       * @param key the key of the property to be obtained.
632       * @return the value of the specified property.
633       * @throws NullPointerException if <code>key</code> is <code>null</code>.
634       */
635      public String getKeyProperty(String key)
636      {
637        if (key == null)
638          throw new NullPointerException("Null key given in request for a value.");
639        return (String) properties.get(key);
640      }
641    
642      /**
643       * Returns the properties in a {@link java.util.Hashtable}.  The table
644       * contains each of the properties as keys mapped to their value.  The
645       * returned table is not unmodifiable, but changes made to it will not
646       * be reflected in the object name.
647       *
648       * @return a {@link java.util.Hashtable}, containing each of the object
649       *         name's properties.
650       */
651      public Hashtable<String,String> getKeyPropertyList()
652      {
653        return new Hashtable<String,String>(properties);
654      }
655    
656      /**
657       * Returns a {@link java.lang.String} representation of the property
658       * list.  If the object name was created using {@link
659       * ObjectName(String)}, then this string will contain the properties
660       * in the same order they were given in at creation.
661       * 
662       * @return the property list.
663       */
664      public String getKeyPropertyListString()
665      {
666        if (propertyListString != null)
667          return propertyListString;
668        return getCanonicalKeyPropertyListString();
669      }
670    
671      /**
672       * Returns a hash code for this object name.  This is calculated as the
673       * summation of the hash codes of the domain and the properties.
674       *
675       * @return a hash code for this object name.
676       */
677      public int hashCode()
678      {
679        return domain.hashCode() + properties.hashCode();
680      }
681    
682      /**
683       * Returns true if the domain of this object name is a pattern.
684       * This is the case if it contains one or more wildcard characters
685       * ('*' or '?').
686       *
687       * @return true if the domain is a pattern.
688       */
689      public boolean isDomainPattern()
690      {
691        return domain.contains("?") || domain.contains("*");
692      }
693    
694      /**
695       * Returns true if this is an object name pattern.  An object
696       * name pattern has a domain containing a wildcard character
697       * ('*' or '?') and/or a '*' in the list of properties.
698       * This method will return true if either {@link #isDomainPattern()}
699       * or {@link #isPropertyPattern()} does.
700       *
701       * @return true if this is an object name pattern.
702       */
703      public boolean isPattern()
704      {
705        return isDomainPattern() || isPropertyPattern();
706      }
707    
708      /**
709       * Returns true if this object name is a property list
710       * pattern, a property value pattern or both.
711       *
712       * @return true if the properties of this name contain a pattern.
713       * @see #isPropertyListPattern
714       * @see #isPropertyValuePattern
715       */
716      public boolean isPropertyPattern()
717      {
718        return propertyListPattern || propertyValuePattern;
719      }
720    
721      /**
722       * Returns true if this object name is a property list pattern.  This is
723       * the case if the list of properties contains an '*'.
724       *
725       * @return true if this is a property list pattern.
726       * @since 1.6
727       */
728      public boolean isPropertyListPattern()
729      {
730        return propertyListPattern;
731      }
732    
733      /**
734       * Returns true if this object name is a property value pattern.  This is
735       * the case if one of the values contains a wildcard character,
736       * '?' or '*'.
737       *
738       * @return true if this is a property value pattern.
739       * @since 1.6
740       */
741      public boolean isPropertyValuePattern()
742      {
743        return propertyValuePattern;
744      }
745    
746      /**
747       * Returns true if the value of the given key is a pattern.  This is
748       * the case if the value contains a wildcard character, '?' or '*'.
749       *
750       * @param key the key whose value should be checked.
751       * @return true if the value of the given key is a pattern.
752       * @since 1.6
753       * @throws NullPointerException if {@code key} is {@code null}.
754       * @throws IllegalArgumentException if {@code key} is not a valid
755       *                                  property.
756       */
757      public boolean isPropertyValuePattern(String key)
758      {
759        String value = getKeyProperty(key);
760        if (value == null)
761          throw new IllegalArgumentException(key + " is not a valid property.");
762        return value.indexOf('?') != -1 || value.indexOf('*') != -1;
763      }
764    
765      /**
766       * <p>
767       * Returns a quoted version of the supplied string.  The string may
768       * contain any character.  The resulting quoted version is guaranteed
769       * to be usable as the value of a property, so this method provides
770       * a good way of ensuring that a value is legal.
771       * </p>
772       * <p>
773       * The string is transformed as follows:
774       * </p>
775       * <ul>
776       * <li>The string is prefixed with an opening quote character, '"'.
777       * <li>For each character, s:
778       * <ul>
779       * <li>If s is a quote ('"'), it is replaced by a backslash
780       * followed by a quote.</li>
781       * <li>If s is a star ('*'), it is replaced by a backslash followed
782       * by a star.</li>
783       * <li>If s is a question mark ('?'), it is replaced by a backslash
784       * followed by a question mark.</li>
785       * <li>If s is a backslash ('\'), it is replaced by two backslashes.</li>
786       * <li>If s is a newline character, it is replaced by a backslash followed by
787       * a '\n'.</li>
788       * <li>Otherwise, s is used verbatim.
789       * </ul></li>
790       * <li>The string is terminated with a closing quote character, '"'.</li>
791       * </ul> 
792       * 
793       * @param string the string to quote.
794       * @return a quoted version of the supplied string.
795       * @throws NullPointerException if <code>string</code> is <code>null</code>.
796       */
797      public static String quote(String string)
798      {
799        StringBuilder builder = new StringBuilder();
800        builder.append('"');
801        for (int a = 0; a < string.length(); ++a)
802          {
803            char s = string.charAt(a);
804            switch (s)
805              {
806              case '"':
807                builder.append("\\\"");
808                break;
809              case '*':
810                builder.append("\\*");
811                break;
812              case '?':
813                builder.append("\\?");
814                break;
815              case '\\':
816                builder.append("\\\\");
817                break;
818              case '\n':
819                builder.append("\\\n");
820                break;
821              default:
822                builder.append(s);
823              }
824          }
825        builder.append('"');
826        return builder.toString();
827      }
828    
829      /**
830       * Changes the {@link MBeanServer} on which this query is performed.
831       *
832       * @param server the new server to use.
833       */
834      public void setMBeanServer(MBeanServer server)
835      {
836        this.server = server;
837      }
838    
839      /**
840       * Returns a textual representation of the object name.
841       *
842       * <p>The format is unspecified beyond that equivalent object
843       * names will return the same string from this method, but note
844       * that Tomcat depends on the string returned by this method
845       * being a valid textual representation of the object name and
846       * will fail to start if it is not.
847       *
848       * @return a textual representation of the object name.
849       */
850      public String toString()
851      {
852        return getCanonicalName();
853      }
854    
855    
856      /**
857       * Serialize this {@link ObjectName}.  The serialized
858       * form is the same as the string parsed by the constructor.
859       *
860       * @param out the output stream to write to.
861       * @throws IOException if an I/O error occurs.
862       */
863      private void writeObject(ObjectOutputStream out)
864        throws IOException
865      {
866        out.defaultWriteObject();
867        StringBuffer buffer = new StringBuffer(getDomain());
868        buffer.append(':');
869        String properties = getKeyPropertyListString();
870        buffer.append(properties);
871        if (isPropertyPattern())
872          {
873           if (properties.length() == 0)
874             buffer.append("*");
875           else
876             buffer.append(",*");
877          }
878        out.writeObject(buffer.toString());
879      }
880    
881      /**
882       * Reads the serialized form, which is that used
883       * by the constructor.
884       *
885       * @param in the input stream to read from.
886       * @throws IOException if an I/O error occurs.
887       */
888      private void readObject(ObjectInputStream in) 
889        throws IOException, ClassNotFoundException
890       {
891         in.defaultReadObject();
892         String objectName = (String)in.readObject();
893         try
894           {
895             parse(objectName);
896           }
897         catch (MalformedObjectNameException x)
898           {
899             throw new InvalidObjectException(x.toString());
900           }
901       }
902    
903    
904      /**
905       * Unquotes the supplied string.  The quotation marks are removed as
906       * are the backslashes preceding the escaped characters ('"', '?',
907       * '*', '\n', '\\').  A one-to-one mapping exists between quoted and
908       * unquoted values.  As a result, a string <code>s</code> should be
909       * equal to <code>unquote(quote(s))</code>.
910       *
911       * @param q the quoted string to unquote.
912       * @return the unquoted string.
913       * @throws NullPointerException if <code>q</code> is <code>null</code>.
914       * @throws IllegalArgumentException if the string is not a valid
915       *                                  quoted string i.e. it is not 
916       *                                  surrounded by quotation marks
917       *                                  and/or characters are not properly
918       *                                  escaped.
919       */
920      public static String unquote(String q)
921      {
922        if (q.charAt(0) != '"')
923          throw new IllegalArgumentException("The string does " +
924                                             "not start with a quote.");
925        if (q.charAt(q.length() - 1) != '"')
926          throw new IllegalArgumentException("The string does " +
927                                             "not end with a quote.");
928        StringBuilder builder = new StringBuilder();
929        for (int a = 1; a < (q.length() - 1); ++a)
930          {
931            char n = q.charAt(a);
932            if (n == '\\')
933              {
934                n = q.charAt(++a);
935                if (n != '"' && n != '?' && n != '*' &&
936                    n != 'n' && n != '\\')
937                  throw new IllegalArgumentException("Illegal escaped character: "
938                                                     + n);
939              }
940            else if (n == '"' || n == '\n') 
941              throw new IllegalArgumentException("Illegal character: " + n);
942            builder.append(n);
943          }
944    
945        return builder.toString();
946      }
947    
948    }