Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Node.Iterator.Extensions.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections;
5 using System.Collections.Concurrent;
6 using System.Collections.Generic;
7 using System.Linq.Expressions;
8 using System.Reflection;
9 
10 using LinqExpression = System.Linq.Expressions.Expression;
11 
12 namespace SiliconStudio.Shaders.Ast
13 {
14  /// <summary>
15  /// Shader childrens iterator.
16  /// </summary>
17  public static class NodeIterator
18  {
19  private static readonly ConcurrentDictionary<Type, NodeProcessor> RegisteredProcessors = new ConcurrentDictionary<Type, NodeProcessor>();
20 
21  /// <summary>
22  /// Iterate on childrens and apply the specified transform function.
23  /// </summary>
24  /// <param name="node">The node.</param>
25  /// <param name="nodeProcessor">The process.</param>
26  /// <param name="listProcessor">The list processor. Default is null using default implementation.</param>
27  /// <returns>The source node</returns>
28  public static Node Childrens(this Node node, NodeProcessor nodeProcessor, NodeListProcessor listProcessor = null)
29  {
30  if (node == null) return null;
31 
32  var type = node.GetType();
33  var process = RegisteredProcessors.GetOrAdd(type, BuildChildrensIterator);
34  if (listProcessor == null)
35  listProcessor = DefaultListProcessor;
36  var context = new NodeProcessorContext(nodeProcessor, listProcessor);
37  return process(node, ref context);
38  }
39 
40  /// <summary>
41  /// Default <see cref="NodeListProcessor"/> implementation used by <see cref="Childrens"/> iterator.
42  /// </summary>
43  /// <param name="list">The list.</param>
44  /// <param name="nodeProcessorContext">The node processor context.</param>
45  public static void DefaultListProcessor(IList list, ref NodeProcessorContext nodeProcessorContext)
46  {
47  if (list != null)
48  {
49  var nodeProcessor = nodeProcessorContext.NodeProcessor;
50  for (int i = 0; i < list.Count;)
51  {
52  var previousValue = (Node)list[i];
53  var temp = nodeProcessor(previousValue, ref nodeProcessorContext);
54 
55  // Recover the position as the list can be modified while processing a node
56  for (i = 0; i < list.Count; i++)
57  {
58  if (ReferenceEquals(previousValue, list[i]))
59  break;
60  }
61 
62  if (temp == null)
63  {
64  list.RemoveAt(i);
65  }
66  else
67  {
68  if (!ReferenceEquals(previousValue, temp))
69  list[i] = temp;
70  i++;
71  }
72  }
73  }
74  }
75 
76 
77  private static FieldInfo FieldReflector<T>(Expression<Func<T,object>> access)
78  {
79  return (FieldInfo)((MemberExpression)access.Body).Member;
80  }
81 
82  private static FieldInfo nodeProcessorProperty = FieldReflector<NodeProcessorContext>(obj => obj.NodeProcessor);
83  private static FieldInfo nodeListProcessorProperty = FieldReflector<NodeProcessorContext>(obj => obj.ListProcessor);
84 
85  /// <summary>
86  /// Builds the childrens iterator.
87  /// </summary>
88  /// <param name="rootType">Type of the root.</param>
89  /// <returns>
90  /// A function that is able to process childrens from a node
91  /// </returns>
92  private static NodeProcessor BuildChildrensIterator(Type rootType)
93  {
94  var sourceParameter = LinqExpression.Parameter(typeof(Node), "source");
95  var explorerParameter = LinqExpression.Parameter(typeof(NodeProcessorContext).MakeByRefType(), "nodeProcessorContext");
96  var variableNodeProcessor = LinqExpression.Variable(typeof(NodeProcessor), "nodeProcessor");
97  var variableNodeListProcessor = LinqExpression.Variable(typeof(NodeListProcessor), "listProcessor");
98  var statements = new List<LinqExpression>();
99 
100  // Cast source variable
101  var castVar = LinqExpression.Variable(rootType, "cast");
102  var variables = new List<ParameterExpression> { castVar, variableNodeProcessor, variableNodeListProcessor };
103 
104  statements.Add(LinqExpression.Assign(castVar, LinqExpression.Convert(sourceParameter, rootType)));
105 
106  statements.Add(LinqExpression.Assign(variableNodeProcessor, LinqExpression.Field(explorerParameter, nodeProcessorProperty)));
107  statements.Add(LinqExpression.Assign(variableNodeListProcessor, LinqExpression.Field(explorerParameter, nodeListProcessorProperty)));
108 
109  // Get all types from most inherited
110  var types = new List<Type>();
111  for (var type = rootType; type != null; type = type.BaseType)
112  types.Add(type);
113  types.Reverse();
114 
115  // Iterate on inherited types
116  foreach (var type in types)
117  {
118  // Iterate on all properties in order to create the iterator.
119  foreach (var sourceField in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))
120  {
121 
122  // If the property is not read-writable or contains a visitor ignore attribute, skip it
123  if (sourceField.GetCustomAttributes(typeof(VisitorIgnoreAttribute), true).Length != 0)
124  {
125  continue;
126  }
127 
128  var propertyType = sourceField.FieldType;
129 
130  var interfaces = propertyType.GetInterfaces();
131 
132  // Get the property type and check if the property inherit from IList<>
133  if (!typeof(StatementList).IsAssignableFrom(propertyType))
134  {
135  foreach (var interfaceBase in interfaces)
136  {
137  if (interfaceBase.IsGenericType && interfaceBase.GetGenericTypeDefinition() == typeof(IList<>))
138  {
139  var parameterType = interfaceBase.GetGenericArguments()[0];
140  if (typeof(Node).IsAssignableFrom(parameterType))
141  statements.Add(
142  LinqExpression.Invoke(variableNodeListProcessor, LinqExpression.Field(castVar, sourceField), explorerParameter));
143  break;
144  }
145  }
146  }
147 
148  if (typeof(Node).IsAssignableFrom(propertyType))
149  {
150  statements.Add(
151  LinqExpression.Assign(
152  LinqExpression.Field(castVar, sourceField),
153  LinqExpression.Convert(LinqExpression.Invoke(variableNodeProcessor, LinqExpression.Field(castVar, sourceField), explorerParameter),propertyType)));
154  }
155  }
156  }
157 
158  // Return source parameter
159  statements.Add(sourceParameter);
160 
161  // Lambda body
162  var block = LinqExpression.Block(variables, statements);
163 
164  // Create lambda and return a compiled version
165  var lambda = LinqExpression.Lambda<NodeProcessor>(block, sourceParameter, explorerParameter);
166  return lambda.Compile();
167  }
168  }
169 }
delegate Node NodeProcessor(Node node, ref NodeProcessorContext nodeProcessorContext)
Processor for a single node.
Abstract node.
Definition: Node.cs:15
static Node Childrens(this Node node, NodeProcessor nodeProcessor, NodeListProcessor listProcessor=null)
Iterate on childrens and apply the specified transform function.
delegate void NodeListProcessor(IList list, ref NodeProcessorContext nodeProcessorContext)
Processor for a list of node.
System.Linq.Expressions.Expression LinqExpression
static void DefaultListProcessor(IList list, ref NodeProcessorContext nodeProcessorContext)
Default NodeListProcessor implementation used by Childrens iterator.