Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
VisitorBase.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.Concurrent;
5 using System.Collections.Generic;
6 using System.Reflection;
7 using SiliconStudio.Shaders.Ast;
8 using LinqExpression = System.Linq.Expressions.Expression;
9 
10 namespace SiliconStudio.Shaders.Visitor
11 {
12  /// <summary>
13  /// Visitor base.
14  /// </summary>
15  public abstract class VisitorBase
16  {
17  private delegate object NodeVisitFunction(VisitorBase visitor, object node);
18 
19  private readonly static Dictionary<Type, Dictionary<Type, NodeVisitFunction>> AllVisitors = new Dictionary<Type, Dictionary<Type, NodeVisitFunction>>();
20  private readonly static Dictionary<Type, List<Type>> MapDefaultTypeToInheritance = new Dictionary<Type, List<Type>>();
21  private readonly Dictionary<Type, NodeVisitFunction> methodVisitors;
22  private readonly Dictionary<Type, List<Type>> mapTypeToInheritance = new Dictionary<Type, List<Type>>();
23 
24  #region Constructors and Destructors
25 
26  /// <summary>
27  /// Initializes a new instance of the <see cref="VisitorBase"/> class.
28  /// </summary>
29  /// <param name="useNodeStack">if set to <c>true</c> [use node stack].</param>
30  protected VisitorBase(bool useNodeStack = false)
31  {
32  lock (AllVisitors)
33  {
34  Dictionary<Type, NodeVisitFunction> concurrentVisitors;
35  var thisType = GetType();
36  if (!AllVisitors.TryGetValue(thisType, out concurrentVisitors))
37  {
38  concurrentVisitors = Initialize(this);
39  AllVisitors.Add(thisType, concurrentVisitors);
40  }
41  methodVisitors = new Dictionary<Type, NodeVisitFunction>(concurrentVisitors);
42  mapTypeToInheritance = new Dictionary<Type, List<Type>>(MapDefaultTypeToInheritance);
43  }
44 
45  if (useNodeStack)
46  {
47  NodeStack = new List<Node>();
48  }
49  }
50 
51  #endregion
52 
53  #region Public Properties
54 
55  /// <summary>
56  /// Gets or sets the node stack.
57  /// </summary>
58  /// <value>
59  /// The node stack.
60  /// </value>
61  public List<Node> NodeStack { get; set; }
62 
63  #endregion
64 
65  #region Public Methods
66 
67  /// <summary>
68  /// Visits the list.
69  /// </summary>
70  /// <typeparam name="T">Type of the item in the list</typeparam>
71  /// <param name="list">The list.</param>
72  /// <param name="filter">The function filter.</param>
73  protected void VisitDynamicList<T>(IList<T> list, Func<T, bool> filter = null) where T : Node
74  {
75  for (int i = 0; i < list.Count; i++)
76  {
77  var item = list[i];
78 
79  // Filter the element
80  if (filter != null && filter(item)) continue;
81 
82  var newNode = VisitDynamic(list[i]);
83 
84  if (newNode == null)
85  {
86  list.RemoveAt(i);
87  i--;
88  }
89  else if (!ReferenceEquals(newNode, item))
90  {
91  list[i] = (T)newNode;
92  }
93  }
94  }
95 
96  /// <summary>
97  /// Visits the node.
98  /// </summary>
99  /// <typeparam name="T">Type of the node</typeparam>
100  /// <param name="node">The node.</param>
101  /// <param name="visitRealType">if set to <c>true</c> [visit real type].</param>
102  /// <returns>
103  /// A node
104  /// </returns>
105  protected virtual Node VisitDynamic<T>(T node, bool visitRealType = true) where T : Node
106  {
107  if (node == null)
108  {
109  return null;
110  }
111 
112  bool nodeStackAdded = false;
113 
114  if (NodeStack != null)
115  {
116  if (NodeStack.Count > 0 && ReferenceEquals(NodeStack[NodeStack.Count - 1], node))
117  throw new InvalidOperationException(string.Format("Cannot visit recursively a node [{0}] already being visited", node));
118 
119  NodeStack.Add(node);
120  nodeStackAdded = true;
121  }
122 
123  // Only Visit in the Iterator
124  bool doVisit = PreVisitNode(node);
125 
126  // Double-dispatch using dynamic, much more faster (x4) than expression or reflection methods.
127  var result = (Node)node;
128  if (doVisit)
129  {
130  NodeVisitFunction process = null;
131  var typeToVisit = visitRealType ? node.GetType() : typeof(T);
132 
133  if (!methodVisitors.TryGetValue(typeToVisit, out process))
134  {
135  foreach (var ancestor in Ancestors(typeToVisit))
136  {
137  if (methodVisitors.TryGetValue(ancestor, out process))
138  {
139  // Optimize for next pass on this kind of type
140  if (typeToVisit != ancestor)
141  methodVisitors[typeToVisit] = process;
142  break;
143  }
144  }
145  }
146 
147  if (process == null)
148  throw new InvalidOperationException(string.Format("Unable to find a suitable visitor method for this type [{0}]", typeToVisit.FullName));
149 
150  result = (Node)process(this, node);
151  }
152 
153  // Only Visit in the Iterator
154  PostVisitNode(node, doVisit);
155 
156  if (NodeStack != null && nodeStackAdded)
157  {
158  NodeStack.RemoveAt(NodeStack.Count - 1);
159  }
160 
161  return result;
162  }
163 
164  /// <summary>
165  /// Get all ancestors (type and interfaces) for a particular type.
166  /// </summary>
167  /// <param name="type">The type.</param>
168  /// <returns>Ancestors of the type.</returns>
169  private IEnumerable<Type> Ancestors(Type type)
170  {
171  return Ancestors(mapTypeToInheritance, type);
172  }
173 
174  /// <summary>
175  /// Get all ancestors (type and interfaces) for a particular type.
176  /// </summary>
177  /// <param name="registeredTypes">The registered types.</param>
178  /// <param name="type">The type.</param>
179  /// <returns>Ancestors of the type.</returns>
180  private static IEnumerable<Type> Ancestors(Dictionary<Type, List<Type>> registeredTypes, Type type)
181  {
182  List<Type> inheritance;
183  if (registeredTypes.TryGetValue(type, out inheritance))
184  return inheritance;
185 
186  inheritance = new List<Type>();
187  inheritance.AddRange(type.GetInterfaces());
188  var baseType = type.BaseType;
189  while (baseType != null)
190  {
191  inheritance.Add(baseType);
192  baseType = baseType.BaseType;
193  }
194  registeredTypes[type] = inheritance;
195 
196  // If the ancestors is not registered, we will register it to both the local registry
197  // and to the shared registry
198  lock (AllVisitors)
199  {
200  MapDefaultTypeToInheritance[type] = inheritance;
201  }
202  return inheritance;
203  }
204 
205  private static Dictionary<Type, NodeVisitFunction> Initialize(VisitorBase visitor)
206  {
207  var methodVisitors = new Dictionary<Type, NodeVisitFunction>();
208  var methods = visitor.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
209 
210  foreach (var method in methods)
211  {
212  var attributes = method.GetCustomAttributes(typeof(VisitAttribute), true);
213  if (attributes.Length > 0)
214  {
215  Type parameterType;
216  var function = BuildMethodVisitor(method, out parameterType);
217  methodVisitors[parameterType] = function;
218 
219  // Pre-register default ancestors for all parameters
220  Ancestors(MapDefaultTypeToInheritance, parameterType);
221  }
222  }
223  return methodVisitors;
224  }
225 
226  private static NodeVisitFunction BuildMethodVisitor(MethodInfo method, out Type parameterType)
227  {
228  var declaringtype = method.DeclaringType;
229 
230  if (declaringtype == null)
231  throw new InvalidOperationException(string.Format("No declaring type for method [{0}]", method.Name));
232 
233  // Throws an exception if parammeters.Count != 1
234  if (method.GetParameters().Length != 1)
235  throw new InvalidOperationException(
236  string.Format("Invalid number of parameters [{0}] for visit method [{1}] for type [{2}]. One parameter is expected", method.GetParameters().Length, method.Name, declaringtype.FullName));
237 
238  parameterType = method.GetParameters()[0].ParameterType;
239  if (!parameterType.IsInterface && !typeof(Node).IsAssignableFrom(parameterType))
240  throw new InvalidOperationException(
241  string.Format("Invalid type parameter [{0}] for visit method [{1}] for type [{2}]. Parameter must inherit from Node", parameterType, method.Name, declaringtype.FullName));
242 
243  var thisParameter = LinqExpression.Parameter(typeof(VisitorBase), "this");
244  var nodeParameter = LinqExpression.Parameter(typeof(object), "node");
245  var thisCastVariable = LinqExpression.Variable(declaringtype, "thisCast");
246 
247  var statements = new List<LinqExpression>
248  {
249  LinqExpression.Assign(thisCastVariable, LinqExpression.Convert(thisParameter, declaringtype))
250  };
251 
252  var callVisitMethod = LinqExpression.Call(thisCastVariable, method, LinqExpression.Convert(nodeParameter, parameterType));
253 
254  if (typeof(void) == method.ReturnType)
255  {
256  statements.Add(callVisitMethod);
257  statements.Add(nodeParameter);
258  }
259  else
260  {
261  statements.Add(callVisitMethod);
262  }
263 
264  var block = LinqExpression.Block(new[] { thisCastVariable }, statements);
265 
266  var lambda = LinqExpression.Lambda<NodeVisitFunction>(block, thisParameter, nodeParameter);
267  return lambda.Compile();
268  }
269 
270  /// <summary>
271  /// Called before visiting the node.
272  /// </summary>
273  /// <param name="node">The node.</param>
274  /// <returns>True to continue visiting the node; false to skip the visit</returns>
275  protected virtual bool PreVisitNode(Node node)
276  {
277  return true;
278  }
279 
280  /// <summary>
281  /// Called after visiting the node.
282  /// </summary>
283  /// <param name="node">The node.</param>
284  /// <param name="nodeVisited">if set to <c>true</c> [node visited].</param>
285  protected virtual void PostVisitNode(Node node, bool nodeVisited)
286  {
287  }
288 
289  #endregion
290 
291  /// <summary>
292  /// Tag a visitable method with this attribute.
293  /// </summary>
294  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
295  protected class VisitAttribute : Attribute
296  {
297  }
298  }
299 }
Tag a visitable method with this attribute.
Definition: VisitorBase.cs:295
virtual void PostVisitNode(Node node, bool nodeVisited)
Called after visiting the node.
Definition: VisitorBase.cs:285
virtual bool PreVisitNode(Node node)
Called before visiting the node.
Definition: VisitorBase.cs:275
Abstract node.
Definition: Node.cs:15
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.
VisitorBase(bool useNodeStack=false)
Initializes a new instance of the VisitorBase class.
Definition: VisitorBase.cs:30
System.Linq.Expressions.Expression LinqExpression