1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
6
using System.ComponentModel;
7
using System.ComponentModel.Design;
9
using System.Globalization;
10
using System.Reflection;
11
using System.Resources;
13
using System.Windows.Forms;
15
using ICSharpCode.Core;
16
using ICSharpCode.Scripting;
17
using ICSharpCode.SharpDevelop;
18
using IronPython.Compiler.Ast;
20
namespace ICSharpCode.PythonBinding
23
/// Visits the code's Python AST and creates a Windows Form.
25
public class PythonComponentWalker : PythonWalker, IComponentWalker
28
PythonControlFieldExpression fieldExpression;
29
IComponentCreator componentCreator;
30
bool walkingAssignment;
31
string componentName = String.Empty;
32
PythonCodeDeserializer deserializer;
33
ClassDefinition classDefinition;
35
public PythonComponentWalker(IComponentCreator componentCreator)
37
this.componentCreator = componentCreator;
38
deserializer = new PythonCodeDeserializer(componentCreator);
42
/// Creates a control either a UserControl or Form from the python code.
44
public IComponent CreateComponent(string pythonCode)
46
PythonParser parser = new PythonParser();
47
PythonAst ast = parser.CreateAst(@"Control.py", new StringTextBuffer(pythonCode));
50
// Did we find the InitializeComponent method?
51
if (!FoundInitializeComponentMethod) {
52
throw new PythonComponentWalkerException("Unable to find InitializeComponents method.");
58
/// Gets the fully qualified name of the base class.
60
public static string GetBaseClassName(ClassDefinition classDefinition)
62
if (classDefinition.Bases.Count > 0) {
63
Expression baseClassExpression = classDefinition.Bases[0];
64
NameExpression nameExpression = baseClassExpression as NameExpression;
65
MemberExpression memberExpression = baseClassExpression as MemberExpression;
66
if (nameExpression != null) {
67
return nameExpression.Name;
69
return PythonControlFieldExpression.GetMemberName(memberExpression);
74
public override bool Walk(ClassDefinition node)
76
classDefinition = node;
77
componentName = node.Name;
82
public override bool Walk(FunctionDefinition node)
84
if (IsInitializeComponentMethod(node)) {
85
Type type = GetComponentType();
86
component = componentCreator.CreateComponent(type, componentName);
87
IResourceReader reader = componentCreator.GetResourceReader(CultureInfo.InvariantCulture);
96
public override bool Walk(AssignmentStatement node)
98
if (!FoundInitializeComponentMethod) {
102
if (node.Left.Count > 0) {
103
MemberExpression lhsMemberExpression = node.Left[0] as MemberExpression;
104
NameExpression lhsNameExpression = node.Left[0] as NameExpression;
105
if (lhsMemberExpression != null) {
106
fieldExpression = PythonControlFieldExpression.Create(lhsMemberExpression);
107
WalkMemberExpressionAssignmentRhs(node.Right);
108
} else if (lhsNameExpression != null) {
109
CallExpression callExpression = node.Right as CallExpression;
110
if (callExpression != null) {
111
object instance = CreateInstance(lhsNameExpression.Name.ToString(), callExpression);
112
if (instance == null) {
113
ThrowCouldNotFindTypeException(callExpression.Target as MemberExpression);
121
public override bool Walk(ConstantExpression node)
123
if (!FoundInitializeComponentMethod) {
127
fieldExpression.SetPropertyValue(componentCreator, node.Value);
131
public override bool Walk(CallExpression node)
133
if (!FoundInitializeComponentMethod) {
137
if (walkingAssignment) {
138
WalkAssignmentRhs(node);
140
WalkMethodCall(node);
145
public override bool Walk(NameExpression node)
147
if (!FoundInitializeComponentMethod) {
151
fieldExpression.SetPropertyValue(componentCreator, node);
156
/// Walks a statement of the form:
160
public override bool Walk(AugmentedAssignStatement node)
162
if (!FoundInitializeComponentMethod) {
166
MemberExpression eventExpression = node.Left as MemberExpression;
167
string eventName = eventExpression.Name.ToString();
168
PythonControlFieldExpression field = PythonControlFieldExpression.Create(eventExpression);
170
MemberExpression eventHandlerExpression = node.Right as MemberExpression;
171
string eventHandlerName = eventHandlerExpression.Name.ToString();
173
IComponent currentComponent = fieldExpression.GetObject(componentCreator) as IComponent;
175
EventDescriptor eventDescriptor = TypeDescriptor.GetEvents(currentComponent).Find(eventName, false);
176
PropertyDescriptor propertyDescriptor = componentCreator.GetEventProperty(eventDescriptor);
177
propertyDescriptor.SetValue(currentComponent, eventHandlerName);
182
/// Walks the binary expression which is the right hand side of an assignment statement.
184
void WalkAssignment(BinaryExpression binaryExpression)
186
object value = deserializer.Deserialize(binaryExpression);
187
fieldExpression.SetPropertyValue(componentCreator, value);
191
/// Walks the right hand side of an assignment to a member expression.
193
void WalkMemberExpressionAssignmentRhs(Expression rhs)
195
MemberExpression rhsMemberExpression = rhs as MemberExpression;
196
if (rhsMemberExpression != null) {
197
object propertyValue = GetPropertyValueFromAssignmentRhs(rhsMemberExpression);
198
fieldExpression.SetPropertyValue(componentCreator, propertyValue);
200
walkingAssignment = true;
201
BinaryExpression binaryExpression = rhs as BinaryExpression;
202
if (binaryExpression != null) {
203
WalkAssignment(binaryExpression);
207
walkingAssignment = false;
211
static bool IsInitializeComponentMethod(FunctionDefinition node)
213
string name = node.Name.ToString().ToLowerInvariant();
214
return name == "initializecomponent" || name == "initializecomponents";
218
/// Adds a component to the list of created objects.
220
void AddComponent(string name, object obj)
222
IComponent component = obj as IComponent;
223
if (component != null) {
224
string variableName = PythonControlFieldExpression.GetVariableName(name);
225
componentCreator.Add(component, variableName);
230
/// Gets the type for the control being walked.
232
Type GetComponentType()
234
string baseClass = GetBaseClassName(classDefinition);
235
Type type = componentCreator.GetType(baseClass);
240
if (baseClass.Contains("UserControl")) {
241
return typeof(UserControl);
247
/// Gets the property value from the member expression. The member expression is taken from the
248
/// right hand side of an assignment.
250
object GetPropertyValueFromAssignmentRhs(MemberExpression memberExpression)
252
return deserializer.Deserialize(memberExpression);
256
/// Walks the right hand side of an assignment where the assignment expression is a call expression.
257
/// Typically the call expression will be a constructor call.
259
/// Constructor call: System.Windows.Forms.Form()
261
void WalkAssignmentRhs(CallExpression node)
263
MemberExpression memberExpression = node.Target as MemberExpression;
264
if (memberExpression != null) {
265
string name = fieldExpression.GetInstanceName(componentCreator);
266
object instance = CreateInstance(name, node);
267
if (instance != null) {
268
if (!fieldExpression.SetPropertyValue(componentCreator, instance)) {
269
AddComponent(fieldExpression.MemberName, instance);
272
object obj = deserializer.Deserialize(node);
274
fieldExpression.SetPropertyValue(componentCreator, obj);
275
} else if (IsResource(memberExpression)) {
276
fieldExpression.SetPropertyValue(componentCreator, GetResource(node));
278
ThrowCouldNotFindTypeException(memberExpression);
281
} else if (node.Target is IndexExpression) {
282
WalkArrayAssignmentRhs(node);
287
/// Walks a method call. Typical method calls are:
289
/// self._menuItem1.Items.AddRange(...)
291
/// This method will execute the method call.
293
void WalkMethodCall(CallExpression node)
295
// Try to get the object being called. Try the form first then
296
// look for other controls.
297
object member = PythonControlFieldExpression.GetMember(component, node);
298
PythonControlFieldExpression field = PythonControlFieldExpression.Create(node);
299
if (member == null) {
300
member = field.GetMember(componentCreator);
303
// Execute the method on the object.
304
if (member != null) {
305
object[] args = deserializer.GetArguments(node).ToArray();
306
InvokeMethod(member, field.MethodName, args);
310
void InvokeMethod(object obj, string name, object[] args)
312
Type type = obj.GetType();
314
type.InvokeMember(name, BindingFlags.InvokeMethod, Type.DefaultBinder, obj, args);
315
} catch (MissingMethodException ex) {
316
// Look for an explicitly implemented interface.
317
MethodInfo method = FindInterfaceMethod(type, name);
318
if (method != null) {
319
method.Invoke(obj, args);
327
/// Looks for an explicitly implemented interface.
329
MethodInfo FindInterfaceMethod(Type type, string name)
331
string nameMatch = "." + name;
332
foreach (MethodInfo method in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)) {
333
if (method.Name.EndsWith(nameMatch)) {
341
/// Creates a new instance with the specified name.
343
object CreateInstance(string name, CallExpression node)
345
MemberExpression memberExpression = node.Target as MemberExpression;
346
if (memberExpression != null) {
347
string typeName = PythonControlFieldExpression.GetMemberName(memberExpression);
348
Type type = componentCreator.GetType(typeName);
350
if (type.IsAssignableFrom(typeof(ComponentResourceManager))) {
351
return componentCreator.CreateInstance(type, new object[0], name, false);
353
List<object> args = deserializer.GetArguments(node);
354
return componentCreator.CreateInstance(type, args, name, false);
360
bool FoundInitializeComponentMethod {
361
get { return component != null; }
365
/// Returns true if the expression is of the form:
367
/// resources.GetObject(...) or
368
/// resources.GetString(...)
370
bool IsResource(MemberExpression memberExpression)
372
string fullName = PythonControlFieldExpression.GetMemberName(memberExpression);
373
return fullName.StartsWith("resources.", StringComparison.InvariantCultureIgnoreCase);
376
object GetResource(CallExpression callExpression)
378
IResourceReader reader = componentCreator.GetResourceReader(CultureInfo.InvariantCulture);
379
if (reader != null) {
380
using (ResourceSet resources = new ResourceSet(reader)) {
381
List<object> args = deserializer.GetArguments(callExpression);
382
return resources.GetObject(args[0] as String);
389
/// Walks the right hand side of an assignment when the assignment is an array creation.
391
void WalkArrayAssignmentRhs(CallExpression callExpression)
393
object array = deserializer.Deserialize(callExpression);
394
fieldExpression.SetPropertyValue(componentCreator, array);
397
void ThrowCouldNotFindTypeException(MemberExpression memberExpression)
399
string typeName = PythonControlFieldExpression.GetMemberName(memberExpression);
400
throw new PythonComponentWalkerException(String.Format(StringParser.Parse("${res:ICSharpCode.PythonBinding.UnknownTypeName}"), typeName));