(no commit message)
[utils] / system / general / src / main / java / org / wamblee / system / adapters / ConstructorConfiguration.java
diff --git a/system/general/src/main/java/org/wamblee/system/adapters/ConstructorConfiguration.java b/system/general/src/main/java/org/wamblee/system/adapters/ConstructorConfiguration.java
new file mode 100644 (file)
index 0000000..2725d03
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.wamblee.collections.CollectionFilter;
+import org.wamblee.conditions.Condition;
+import org.wamblee.system.core.RequiredInterface;
+import org.wamblee.system.core.Scope;
+import org.wamblee.system.core.SystemAssemblyException;
+
+public class ConstructorConfiguration {
+       private Class _class; 
+       private Constructor<?> _constructor;
+       private ParameterValues _values;
+       private boolean _publicOnly; 
+
+       /**
+        * Constructs the configuration. By default no constructor is selected and 
+        * one of {@link #select(Class...)} or 
+        * {@link #greedy()} must be called.  
+        * @param aClass Class to construct. 
+        */
+       public ConstructorConfiguration(Class aClass) {
+               _class = aClass; 
+               _constructor = null;  
+               _publicOnly = true; 
+       }
+       
+       /**
+        * Sets whether or no non public constructors are also considered.
+        * Reset the choice of a constructor. 
+        * @param aNonPublic
+        * @return
+        */
+       public ConstructorConfiguration setNonPublic(boolean aNonPublic) { 
+               _publicOnly = !aNonPublic;
+               _constructor = null;
+               _values = null;
+               return this; 
+       }
+       
+       /**
+        * Selects an explicit constructor.
+        * @param aTypes Arguments of the constructor.
+        * @return Return the injector to allow call chaining. 
+        */
+       public ConstructorConfiguration select(Class... aTypes) {
+               try {
+                       _constructor = _class.getDeclaredConstructor(aTypes); 
+               } catch (Exception e) {
+                       throw new SystemAssemblyException(e.getMessage(), e);
+               }
+               resetValues(); 
+               return this; 
+       }
+       
+       /**
+        * Selects the greediest constructor. 
+        * @return The injector to allow call chaining.
+        * @throws SystemAssemblyException if the greediest constructor cannot be uniquely 
+        *    identified.  
+        */
+       public ConstructorConfiguration greedy() { 
+               Constructor<?>[] declared = _class.getDeclaredConstructors();
+               if (declared.length == 0) {
+                       throw new SystemAssemblyException("Class '" + _class
+                                       + " is an interface, primitive type, or array");
+               }
+               int max = -1;
+               List<Constructor<?>> checked = new ArrayList<Constructor<?>>();
+               CollectionFilter.filter(Arrays.asList(declared), checked, 
+                       new Condition<Constructor<?>>() { 
+                       @Override
+                       public boolean matches(Constructor<?> aObject) {
+                               if ( !_publicOnly ) { 
+                                       return true; 
+                               } else { 
+                                       return Modifier.isPublic(aObject.getModifiers());
+                               }
+                       }
+               });
+               for (Constructor ctor : checked) {
+                       if (ctor.getParameterTypes().length > max) {
+                               max = ctor.getParameterTypes().length;
+                       }
+               }
+               final int max2 = max;
+               List<Constructor<?>> ctors = checked;
+               List<Constructor<?>> longest = new ArrayList<Constructor<?>>();
+               CollectionFilter.filter(ctors, longest,
+                               new Condition<Constructor<?>>() {
+                                       @Override
+                                       public boolean matches(Constructor<?> aObject) {
+                                               return aObject.getParameterTypes().length == max2;
+                                       }
+                               });
+               if (longest.size() > 1) {
+                       throw new SystemAssemblyException(
+                                       "Greediest constructor cannot be uniquely determined");
+               }
+           _constructor = longest.get(0);
+           resetValues();
+           return this; 
+       }
+       
+       public ParameterValues getParameters() {
+               getConstructor(); // initialize constructor if needed.
+               return _values; 
+       }
+
+       /**
+        * Resets the values. 
+        */
+       private void resetValues() {
+               _constructor.setAccessible(true);
+               _values = new ParameterValues(_constructor.getParameterTypes());        
+       }
+
+       /**
+        * Creates the object in the given scope. 
+        * @param aScope Scope containing required interfaces for this object. 
+        * @return object. 
+        */
+       public Object create(Scope aScope) {
+               Object[] values = _values.values(aScope);
+               try {
+                       return getConstructor().newInstance(values);
+               } catch (Exception e) {
+                       throw new SystemAssemblyException("Could not construct object "
+                                       + getConstructor() + " " + Arrays.asList(values), e);
+               }
+       }
+       
+       public List<RequiredInterface> getRequiredInterfaces() {
+               getConstructor(); // initialize constructor if needed.
+               return _values.getRequiredInterfaces(); 
+       }
+       
+       private Constructor getConstructor() { 
+               if (_constructor == null ) { 
+                       greedy(); 
+               }
+               return _constructor; 
+       }
+}