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 * <domain>:<properties><wild>. <domain>
172 * represents the domain section of the name. <properties>
173 * represents the key-value pairs, as returned by {@link
174 * #getKeyPropertyListString()}. <wild> 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 * <domain>:<properties><wild>. <properties>
497 * is the same value as returned by
498 * {@link #getCanonicalKeyPropertyListString()}. <wild>
499 * is:
500 * </p>
501 * <ul>
502 * <li>an empty string, if the object name is not a property pattern.</li>
503 * <li>'*' if <properties> 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 }