Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Operators.Programmable/ProgrammableOperator.cs @ 866

Last change on this file since 866 was 694, checked in by gkronber, 16 years ago

fixed #315 (Support for LINQ extensions in ProgrammableOperator)

File size: 8.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25using System.Xml;
26using System.IO;
27using System.Linq;
28using System.Reflection;
29using System.CodeDom;
30using System.CodeDom.Compiler;
31using Microsoft.CSharp;
32using System.Text.RegularExpressions;
33using HeuristicLab.Core;
34using HeuristicLab.Data;
35using System.Data.Linq;
36
37namespace HeuristicLab.Operators.Programmable {
38  public class ProgrammableOperator : OperatorBase {
39    private MethodInfo executeMethod;
40
41    private string myDescription;
42    public override string Description {
43      get { return myDescription; }
44    }
45    private string myCode;
46    public string Code {
47      get { return myCode; }
48      set {
49        if (value != myCode) {
50          myCode = value;
51          executeMethod = null;
52          OnCodeChanged();
53        }
54      }
55    }
56
57    public ProgrammableOperator() {
58      myCode = "Result.Data = true;";
59      myDescription = "An operator that can be programmed for arbitrary needs.";
60      AddVariableInfo(new VariableInfo("Result", "A computed variable", typeof(BoolData), VariableKind.New | VariableKind.Out));
61      executeMethod = null;
62    }
63
64    public void SetDescription(string description) {
65      if (description == null)
66        throw new NullReferenceException("description must not be null");
67
68      if (description != myDescription) {
69        myDescription = description;
70        OnDescriptionChanged();
71      }
72    }
73
74    public virtual void Compile() {
75      CodeNamespace ns = new CodeNamespace("HeuristicLab.Operators.Programmable.CustomOperators");
76      CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Operator");
77      typeDecl.IsClass = true;
78      typeDecl.TypeAttributes = TypeAttributes.Public;
79
80      CodeMemberMethod method = new CodeMemberMethod();
81      method.Name = "Execute";
82      method.ReturnType = new CodeTypeReference(typeof(IOperation));
83      method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
84      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IOperator), "op"));
85      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IScope), "scope"));
86      foreach (IVariableInfo info in VariableInfos)
87        method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName));
88      string code = myCode + "\r\n" + "return null;";
89      method.Statements.Add(new CodeSnippetStatement(code));
90      typeDecl.Members.Add(method);
91
92      ns.Types.Add(typeDecl);
93      ns.Imports.Add(new CodeNamespaceImport("System"));
94      ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
95      ns.Imports.Add(new CodeNamespaceImport("System.Text"));
96      ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
97      ns.Imports.Add(new CodeNamespaceImport("System.Data.Linq"));
98      ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core"));
99      foreach (IVariableInfo variableInfo in VariableInfos)
100        ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace));
101
102      CodeCompileUnit unit = new CodeCompileUnit();
103      unit.Namespaces.Add(ns);
104      CompilerParameters parameters = new CompilerParameters();
105      parameters.GenerateExecutable = false;
106      parameters.GenerateInMemory = true;
107      parameters.IncludeDebugInformation = false;
108      Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
109      foreach (Assembly loadedAssembly in loadedAssemblies)
110        parameters.ReferencedAssemblies.Add(loadedAssembly.Location);
111      parameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location); // add reference to version 3.5 of System.dll
112      parameters.ReferencedAssemblies.Add(typeof(DataContext).Assembly.Location); // add reference System.Data.Linq.Dll
113      CodeDomProvider provider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });  // support C# 3.0 syntax
114      CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit);
115
116      executeMethod = null;
117      if (results.Errors.HasErrors) {
118        StringWriter writer = new StringWriter();
119        CodeGeneratorOptions options = new CodeGeneratorOptions();
120        options.BlankLinesBetweenMembers = false;
121        options.ElseOnClosing = true;
122        options.IndentString = "  ";
123        provider.GenerateCodeFromCompileUnit(unit, writer, options);
124        writer.Flush();
125        string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
126        StringBuilder builder = new StringBuilder();
127        for (int i = 0; i < source.Length; i++)
128          builder.AppendLine((i + 1).ToString("###") + "     " + source[i]);
129        builder.AppendLine();
130        builder.AppendLine();
131        builder.AppendLine();
132        foreach (CompilerError error in results.Errors) {
133          builder.Append("Line " + error.Line.ToString());
134          builder.Append(", Column " + error.Column.ToString());
135          builder.AppendLine(": " + error.ErrorText);
136        }
137        throw new Exception("Compile Errors:\n\n" + builder.ToString());
138      } else {
139        Assembly assembly = results.CompiledAssembly;
140        Type[] types = assembly.GetTypes();
141        executeMethod = types[0].GetMethod("Execute");
142      }
143    }
144
145    public override object Clone(IDictionary<Guid, object> clonedObjects) {
146      ProgrammableOperator clone = (ProgrammableOperator)base.Clone(clonedObjects);
147      clone.myDescription = Description;
148      clone.myCode = Code;
149      clone.executeMethod = executeMethod;
150      return clone;
151    }
152
153    public override IOperation Apply(IScope scope) {
154      if (executeMethod == null) {
155        Compile();
156      }
157
158      // collect parameters
159      object[] parameters = new object[VariableInfos.Count + 2];
160      parameters[0] = this;
161      parameters[1] = scope;
162      int i = 2;
163      foreach (IVariableInfo info in VariableInfos) {
164        if ((info.Kind & VariableKind.New) == VariableKind.New) {
165          parameters[i] = GetVariableValue(info.FormalName, scope, false, false);
166          if (parameters[i] == null) {
167            IItem value = (IItem)Activator.CreateInstance(info.DataType);
168            if (info.Local) {
169              AddVariable(new Variable(info.ActualName, value));
170            } else {
171              scope.AddVariable(new Variable(scope.TranslateName(info.FormalName), value));
172            }
173            parameters[i] = value;
174          }
175        } else
176          parameters[i] = GetVariableValue(info.FormalName, scope, true);
177        i++;
178      }
179
180      return (IOperation)executeMethod.Invoke(null, parameters);
181    }
182
183    public override IView CreateView() {
184      return new ProgrammableOperatorView(this);
185    }
186
187    public event EventHandler DescriptionChanged;
188    protected virtual void OnDescriptionChanged() {
189      if (DescriptionChanged != null)
190        DescriptionChanged(this, new EventArgs());
191    }
192    public event EventHandler CodeChanged;
193    protected virtual void OnCodeChanged() {
194      if (CodeChanged != null)
195        CodeChanged(this, new EventArgs());
196    }
197
198    #region Persistence Methods
199    public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
200      XmlNode node = base.GetXmlNode(name, document, persistedObjects);
201      XmlNode descriptionNode = document.CreateNode(XmlNodeType.Element, "Description", null);
202      descriptionNode.InnerText = myDescription;
203      node.AppendChild(descriptionNode);
204      XmlNode codeNode = document.CreateNode(XmlNodeType.Element, "Code", null);
205      codeNode.InnerText = myCode;
206      node.AppendChild(codeNode);
207      return node;
208    }
209    public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
210      base.Populate(node, restoredObjects);
211      XmlNode descriptionNode = node.SelectSingleNode("Description");
212      myDescription = descriptionNode.InnerText;
213      XmlNode codeNode = node.SelectSingleNode("Code");
214      myCode = codeNode.InnerText;
215    }
216    #endregion
217  }
218}
Note: See TracBrowser for help on using the repository browser.