From c00af1f895375fe858dcf7ae396907fc780f04c5 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 12 May 2008 09:29:19 +0000 Subject: [PATCH] Added SetterConfiguration class for configuring how setters correspond to required interfaces. --- .../system/adapters/ParameterValues.java | 35 ++- .../system/adapters/SetterConfiguration.java | 211 ++++++++++++++++++ .../adapters/SetterConfigurationTest.java | 183 +++++++++++++++ .../java/org/wamblee/system/adapters/X5.java | 38 ++++ .../java/org/wamblee/system/adapters/X6.java | 42 ++++ .../core/RequiredInterfaceComparator.java | 28 +++ 6 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java create mode 100644 system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java create mode 100644 system/general/src/test/java/org/wamblee/system/adapters/X5.java create mode 100644 system/general/src/test/java/org/wamblee/system/adapters/X6.java create mode 100644 system/general/src/test/java/org/wamblee/system/core/RequiredInterfaceComparator.java diff --git a/system/general/src/main/java/org/wamblee/system/adapters/ParameterValues.java b/system/general/src/main/java/org/wamblee/system/adapters/ParameterValues.java index 609f9d25..17789818 100644 --- a/system/general/src/main/java/org/wamblee/system/adapters/ParameterValues.java +++ b/system/general/src/main/java/org/wamblee/system/adapters/ParameterValues.java @@ -29,6 +29,7 @@ import org.wamblee.system.core.Scope; * @author Erik Brakkee */ public class ParameterValues { + private String[] _names; private Class[] _types; private ValueProvider[] _values; @@ -39,10 +40,32 @@ public class ParameterValues { * @param aClass Class to construct. */ public ParameterValues(Class[] aTypes) { + _names = new String[aTypes.length]; + for (int i = 0; i < aTypes.length; i++) { + _names[i] = "arg" + i; + } _types = aTypes; resetValues(); } + /** + * Constructs the configuration. By default no constructor is selected and + * one of {@link #select(Class...)} or + * {@link #greedy()} must be called. + * @param aNames Names of the arguments. + * @param aClass Class to construct. + */ + public ParameterValues(String[] aNames, Class[] aTypes) { + assert aNames.length == aTypes.length; + _names = aNames; + _types = aTypes; + resetValues(); + } + + /** + * The types of the parameter values. + * @return Types. + */ public Class[] getTypes() { return _types; } @@ -74,10 +97,15 @@ public class ParameterValues { _values = new ValueProvider[_types.length]; for (int i = 0; i < _values.length; i++) { _values[i] = new RequiredInterfaceProvider(new DefaultRequiredInterface( - "arg" + i, _types[i])); + _names[i], _types[i])); } } + /** + * Gets the required interfaces to provide values that are not provided + * in another way. + * @return Required interfaces. + */ public List getRequiredInterfaces() { List result = new ArrayList(); for (ValueProvider provider: _values) { @@ -88,6 +116,11 @@ public class ParameterValues { return result; } + /** + * Returns the values to use in the given scope. + * @param aScope Scope within which to retrieve the values. + * @return Values. + */ public Object[] values(Scope aScope) { Object[] values = new Object[_values.length]; for (int i = 0; i < _values.length; i++) { diff --git a/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java b/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java new file mode 100644 index 00000000..83992d38 --- /dev/null +++ b/system/general/src/main/java/org/wamblee/system/adapters/SetterConfiguration.java @@ -0,0 +1,211 @@ +/* + * Copyright 2008 the original author or authors. + * + * 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.wamblee.system.adapters; + +import java.awt.CompositeContext; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.wamblee.collections.CollectionFilter; +import org.wamblee.conditions.Condition; +import org.wamblee.conditions.FixedCondition; +import org.wamblee.general.Pair; +import org.wamblee.system.core.DefaultProvidedInterface; +import org.wamblee.system.core.DefaultRequiredInterface; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.core.Scope; +import org.wamblee.system.core.SystemAssemblyException; + +/** + * Represents the configuration for exposing the setters of a class as required + * interfaces. + * + * @author Erik Brakkee + */ +public class SetterConfiguration { + + private Class _class; + + private List> _setters; + + /** + * Constructs the setter configuration. By default all setters are added. + * + * @param aClass + * Class which is being configured. + */ + public SetterConfiguration(Class aClass) { + _class = aClass; + _setters = getAllSetters(); + } + + /** + * Removes all setters. + * + * @return Reference to the current object to allow call chaining. + */ + public SetterConfiguration clear() { + _setters.clear(); + return this; + } + + /** + * Removes a setter from the set of methods. + * + * @param aName + * Name of the setter to remove (without the "set" prefix). + * @return Reference to the current object to allow call chaining. + */ + public SetterConfiguration remove(String aName) { + final String name = createSetterName(aName); + List> setters = + new ArrayList>(); + CollectionFilter.filter(_setters, setters, new Condition>() { + @Override + public boolean matches(Pair aObject) { + return !aObject.getFirst().getName().equals(name); + } + + }); + if ( _setters.size() == setters.size()) { + throw new IllegalArgumentException("No setter configured by the name of '" + aName + "'"); + } + _setters = setters; + return this; + } + + /** + * Creates the name of a setter based on the name of the setter without + * the "set" prefix. + * @param aName Setter name. + * @return Setter name. + */ + private String createSetterName(String aName) { + return "set" + aName.substring(0, 1).toUpperCase() + + aName.substring(1); + } + + /** + * Adds a given setter name to the setters. + * @param aName + * @return Reference to the current object to allow call chaining. + */ + public SetterConfiguration add(String aName) { + final String name = createSetterName(aName); + int oldlen = _setters.size(); + CollectionFilter.filter(getAllSetters(), _setters, + new Condition>() { + @Override + public boolean matches(Pair aObject) { + return aObject.getFirst().getName().equals(name); + } + + }); + if ( _setters.size() == oldlen) { + throw new IllegalArgumentException("No setter found for '" + aName + "'"); + } + return this; + } + + /** + * Gets all setters for the current class. + * @return List of all setters. + */ + private List> getAllSetters() { + List> result = + new ArrayList>(); + for (Method method : _class.getMethods()) { + if (method.getName().startsWith("set") + && method.getParameterTypes().length == 1) { + String name = getSetterName(method); + result.add(new Pair(method, + new ParameterValues( + new String[] { name }, new Class[] { method.getParameterTypes()[0] }))); + } + } + return result; + } + + /** + * Gets the required interfaces based on the configured setteres. + * @return List of required interfaces. + */ + public List getRequiredInterfaces() { + List result = new ArrayList(); + for (Pair method: _setters) { + result.addAll(method.getSecond().getRequiredInterfaces()); + } + return result; + } + + + /** + * Invokes all configured setters with the appropriate values. + * @param aScope Scope within which invocation takes place. + * @param aObject Object on which the invocation takes place. + */ + public void invoke(Scope aScope, Object aObject) { + if ( !_class.isInstance(aObject)) { + throw new IllegalArgumentException("Object '" + aObject + "' is not an instance of " + + _class.getName()); + } + for (Pair setter: _setters) { + Method method = setter.getFirst(); + ParameterValues values = setter.getSecond(); + + try { + method.invoke(aObject, values.values(aScope)); + } catch (IllegalAccessException e) { + throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e); + } catch (InvocationTargetException e) { + throw new SystemAssemblyException("Problem invoking " + method + " with " + values, e); + } + } + } + + /** + * Returns the parameter values for allowing detailed configuration of how + * parameter values are set. + * @param aSetter Setter name without the "set" prefix with the first + * character converted to lower case. + * @return Parameter values. + */ + public ParameterValues values(String aMethod) { + String name = createSetterName(aMethod); + for (Pair method: _setters) { + if ( method.getFirst().getName().equals(name) ) { + return method.getSecond(); + } + } + throw new IllegalArgumentException("No setter method '" + name + "' found"); + } + + /** + * Gets the setter name for a given setter method. This is the name of the + * setter without the "set" prefix and with the first character converted to + * lowercase. + * @param aMethod Method. + * @return Setter name. + */ + private String getSetterName(Method aMethod) { + String result = aMethod.getName().substring(3); + return result.substring(0,1).toLowerCase() + result.substring(1); + } + +} diff --git a/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java b/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java new file mode 100644 index 00000000..106da512 --- /dev/null +++ b/system/general/src/test/java/org/wamblee/system/adapters/SetterConfigurationTest.java @@ -0,0 +1,183 @@ +/* + * Copyright 2008 the original author or authors. + * + * 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.wamblee.system.adapters; + +import java.util.Collections; +import java.util.List; + +import org.wamblee.system.core.DefaultProvidedInterface; +import org.wamblee.system.core.ProvidedInterface; +import org.wamblee.system.core.RequiredInterface; +import org.wamblee.system.core.RequiredInterfaceComparator; +import org.wamblee.test.AssertionUtils; + +public class SetterConfigurationTest extends AdapterTestCase { + + public void testOneSetter() { + SetterConfiguration config = new SetterConfiguration(X5.class); + List required = config.getRequiredInterfaces(); + assertEquals(1, required.size()); + assertEquals("value", required.get(0).getName()); + + ProvidedInterface provided = new DefaultProvidedInterface("janse", String.class); + required.get(0).setProvider(provided); + provided.publish("hello", _scope); + + X5 obj = new X5(); + assertNull(obj.getValue()); + config.invoke(_scope, obj); + assertEquals("hello", obj.getValue()); + } + + public void testMultipleSetters() { + SetterConfiguration config = new SetterConfiguration(X6.class); + List required = config.getRequiredInterfaces(); + Collections.sort(required, new RequiredInterfaceComparator()); + assertEquals(2, required.size()); + assertEquals("host", required.get(0).getName()); + assertEquals("port", required.get(1).getName()); + + ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class); + required.get(0).setProvider(provided0); + provided0.publish("hello", _scope); + + ProvidedInterface provided1 = new DefaultProvidedInterface("port", Integer.class); + required.get(1).setProvider(provided1); + provided1.publish(10, _scope); + + X6 obj = new X6(); + assertNull(obj.getHost()); + assertNull(obj.getPort()); + + config.invoke(_scope, obj); + assertEquals("hello", obj.getHost()); + assertEquals(10, obj.getPort().intValue()); + } + + public void testInvokeWrongType() { + final SetterConfiguration config = new SetterConfiguration(X5.class); + List required = config.getRequiredInterfaces(); + assertEquals(1, required.size()); + assertEquals("value", required.get(0).getName()); + + ProvidedInterface provided = new DefaultProvidedInterface("janse", String.class); + required.get(0).setProvider(provided); + provided.publish("hello", _scope); + + final X6 obj = new X6(); + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + config.invoke(_scope, obj); + } + }, IllegalArgumentException.class); + } + + public void testSetExplicitValue() { + SetterConfiguration config = new SetterConfiguration(X5.class); + config.values("value").setValue(0, "bladibla"); + + List required = config.getRequiredInterfaces(); + assertEquals(0, required.size()); + + X5 obj = new X5(); + assertNull(obj.getValue()); + config.invoke(_scope, obj); + assertEquals("bladibla", obj.getValue()); + } + + public void testClear() { + SetterConfiguration config = new SetterConfiguration(X6.class); + config.clear(); + List required = config.getRequiredInterfaces(); + Collections.sort(required, new RequiredInterfaceComparator()); + assertEquals(0, required.size()); + + X6 obj = new X6(); + assertNull(obj.getHost()); + assertNull(obj.getPort()); + + config.invoke(_scope, obj); + + assertNull(obj.getHost()); + assertNull(obj.getPort()); + } + + public void testAdd() { + SetterConfiguration config = new SetterConfiguration(X6.class); + config.clear().add("host"); + List required = config.getRequiredInterfaces(); + Collections.sort(required, new RequiredInterfaceComparator()); + assertEquals(1, required.size()); + assertEquals("host", required.get(0).getName()); + + ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class); + required.get(0).setProvider(provided0); + provided0.publish("hello", _scope); + + X6 obj = new X6(); + assertNull(obj.getHost()); + assertNull(obj.getPort()); + + config.invoke(_scope, obj); + assertEquals("hello", obj.getHost()); + assertNull(obj.getPort()); + } + + public void testAddNonExisting() { + final SetterConfiguration config = new SetterConfiguration(X6.class); + config.clear(); + + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + config.add("bladibla"); + } + }, IllegalArgumentException.class); + } + + public void testRemove() { + SetterConfiguration config = new SetterConfiguration(X6.class); + config.remove("port"); + List required = config.getRequiredInterfaces(); + Collections.sort(required, new RequiredInterfaceComparator()); + assertEquals(1, required.size()); + assertEquals("host", required.get(0).getName()); + + ProvidedInterface provided0 = new DefaultProvidedInterface("janse", String.class); + required.get(0).setProvider(provided0); + provided0.publish("hello", _scope); + + X6 obj = new X6(); + assertNull(obj.getHost()); + assertNull(obj.getPort()); + + config.invoke(_scope, obj); + assertEquals("hello", obj.getHost()); + assertNull(obj.getPort()); + } + + public void testRemoveNonExisting() { + final SetterConfiguration config = new SetterConfiguration(X6.class); + + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + config.remove("bladibla"); + } + }, IllegalArgumentException.class); + } +} diff --git a/system/general/src/test/java/org/wamblee/system/adapters/X5.java b/system/general/src/test/java/org/wamblee/system/adapters/X5.java new file mode 100644 index 00000000..de3e0fb3 --- /dev/null +++ b/system/general/src/test/java/org/wamblee/system/adapters/X5.java @@ -0,0 +1,38 @@ +/* + * Copyright 2008 the original author or authors. + * + * 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.wamblee.system.adapters; + +public class X5 { + + private String _value; + + public X5() { + + } + + public void setValue(String aValue) { + AdapterTestCase.EVENT_TRACKER.eventOccurred("x5.setValue(" + aValue + ")"); + _value = aValue; + } + + public String getValue() { + return _value; + } + + public void doSomething() { + // Empty. + } +} \ No newline at end of file diff --git a/system/general/src/test/java/org/wamblee/system/adapters/X6.java b/system/general/src/test/java/org/wamblee/system/adapters/X6.java new file mode 100644 index 00000000..06b7b5f5 --- /dev/null +++ b/system/general/src/test/java/org/wamblee/system/adapters/X6.java @@ -0,0 +1,42 @@ +/* + * Copyright 2008 the original author or authors. + * + * 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.wamblee.system.adapters; + +public class X6 { + + private String _host; + private Integer _port; + + public X6() { + + } + + public Integer getPort() { + return _port; + } + + public void setPort(Integer aPort) { + _port = aPort; + } + + public String getHost() { + return _host; + } + + public void setHost(String aHost) { + _host = aHost; + } +} \ No newline at end of file diff --git a/system/general/src/test/java/org/wamblee/system/core/RequiredInterfaceComparator.java b/system/general/src/test/java/org/wamblee/system/core/RequiredInterfaceComparator.java new file mode 100644 index 00000000..404fec70 --- /dev/null +++ b/system/general/src/test/java/org/wamblee/system/core/RequiredInterfaceComparator.java @@ -0,0 +1,28 @@ +/* + * Copyright 2008 the original author or authors. + * + * 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.wamblee.system.core; + +import java.util.Comparator; + +public class RequiredInterfaceComparator implements + Comparator { + + @Override + public int compare(RequiredInterface aO1, RequiredInterface aO2) { + return aO1.getName().compareTo(aO2.getName()); + } + +} -- 2.31.1