Author: jboynes Date: Mon Feb 21 13:19:07 2005 New Revision: 154723 URL: http://svn.apache.org/viewcvs?view=rev&rev=154723 Log: initial impl of GBeanName
Added: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/gbean/GBeanName.java geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/gbean/GBeanNameTest.java Added: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/gbean/GBeanName.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/gbean/GBeanName.java?view=auto&rev=154723 ============================================================================== --- geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/gbean/GBeanName.java (added) +++ geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/gbean/GBeanName.java Mon Feb 21 13:19:07 2005 @@ -0,0 +1,184 @@ +/** + * + * 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.Iterator; +import java.util.Map; +import java.util.Properties; + + +/** + * 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 Properties 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 sorted order. + * + * @param domain the domain + * @param props the properties used to qualify this name + */ + public GBeanName(String domain, Properties 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 Properties(props); + this.name = sortName(domain, props); + this.hashCode = domain.hashCode() + 37 * props.hashCode(); + } + + /** + * 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 Properties parseName(String name) { + if (name.endsWith(",")) { + throw new IllegalArgumentException("Missing last property pair"); + } + Properties props = new Properties(); + 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; + } + + private static String sortName(String domain, Map props) { + String[] names = (String[]) props.keySet().toArray(new String[props.size()]); + Arrays.sort(names); + StringBuffer buf = new StringBuffer(128); + buf.append(domain).append(':'); + buf.append(names[0]).append('=').append(props.get(names[0])); + for (int i = 1; i < names.length; i++) { + String name = names[i]; + buf.append(',').append(name).append('=').append(props.get(name)); + } + return buf.toString(); + } + + /** + * 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 + * @return true if this instance matches the pattern + */ + public boolean matches(String domain, Properties 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 sorted order. + * + * @return a readable name + */ + public String toString() { + return name; + } + + private Object readResolve() { + return new GBeanName(name); + } +} \ No newline at end of file Added: geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/gbean/GBeanNameTest.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/gbean/GBeanNameTest.java?view=auto&rev=154723 ============================================================================== --- geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/gbean/GBeanNameTest.java (added) +++ geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/gbean/GBeanNameTest.java Mon Feb 21 13:19:07 2005 @@ -0,0 +1,157 @@ +/** + * + * 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.util.Properties; +import java.util.HashSet; +import java.util.Set; +import java.rmi.MarshalledObject; + +import junit.framework.TestCase; + +/** + * @version $Rev$ $Date$ + */ +public class GBeanNameTest extends TestCase { + private Properties props; + + public void testPropertyConstruction() { + String domain = "testDomain"; + props.put("prop1", "value1"); + props.put("prop2", "value2"); + GBeanName name = new GBeanName(domain, props); + assertEquals("testDomain:prop1=value1,prop2=value2", name.toString()); + } + + public void testNameConstruction() { + GBeanName name = new GBeanName("testDomain:prop1=value1,prop2=value2"); + assertEquals("testDomain:prop1=value1,prop2=value2", name.toString()); + + name = new GBeanName("testDomain:prop2=value2,prop1=value1"); + assertEquals("testDomain:prop2=value2,prop1=value1", name.toString()); + } + + public void testMatches() { + GBeanName name = new GBeanName("testDomain:prop1=value1,prop2=value2"); + assertTrue(name.matches(null, null)); + assertTrue(name.matches(null, props)); + assertTrue(name.matches("testDomain", null)); + assertTrue(name.matches("testDomain", props)); + + assertFalse(name.matches("test", null)); + assertFalse(name.matches("test", props)); + + props.setProperty("prop1", "value2"); + assertFalse(name.matches("testDomain", props)); + props.setProperty("prop1", "value1"); + assertTrue(name.matches("testDomain", props)); + props.setProperty("prop2", "value2"); + assertTrue(name.matches("testDomain", props)); + props.setProperty("prop3", "value3"); + assertFalse(name.matches("testDomain", props)); + } + + public void testInvalidNames() { + try { + new GBeanName(null); + fail(); + } catch (NullPointerException e) { + } + try { + new GBeanName(""); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("foo=bar"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x: "); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:foo"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:x=x,foo"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:x=x,").toString(); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:x=x, ").toString(); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:,x=x"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:x=x,,y=y"); + fail(); + } catch (IllegalArgumentException e) { + } + try { + new GBeanName("x:x=x,x=x"); + fail(); + } catch (IllegalArgumentException e) { + } + } + + public void testEquals() { + GBeanName name = new GBeanName("testDomain:prop1=value1,prop2=value2"); + assertEquals(name, name); + assertEquals(new GBeanName("testDomain:prop2=value2,prop1=value1"), name); + assertFalse(name.equals(new GBeanName("foo:prop1=value1,prop2=value2"))); + assertFalse(name.equals(new GBeanName("testDomain:prop1=value1"))); + assertFalse(name.equals(new GBeanName("testDomain:prop2=value2"))); + assertFalse(name.equals(new GBeanName("testDomain:prop2=value2"))); + assertFalse(name.equals(new GBeanName("testDomain:prop1=value1,prop2=value2,prop3=value3"))); + + Set set = new HashSet(); + set.add(name); + set.add(name); + assertEquals(1, set.size()); + } + + public void testSerialization() throws Exception { + GBeanName name = new GBeanName("testDomain:prop1=value1,prop2=value2"); + MarshalledObject o = new MarshalledObject(name); + GBeanName name2 = (GBeanName) o.get(); + assertEquals(name, name2); + } + + protected void setUp() throws Exception { + props = new Properties(); + } +}