Skip to content

Commit

Permalink
Allow specifying final placement #13
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRappl committed May 28, 2016
1 parent df09df5 commit 89d7fd2
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 39 deletions.
41 changes: 38 additions & 3 deletions src/Mages.Core.Tests/ExtensibilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public void GlobalPropertyTestGetPropertyShouldWork()
public void ProvideSimpleArrayClassShouldWork()
{
var engine = new Engine();
engine.SetStatic<List<Object>>("Array");
engine.SetStatic<List<Object>>().WithName("Array");

var result = engine.Interpret("x = Array.create(); x.add(\"one\");x.add(\"two\");x.insert(1, \"half\");x.add(x.count);x.add(x.at(0)); x");
var list = result as List<Object>;
Expand All @@ -299,7 +299,7 @@ public void ProvideSimpleArrayClassShouldWork()
public void ProvideStringBuilderWithoutNameShouldYieldNormalName()
{
var engine = new Engine();
engine.SetStatic<StringBuilder>();
engine.SetStatic<StringBuilder>().WithDefaultName();

var result = engine.Interpret("sb = StringBuilder.create(); sb.append(\"foo\").append(\"bar\"); sb.toString()");

Expand Down Expand Up @@ -355,13 +355,35 @@ public void NamesAreTakenFromTheCustomNameSelector()

using (Container.Register(service))
{
engine.SetStatic<StringBuilder>();
engine.SetStatic<StringBuilder>().WithDefaultName();
var result = engine.Interpret("sb = foo.New(); sb.APPEND(\"foo\").APPEND(\"bar\"); sb.TOSTRING()");

Assert.AreEqual("foobar", result);
}
}

[Test]
public void ProvideMethodsFromStaticClassByScattering()
{
var engine = new Engine();
engine.SetStatic(typeof(Functions)).Scattered();

var result = engine.Interpret("foo() + bar()");

Assert.AreEqual("foo0bar1", result);
}

[Test]
public void ProvideMethodsFromStaticClassByNamespaceObject()
{
var engine = new Engine();
engine.SetStatic(typeof(Functions)).WithName("baz");

var result = engine.Interpret("baz.foo() + baz.bar()");

Assert.AreEqual("foo0bar1", result);
}

sealed class Point
{
public Double x;
Expand All @@ -378,5 +400,18 @@ sealed class PropertyTest
{
public String test { get; set; }
}

static class Functions
{
public static String Foo()
{
return "foo0";
}

public static String Bar()
{
return "bar1";
}
}
}
}
109 changes: 83 additions & 26 deletions src/Mages.Core/EngineExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using Mages.Core.Runtime;
using System;
using System.Collections.Generic;
using System.Reflection;

/// <summary>
Expand All @@ -21,8 +22,8 @@ public static void SetFunction(this Engine engine, String name, Function functio
}

/// <summary>
/// Adds or replaces a function represented as a general delegate by wrapping it as
/// a function with the given name.
/// Adds or replaces a function represented as a general delegate by wrapping
/// it as a function with the given name.
/// </summary>
/// <param name="engine">The engine.</param>
/// <param name="name">The name of the function to add or replace.</param>
Expand All @@ -33,8 +34,8 @@ public static void SetFunction(this Engine engine, String name, Delegate functio
}

/// <summary>
/// Adds or replaces a function represented as a reflected method info by wrapping it
/// as a function with the given name.
/// Adds or replaces a function represented as a reflected method info by
/// wrapping it as a function with the given name.
/// </summary>
/// <param name="engine">The engine.</param>
/// <param name="name">The name of the function to add or replace.</param>
Expand All @@ -46,8 +47,8 @@ public static void SetFunction(this Engine engine, String name, MethodInfo metho
}

/// <summary>
/// Adds or replaces an object represented as the MAGES primitive. This is either directly
/// the given value or a wrapper around it.
/// Adds or replaces an object represented as the MAGES primitive. This is
/// either directly the given value or a wrapper around it.
/// </summary>
/// <param name="engine">The engine.</param>
/// <param name="name">The name of the constant to add or replace.</param>
Expand All @@ -58,50 +59,106 @@ public static void SetConstant(this Engine engine, String name, Object value)
}

/// <summary>
/// Adds or replaces a type represented as the MAGES primitive. This exposes all static
/// methods and its constructors via the name of the type.
/// Adds or replaces a type represented as the MAGES primitive. This exposes
/// all static methods and its constructors via the name of the type.
/// </summary>
/// <typeparam name="T">The type to expose.</typeparam>
/// <param name="engine">The engine.</param>
public static void SetStatic<T>(this Engine engine)
public static IPlacement SetStatic<T>(this Engine engine)
{
engine.SetStatic(typeof(T));
return engine.SetStatic(typeof(T));
}

/// <summary>
/// Adds or replaces a type represented as the MAGES primitive. This exposes all static
/// methods and its constructors via the name of the type.
/// Adds or replaces a type represented as the MAGES primitive. This exposes
/// all static methods and its constructors via the name of the type.
/// </summary>
/// <param name="engine">The engine.</param>
/// <param name="type">The type to expose.</param>
public static void SetStatic(this Engine engine, Type type)
public static IPlacement SetStatic(this Engine engine, Type type)
{
var name = Helpers.FindName(engine.Globals.Keys, type);
engine.SetStatic(name, type);
var obj = Helpers.Expose(type);
return new Placement(engine, name, obj);
}

/// <summary>
/// Adds or replaces a type represented as the MAGES primitive. This exposes all static
/// methods and its constructors via the given name.
/// Adds or replaces the names of the types from the assembly represented as
/// MAGES primitives.
/// </summary>
/// <typeparam name="T">The type to expose.</typeparam>
/// <param name="engine">The engine.</param>
/// <param name="name">The name of the constant to add or replace.</param>
public static void SetStatic<T>(this Engine engine, String name)
/// <param name="lib">The library containing the types to expose.</param>
public static IPlacement SetStatic(this Engine engine, Assembly lib)
{
engine.SetStatic(name, typeof(T));
var types = lib.GetTypes();
var libNameParts = lib.GetName().Name.Split(new[] { '.', ',', ' ', '-', '+' }, StringSplitOptions.RemoveEmptyEntries);
var libName = String.Join(String.Empty, libNameParts);
var obj = new Dictionary<String, Object>();

foreach (var type in types)
{
var name = Helpers.FindName(obj.Keys, type);
var value = Helpers.Expose(type);
obj[name] = value;
}

return new Placement(engine, libName, obj);
}

/// <summary>
/// Adds or replaces a type represented as the MAGES primitive. This exposes all static
/// methods and its constructors via the given name.
/// Adds or replaces the types from the list represented as MAGES primitives.
/// </summary>
/// <param name="engine">The engine.</param>
/// <param name="name">The name of the constant to add or replace.</param>
/// <param name="type">The type to expose.</param>
public static void SetStatic(this Engine engine, String name, Type type)
/// <param name="types">The types to include.</param>
public static IPlacement SetStatic(this Engine engine, IEnumerable<Type> types)
{
var obj = new Dictionary<String, Object>();

foreach (var type in types)
{
var name = Helpers.FindName(obj.Keys, type);
var value = Helpers.Expose(type);
obj[name] = value;
}

return new Placement(engine, null, obj);
}

sealed class Placement : IPlacement
{
engine.Globals[name] = Helpers.Expose(type);
private readonly Engine _engine;
private readonly String _name;
private readonly IDictionary<String, Object> _obj;

public Placement(Engine engine, String name, IDictionary<String, Object> obj)
{
_engine = engine;
_name = name;
_obj = obj;
}

public void WithName(String name)
{
if (String.IsNullOrEmpty(name))
{
throw new ArgumentException("The given name has to be non-empty.", "name");
}

_engine.Globals[name] = _obj;
}

public void WithDefaultName()
{
WithName(_name);
}

public void Scattered()
{
foreach (var item in _obj)
{
_engine.Globals[item.Key] = item.Value;
}
}
}
}
}
26 changes: 26 additions & 0 deletions src/Mages.Core/IPlacement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Mages.Core
{
using System;

/// <summary>
/// Determines the placement of objects in the global scope.
/// </summary>
public interface IPlacement
{
/// <summary>
/// Placed with the given name.
/// </summary>
/// <param name="name">The name to use.</param>
void WithName(String name);

/// <summary>
/// Placed with the default name.
/// </summary>
void WithDefaultName();

/// <summary>
/// The children of the object are placed in the scope.
/// </summary>
void Scattered();
}
}
1 change: 1 addition & 0 deletions src/Mages.Core/Mages.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<Compile Include="Configuration.cs" />
<Compile Include="EngineExtensions.cs" />
<Compile Include="Function.cs" />
<Compile Include="IPlacement.cs" />
<Compile Include="Runtime\Container.cs" />
<Compile Include="Runtime\Converters\CamelNameSelector.cs" />
<Compile Include="Runtime\Converters\INameSelector.cs" />
Expand Down
2 changes: 1 addition & 1 deletion src/Mages.Core/Runtime/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public static Double Factorial(this Double value)
return result;
}

public static Object Expose(Type type)
public static WrapperObject Expose(Type type)
{
return new WrapperObject(type);
}
Expand Down
42 changes: 34 additions & 8 deletions src/Mages.Core/Runtime/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,31 +96,56 @@ public static Dictionary<String, BaseProxy> GetStaticProxies(this Type type, Wra
{
var proxies = new Dictionary<String, BaseProxy>();
var ctors = type.GetConstructors();
var flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
var fields = type.GetFields(flags);
var properties = type.GetProperties(flags);
var methods = type.GetMethods(flags);
var selector = Container.GetService<INameSelector>(CamelNameSelector.Instance);

if (ctors.Length > 0)
{
var name = selector.Select(proxies.Keys, ctors[0]);
proxies[name] = new ConstructorProxy(target, ctors);
}
fields.AddToProxy(target, proxies, selector);
properties.AddToProxy(target, proxies, selector);
methods.AddToProxy(target, proxies, selector);
ctors.AddToProxy(target, proxies, selector);

return proxies;
}

public static Dictionary<String, BaseProxy> GetMemberProxies(this Type type, WrapperObject target)
{
var proxies = new Dictionary<String, BaseProxy>();
var indices = new List<PropertyInfo>();
var fields = type.GetFields();
var properties = type.GetProperties();
var methods = type.GetMethods();
var selector = Container.GetService<INameSelector>(CamelNameSelector.Instance);

fields.AddToProxy(target, proxies, selector);
properties.AddToProxy(target, proxies, selector);
methods.AddToProxy(target, proxies, selector);

return proxies;
}

private static void AddToProxy(this ConstructorInfo[] constructors, WrapperObject target, IDictionary<String, BaseProxy> proxies, INameSelector selector)
{
if (constructors.Length > 0)
{
var name = selector.Select(proxies.Keys, constructors[0]);
proxies[name] = new ConstructorProxy(target, constructors);
}
}

private static void AddToProxy(this FieldInfo[] fields, WrapperObject target, IDictionary<String, BaseProxy> proxies, INameSelector selector)
{
foreach (var field in fields)
{
var name = selector.Select(proxies.Keys, field);
proxies.Add(name, new FieldProxy(target, field));
}
}

private static void AddToProxy(this PropertyInfo[] properties, WrapperObject target, IDictionary<String, BaseProxy> proxies, INameSelector selector)
{
var indices = new List<PropertyInfo>();

foreach (var property in properties)
{
Expand All @@ -140,15 +165,16 @@ public static Dictionary<String, BaseProxy> GetMemberProxies(this Type type, Wra
var name = selector.Select(proxies.Keys, indices[0]);
proxies[name] = new IndexProxy(target, indices.ToArray());
}
}

private static void AddToProxy(this MethodInfo[] methods, WrapperObject target, IDictionary<String, BaseProxy> proxies, INameSelector selector)
{
foreach (var method in methods.Where(m => !m.IsSpecialName).GroupBy(m => m.Name))
{
var overloads = method.ToArray();
var name = selector.Select(proxies.Keys, overloads[0]);
proxies.Add(name, new MethodProxy(target, overloads));
}

return proxies;
}
}
}
3 changes: 2 additions & 1 deletion src/Mages.Core/Runtime/WrapperObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Represents the object wrapper from MAGES.
Expand Down Expand Up @@ -195,7 +196,7 @@ void ICollection<KeyValuePair<String, Object>>.CopyTo(KeyValuePair<String, Objec
/// <returns>The extension's enumerator.</returns>
public IEnumerator<KeyValuePair<String, Object>> GetEnumerator()
{
return _extends.GetEnumerator();
return _extends.Concat(_proxies.Select(m => new KeyValuePair<String, Object>(m.Key, m.Value.Value))).GetEnumerator();
}

Boolean ICollection<KeyValuePair<String, Object>>.Remove(KeyValuePair<String, Object> item)
Expand Down

0 comments on commit 89d7fd2

Please sign in to comment.