Commit 01ca372

mokhan <mokhan@ce5e1baf-6525-42e4-a1b2-857ea38da20a>
2009-04-30 21:28:56
git-svn-id: https://svn.xp-dev.com/svn/mokhan-mo.money@201 ce5e1baf-6525-42e4-a1b2-857ea38da20a
1 parent 3ed7da1
trunk/product/Gorilla.Commons.Infrastructure.ThirdParty/New/New.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq.Expressions;
+using Gorilla.Commons.Infrastructure.Castle.DynamicProxy;
+using Gorilla.Commons.Infrastructure.Container;
+using Gorilla.Commons.Utility;
+using Gorilla.Commons.Utility.Core;
+using Gorilla.Commons.Utility.Extensions;
+
+namespace Gorilla.Commons.Infrastructure.New
+{
+    public interface IContainerBuilder : IBuilder<IDependencyRegistry>
+    {
+        IExtendedRegistration<T> register<T>(Expression<Func<T>> func) where T : class;
+    }
+
+    public interface IExtendedRegistration
+    {
+        string pretty_print { get; }
+    }
+
+    public interface IExtendedRegistration<T> : IExtendedRegistration where T: class
+    {
+        IExtendedRegistration<T> as_singleton();
+        IExtendedRegistration<T> with_expiry(string dateTime);
+        IExtendedRegistration<T> with_proxy(IConfiguration<IProxyBuilder<T>> configuration);
+    }
+
+    public class SimpleContainerBuilder : IContainerBuilder
+    {
+        IDictionary<Type, IExtendedRegistration> registries = new Dictionary<Type, IExtendedRegistration>();
+
+        public IDependencyRegistry build()
+        {
+            throw new NotImplementedException();
+        }
+
+        public IExtendedRegistration<T> register<T>(Expression<Func<T>> func) where T : class
+        {
+            try
+            {
+                var reg = new ExtendedRegistration<T>(func);
+                registries.Add(typeof (T), reg);
+                return reg;
+            }
+            catch (ArgumentException e)
+            {
+                throw new TypeAlreadyRegisteredInContainerException(typeof (T), registries[typeof (T)].pretty_print);
+            }
+        }
+    }
+
+    public class ExtendedRegistration<T> : IExtendedRegistration<T> where T : class
+    {
+        Func<T> func;
+        public const string time_format = "dd/MM/yyyy HH:mm:ss";
+
+        public ExtendedRegistration(Expression<Func<T>> expression)
+        {
+            pretty_print = expression.ToString();
+            func = expression.Compile();
+        }
+
+        public string pretty_print { get; set; }
+
+        public IExtendedRegistration<T> as_singleton()
+        {
+            func = func.memorize<T>();
+            return this;
+        }
+
+        public IExtendedRegistration<T> with_expiry(string dateTime)
+        {
+            var theDateTime = DateTime.ParseExact(dateTime, time_format, CultureInfo.InvariantCulture);
+            var original_func = func;
+
+            func = () =>
+                       {
+                           if (Clock.now() > theDateTime)
+                           {
+                               throw new ObjectUsageHasExpiredException(original_func().GetType(), dateTime);
+                           }
+
+                           return original_func();
+                       };
+            return this;
+        }
+
+        public IExtendedRegistration<T> with_proxy(IConfiguration<IProxyBuilder<T>> configuration)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    public class TypeAlreadyRegisteredInContainerException : Exception
+    {
+        public TypeAlreadyRegisteredInContainerException(Type typeNotFound, string registration)
+            : base(build_message(typeNotFound, registration))
+        {
+        }
+
+        static string build_message(Type typeNotFound, string registration)
+        {
+            return string.Format("The type {0} has already been registered with {1} in the container",
+                                 typeNotFound.FullName, registration);
+        }
+    }
+
+    internal class ObjectUsageHasExpiredException : Exception
+    {
+        public ObjectUsageHasExpiredException(Type type, string dateTime) : base(build_message(type, dateTime))
+        {
+        }
+
+        static string build_message(Type type, string dateTime)
+        {
+            return string.Format("Cannot use {0} after {1}.", type.Name, dateTime);
+        }
+    }
+}
\ No newline at end of file
trunk/product/Gorilla.Commons.Infrastructure.ThirdParty/Gorilla.Commons.Infrastructure.ThirdParty.csproj
@@ -131,6 +131,7 @@
     <Compile Include="Castle\Windsor\WindsorContainerFactory.cs" />
     <Compile Include="Castle\Windsor\WindsorDependencyRegistry.cs" />
     <Compile Include="Castle\Windsor\WindsorDependencyRegistrySpecs.cs" />
+    <Compile Include="New\New.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Gorilla.Commons.Infrastructure\Gorilla.Commons.Infrastructure.csproj">
trunk/product/Gorilla.Commons.Utility/Core/Map.cs → trunk/product/Gorilla.Commons.Utility/Core/Mapper.cs
@@ -2,11 +2,11 @@ using System;
 
 namespace Gorilla.Commons.Utility.Core
 {
-    public class Map<Input, Output> : IMapper<Input, Output>
+    public class Mapper<Input, Output> : IMapper<Input, Output>
     {
         private readonly Converter<Input, Output> converter;
 
-        public Map(Converter<Input, Output> converter)
+        public Mapper(Converter<Input, Output> converter)
         {
             this.converter = converter;
         }
trunk/product/Gorilla.Commons.Utility/Gorilla.Commons.Utility.csproj
@@ -88,7 +88,7 @@
     <Compile Include="Core\IValueReturningVisitor.cs" />
     <Compile Include="Core\IVisitable.cs" />
     <Compile Include="Core\IVisitor.cs" />
-    <Compile Include="Core\Map.cs" />
+    <Compile Include="Core\Mapper.cs" />
     <Compile Include="Core\OrSpecification.cs" />
     <Compile Include="Core\OrSpecificationSpecs.cs" />
     <Compile Include="Core\PredicateSpecification.cs" />
trunk/product/MyMoney/boot/container/registration/mapping/DelegateTargetAction.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class DelegateTargetAction<Destination, Value> : ITargetAction<Destination, Value>
+    {
+        private readonly Action<Destination, Value> action;
+
+        public DelegateTargetAction(Action<Destination, Value> action)
+        {
+            this.action = action;
+        }
+
+        public void act_against(Destination destination, Value value)
+        {
+            action(destination, value);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/ExpressionSourceEvaluator.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class ExpressionSourceEvaluator<Input, Result> : ISourceEvaluator<Input, Result>
+    {
+        private readonly Expression<Func<Input, Result>> original_expression;
+        private Func<Input, Result> evaluator_expression;
+
+        public ExpressionSourceEvaluator(Expression<Func<Input, Result>> original_expression)
+        {
+            this.original_expression = original_expression;
+        }
+
+        public Result evaluate_against(Input input)
+        {
+            initialize_evaluator();
+            return evaluator_expression(input);
+        }
+
+        private void initialize_evaluator()
+        {
+            if (evaluator_expression != null) return;
+            evaluator_expression = original_expression.Compile();
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/FuncInitializationStep.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class FuncInitializationStep<Destination> : IMapInitializationStep<Destination>
+    {
+        private readonly Func<Destination> func;
+
+        public FuncInitializationStep(Func<Destination> func)
+        {
+            this.func = func;
+        }
+
+        public Destination initialize()
+        {
+            return func();
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/IMap.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Linq.Expressions;
+using Gorilla.Commons.Utility.Core;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface IMap<Input, Output> : IMapper<Input, Output>
+    {
+        void add(IMappingStep<Input, Output> step);
+
+        IMap<Input, Output> map<PropertyType>(Expression<Func<Input, PropertyType>> from,
+                                              Expression<Func<Output, PropertyType>> to);
+
+        IMap<Input, Output> initialize_mapping_using(Func<Output> initializer_expression);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/IMapInitializationStep.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface IMapInitializationStep<T>
+    {
+        T initialize();
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/IMappingStep.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface IMappingStep<Source, Destination>
+    {
+        void map(Source source, Destination destination);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/IMappingStepFactory.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface IMappingStepFactory
+    {
+        IMappingStep<Source, Destination> create_mapping_step_for<Source, Destination, PropertyType>(
+            Expression<Func<Source, PropertyType>> source_expression,
+            Expression<Func<Destination, PropertyType>> destination_expression);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/ImmutablePropertyException.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Reflection;
+using Gorilla.Commons.Utility.Extensions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class ImmutablePropertyException : Exception
+    {
+        public const string exception_message_format = "The property [{0}] on the target type [{1}] is immutable";
+
+        public ImmutablePropertyException(Type target, PropertyInfo property)
+            : base(exception_message_format.formatted_using(property.Name, target.Name))
+
+        {
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/IPropertyResolver.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface IPropertyResolver
+    {
+        PropertyInfo resolve_using<Input, PropertyType>(Expression<Func<Input, PropertyType>> expression);
+        PropertyInfo resolve_using(Type type, string property_name);
+        IEnumerable<PropertyInfo> all_properties_belonging_to(Type type);
+        IEnumerable<PropertyInfo> all_properties_belonging_to<T>();
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/ISourceEvaluator.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface ISourceEvaluator<Source, Result>
+    {
+        Result evaluate_against(Source source);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/ITargetAction.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface ITargetAction<Target, ValueType>
+    {
+        void act_against(Target destination, ValueType value);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/ITargetActionFactory.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public interface ITargetActionFactory
+    {
+        ITargetAction<Target, ValueType> create_action_target_from<Target, ValueType>(
+            Expression<Func<Target, ValueType>> target_expression);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/Map.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using Gorilla.Commons.Utility.Extensions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class Map<Input, Output> : IMap<Input, Output>
+    {
+        private IMapInitializationStep<Output> map_initialization_step;
+        private readonly IList<IMappingStep<Input, Output>> mapping_steps;
+        private readonly IMappingStepFactory mapping_step_factory;
+
+        public Map() : this(new MappingStepFactory())
+        {
+        }
+
+        public Map(IMappingStepFactory mapping_step_factory)
+            : this(
+                new MissingInitializationStep<Output>(), new List<IMappingStep<Input, Output>>(), mapping_step_factory)
+        {
+        }
+
+        public Map(IMapInitializationStep<Output> map_initialization_step,
+                   IList<IMappingStep<Input, Output>> mapping_steps, IMappingStepFactory mapping_step_factory)
+        {
+            this.map_initialization_step = map_initialization_step;
+            this.mapping_steps = mapping_steps;
+            this.mapping_step_factory = mapping_step_factory;
+        }
+
+        public void add(IMappingStep<Input, Output> step)
+        {
+            mapping_steps.Add(step);
+        }
+
+        public IMap<Input, Output> map<PropertyType>(Expression<Func<Input, PropertyType>> from,
+                                                     Expression<Func<Output, PropertyType>> to)
+        {
+            add(mapping_step_factory.create_mapping_step_for(from, to));
+            return this;
+        }
+
+        public IMap<Input, Output> initialize_mapping_using(Func<Output> initializer_expression)
+        {
+            map_initialization_step = new FuncInitializationStep<Output>(initializer_expression);
+            return this;
+        }
+
+        public Output map_from(Input input)
+        {
+            var output = map_initialization_step.initialize();
+            mapping_steps.each(x => x.map(input, output));
+            return output;
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/Mappers.cs
@@ -0,0 +1,16 @@
+using Gorilla.Commons.Utility.Core;
+using MoMoney.Domain.Accounting;
+using MoMoney.DTO;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class Mappers
+    {
+        static public IMapper<IBill, BillInformationDTO> bill_mapper =
+            new Map<IBill, BillInformationDTO>()
+                .initialize_mapping_using(() => new BillInformationDTO())
+                .map(x => x.company_to_pay.name, y => y.company_name)
+                .map(x => x.the_amount_owed.ToString(), y => y.the_amount_owed)
+                .map(x => x.due_date.to_date_time(), y => y.due_date);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/MappingStep.cs
@@ -0,0 +1,20 @@
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class MappingStep<Input, Output, Type> : IMappingStep<Input, Output>
+    {
+        private readonly ISourceEvaluator<Input, Type> input_evaluator;
+        private readonly ITargetAction<Output, Type> action_to_run_against_destination;
+
+        public MappingStep(ISourceEvaluator<Input, Type> source_evaluator, ITargetAction<Output, Type> target_action)
+        {
+            input_evaluator = source_evaluator;
+            action_to_run_against_destination = target_action;
+        }
+
+        public void map(Input input, Output destination)
+        {
+            var value_pulled_from_input_item = input_evaluator.evaluate_against(input);
+            action_to_run_against_destination.act_against(destination, value_pulled_from_input_item);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/MappingStepFactory.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class MappingStepFactory : IMappingStepFactory
+    {
+        private readonly ITargetActionFactory target_action_factory;
+
+        public MappingStepFactory() : this(new TargetActionFactory())
+        {
+        }
+
+        public MappingStepFactory(ITargetActionFactory target_action_factory)
+        {
+            this.target_action_factory = target_action_factory;
+        }
+
+        public IMappingStep<Source, Destination> create_mapping_step_for<Source, Destination, PropertyType>(
+            Expression<Func<Source, PropertyType>> source_expression,
+            Expression<Func<Destination, PropertyType>> destination_expression)
+        {
+            var source_evaluator = new ExpressionSourceEvaluator<Source, PropertyType>(source_expression);
+
+            var target_action = target_action_factory.create_action_target_from(destination_expression);
+
+            return new MappingStep<Source, Destination, PropertyType>(source_evaluator, target_action);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/MissingInitializationStep.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class MissingInitializationStep<Output> : IMapInitializationStep<Output>
+    {
+        public Output initialize()
+        {
+            throw new ArgumentException("A map must be provided an initialization step before it can be used to map");
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/PropertyResolutionException.cs
@@ -0,0 +1,19 @@
+using System;
+using Gorilla.Commons.Utility.Extensions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class PropertyResolutionException : Exception
+    {
+        public const string exception_message_format = "Failed to find the property named {0} on type {1}";
+
+        public PropertyResolutionException(Type type_that_did_not_have_the_property,
+                                           string property_that_could_not_be_found)
+            : base(
+                exception_message_format.formatted_using(property_that_could_not_be_found,
+                                                         type_that_did_not_have_the_property.Name))
+
+        {
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/PropertyResolver.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Gorilla.Commons.Utility.Extensions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class PropertyResolver : IPropertyResolver
+    {
+        private BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
+                                     BindingFlags.FlattenHierarchy;
+
+        public PropertyInfo resolve_using<Input, PropertyType>(Expression<Func<Input, PropertyType>> expression)
+        {
+            var member_accessor = (MemberExpression) expression.Body;
+            return resolve_using(typeof (Input), member_accessor.Member.Name);
+        }
+
+        public PropertyInfo resolve_using(Type type, string property_name)
+        {
+            var property = all_properties_belonging_to(type).Where(x => x.Name.Equals(property_name)).FirstOrDefault();
+
+            if (property == null) throw new PropertyResolutionException(type, property_name);
+
+            return property;
+        }
+
+        public IEnumerable<PropertyInfo> all_properties_belonging_to(Type type)
+        {
+            var stack = new Stack<Type>();
+            stack.Push(type);
+
+            while (stack.Count > 0)
+            {
+                var type_to_interrogate = stack.Pop();
+
+                type_to_interrogate.GetInterfaces().each(stack.Push);
+                foreach (var a_property in all_properties_for(type_to_interrogate))
+                {
+                    yield return a_property;
+                }
+            }
+        }
+
+        public IEnumerable<PropertyInfo> all_properties_belonging_to<T>()
+        {
+            return all_properties_belonging_to(typeof (T));
+        }
+
+        private PropertyInfo[] all_properties_for(Type type)
+        {
+            return type.GetProperties(flags);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/mapping/TargetActionFactory.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Linq.Expressions;
+
+namespace MoMoney.boot.container.registration.mapping
+{
+    public class TargetActionFactory : ITargetActionFactory
+    {
+        private readonly IPropertyResolver property_resolver;
+
+        public TargetActionFactory(IPropertyResolver property_resolver)
+        {
+            this.property_resolver = property_resolver;
+        }
+
+        public TargetActionFactory() : this(new PropertyResolver())
+        {
+        }
+
+        public ITargetAction<Target, ValueType> create_action_target_from<Target, ValueType>(
+            Expression<Func<Target, ValueType>> target_expression)
+        {
+            var property = property_resolver.resolve_using(target_expression);
+            if (property.CanWrite)
+                return new DelegateTargetAction<Target, ValueType>((x, y) => property.SetValue(x, y, new object[0]));
+
+            throw new ImmutablePropertyException(typeof (Target), property);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/boot/container/registration/wire_up_the_mappers_in_to_the.cs
@@ -18,7 +18,7 @@ namespace MoMoney.boot.container.registration
 
         public void run()
         {
-            registry.transient(typeof (IMapper<,>), typeof (Map<,>));
+            registry.transient(typeof (IMapper<,>), typeof (Mapper<,>));
             registry.singleton<Converter<IBill, BillInformationDTO>>(
                 () => x => new BillInformationDTO
                                {
trunk/product/MyMoney/MyMoney.csproj
@@ -167,6 +167,26 @@
     <Compile Include="boot\container\registration\auto_wire_components_in_to_the.cs" />
     <Compile Include="boot\container\registration\auto_wire_components_in_to_the_specs.cs" />
     <Compile Include="boot\container\registration\IStartupCommand.cs" />
+    <Compile Include="boot\container\registration\mapping\Mappers.cs" />
+    <Compile Include="boot\container\registration\mapping\DelegateTargetAction.cs" />
+    <Compile Include="boot\container\registration\mapping\ExpressionSourceEvaluator.cs" />
+    <Compile Include="boot\container\registration\mapping\FuncInitializationStep.cs" />
+    <Compile Include="boot\container\registration\mapping\IMap.cs" />
+    <Compile Include="boot\container\registration\mapping\IMapInitializationStep.cs" />
+    <Compile Include="boot\container\registration\mapping\IMappingStep.cs" />
+    <Compile Include="boot\container\registration\mapping\IMappingStepFactory.cs" />
+    <Compile Include="boot\container\registration\mapping\ImmutablePropertyException.cs" />
+    <Compile Include="boot\container\registration\mapping\IPropertyResolver.cs" />
+    <Compile Include="boot\container\registration\mapping\ISourceEvaluator.cs" />
+    <Compile Include="boot\container\registration\mapping\ITargetAction.cs" />
+    <Compile Include="boot\container\registration\mapping\ITargetActionFactory.cs" />
+    <Compile Include="boot\container\registration\mapping\Map.cs" />
+    <Compile Include="boot\container\registration\mapping\MappingStep.cs" />
+    <Compile Include="boot\container\registration\mapping\MappingStepFactory.cs" />
+    <Compile Include="boot\container\registration\mapping\MissingInitializationStep.cs" />
+    <Compile Include="boot\container\registration\mapping\PropertyResolutionException.cs" />
+    <Compile Include="boot\container\registration\mapping\PropertyResolver.cs" />
+    <Compile Include="boot\container\registration\mapping\TargetActionFactory.cs" />
     <Compile Include="boot\container\registration\proxy_configuration\NoConfiguration.cs" />
     <Compile Include="boot\container\registration\proxy_configuration\ServiceLayerConfiguration.cs" />
     <Compile Include="boot\container\registration\proxy_configuration\SynchronizedConfiguration.cs" />