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