/** * * Copyright 2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.geronimo.gbean; import java.io.Serializable; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; /** * Class that represents the name for a GBean. * A name is comprised of a domain combined with one or more properties. * The domain is a fixed base name, properties qualify that as necessary. * Two names are equal if they have the same name and the same properties. * The String representation of a name can be written as domain:key1=value1,key2=value2,... * Values are case sensitive, spaces are significant and there is no escaping mechanism; * this is intended to be fast rather than allow lax syntax. * * @version $Rev$ $Date$ */ public final class GBeanName implements Serializable { private static final long serialVersionUID = 8571821054715922993L; /** * Original name preserved; used for toString and Serialized form */ private final String name; private final transient String domain; private final transient HashMap props; private final transient int hashCode; /** * Construct a GBeanName by combining a domain with explicit properties. * The string representation of this name is generated by combining the properties in iteration order. * * @param domain the domain * @param props the properties used to qualify this name; a Map<String,String> */ public GBeanName(String domain, Map props) { if (domain == null) { throw new IllegalArgumentException("domain is null"); } else if (props == null) { throw new IllegalArgumentException("props is null"); } else if (props.isEmpty()) { throw new IllegalArgumentException("props is empty"); } this.domain = domain; this.props = new HashMap(props); this.name = buildName(domain, props); this.hashCode = domain.hashCode() + 37 * props.hashCode(); } private static String buildName(String domain, Map props) { StringBuffer buf = new StringBuffer(128); buf.append(domain).append(':'); Iterator i = props.entrySet().iterator(); Map.Entry entry = (Map.Entry) i.next(); buf.append(entry.getKey()).append('=').append(entry.getValue()); while (i.hasNext()) { entry = (Map.Entry) i.next(); buf.append(',').append(entry.getKey()).append('=').append(entry.getValue()); } return buf.toString(); } /** * Construct a GBeanName by parsing a string. * * @param name the name to parse */ public GBeanName(String name) { int idx = name.indexOf(':'); if (idx == -1) { throw new IllegalArgumentException("Missing ':' for domain: " + name); } this.name = name; this.domain = name.substring(0, idx); this.props = parseName(name.substring(idx + 1)); this.hashCode = domain.hashCode() + 37 * props.hashCode(); } private static HashMap parseName(String name) { if (name.endsWith(",")) { throw new IllegalArgumentException("Missing last property pair"); } HashMap props = new HashMap(); String[] pairs = name.split(","); for (int i = 0; i < pairs.length; i++) { String pair = pairs[i]; int idx = pair.indexOf('='); if (idx == -1) { throw new IllegalArgumentException("Invalid property pair: " + pair); } String key = pair.substring(0, idx); String value = pair.substring(idx + 1); if (props.put(key, value) != null) { throw new IllegalArgumentException("Duplicate property: " + key); } } return props; } /** * Determine if this name matches the supplied pattern. * This performs a fast but simplistic pattern match which is true if: * <ul> * <li>The domains are equal</li> * <li>If this instance has all the supplied properties with equal values</li> * <ul> * A null domain and a null or empty properties object are considered wildcards * and always match; in other words GBeanName.match(null, new Properties()) will * always evaluate to true. * * @param domain the domain to match * @param pattern the set properties to match; a Map<String,String> * @return true if this instance matches the pattern */ public boolean matches(String domain, Map pattern) { if (domain != null) { if (!this.domain.equals(domain)) { return false; } } if (pattern != null && !pattern.isEmpty()) { for (Iterator i = pattern.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String key = (String) entry.getKey(); String ourValue = (String) props.get(key); if (ourValue == null || !ourValue.equals(entry.getValue())) { return false; } } } return true; } /** * Test for equality. * This instance will be equal if the supplied object is a GBeanName with * equal domain and properties. * * @param obj * @return */ public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof GBeanName == false) return false; final GBeanName other = (GBeanName) obj; return this.domain.equals(other.domain) && this.props.equals(other.props); } public int hashCode() { return hashCode; } /** * Return a human readable version of this GBeanName. If the instance was created * by parsing a String, this will be the supplied value; it it was created by * supplying properties, the name will contain properties in an unspecified order. * * @return a readable name */ public String toString() { return name; } /** * Return a String representation of ths GBeanName. * The format will be <domain> ':' <key> '=' <value> ( ',' <key> '=' <value> )* * Keys are appended in the order determined by the supplied Comparator. * * @param keySorter the Comparator to use to order the keys. * @return a String representation of this GBean */ public String toString(Comparator keySorter) { String[] keyList = (String[]) props.keySet().toArray(new String[props.keySet().size()]); Arrays.sort(keyList, keySorter); StringBuffer buf = new StringBuffer(128); buf.append(domain).append(':'); String key = keyList[0]; buf.append(key).append('=').append(props.get(key)); for (int i = 1; i < keyList.length; i++) { key = keyList[i]; buf.append(',').append(key).append('=').append(props.get(key)); } return buf.toString(); } private Object readResolve() { return new GBeanName(name); } // utility methods to support conversion from ObjectName to GBeanName /** * @deprecated */ public ObjectName getObjectName() throws MalformedObjectNameException { return new ObjectName(domain, new Hashtable(props)); } /** * @deprecated */ public GBeanName(ObjectName name) { this.name = name.toString(); this.domain = name.getDomain(); this.props = new HashMap(name.getKeyPropertyList()); this.hashCode = domain.hashCode() + 37 * props.hashCode(); } }