Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 1475 was 1211, checked in by gkronber, 16 years ago

merged r1210 from CEDMA refactoring branch into trunk #488 (Lazy compilation pattern in ProgrammableOperator is not thread-safe and doesn't work in thread parallel engines)

File size: 9.0 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    private object syncRoot = new object();
58
59    public ProgrammableOperator() {
60      myCode = "Result.Data = true;";
61      myDescription = "An operator that can be programmed for arbitrary needs.";
62      AddVariableInfo(new VariableInfo("Result", "A computed variable", typeof(BoolData), VariableKind.New | VariableKind.Out));
63      executeMethod = null;
64    }
65
66    public void SetDescription(string description) {
67      if (description == null)
68        throw new NullReferenceException("description must not be null");
69
70      if (description != myDescription) {
71        myDescription = description;
72        OnDescriptionChanged();
73      }
74    }
75
76    public virtual void Compile() {
77      CodeNamespace ns = new CodeNamespace("HeuristicLab.Operators.Programmable.CustomOperators");
78      CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Operator");
79      typeDecl.IsClass = true;
80      typeDecl.TypeAttributes = TypeAttributes.Public;
81
82      CodeMemberMethod method = new CodeMemberMethod();
83      method.Name = "Execute";
84      method.ReturnType = new CodeTypeReference(typeof(IOperation));
85      method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
86      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IOperator), "op"));
87      method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IScope), "scope"));
88      foreach (IVariableInfo info in VariableInfos)
89        method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName));
90      string code = myCode + "\r\n" + "return null;";
91      method.Statements.Add(new CodeSnippetStatement(code));
92      typeDecl.Members.Add(method);
93
94      ns.Types.Add(typeDecl);
95      ns.Imports.Add(new CodeNamespaceImport("System"));
96      ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
97      ns.Imports.Add(new CodeNamespaceImport("System.Text"));
98      ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
99      ns.Imports.Add(new CodeNamespaceImport("System.Data.Linq"));
100      ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core"));
101      foreach (IVariableInfo variableInfo in VariableInfos)
102        ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace));
103
104      CodeCompileUnit unit = new CodeCompileUnit();
105      unit.Namespaces.Add(ns);
106      CompilerParameters parameters = new CompilerParameters();
107      parameters.GenerateExecutable = false;
108      parameters.GenerateInMemory = true;
109      parameters.IncludeDebugInformation = false;
110      Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
111      foreach (Assembly loadedAssembly in loadedAssemblies)
112        parameters.ReferencedAssemblies.Add(loadedAssembly.Location);
113      parameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location); // add reference to version 3.5 of System.dll
114      parameters.ReferencedAssemblies.Add(typeof(DataContext).Assembly.Location); // add reference System.Data.Linq.Dll
115      CodeDomProvider provider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });  // support C# 3.0 syntax
116      CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit);
117
118      executeMethod = null;
119      if (results.Errors.HasErrors) {
120        StringWriter writer = new StringWriter();
121        CodeGeneratorOptions options = new CodeGeneratorOptions();
122        options.BlankLinesBetweenMembers = false;
123        options.ElseOnClosing = true;
124        options.IndentString = "  ";
125        provider.GenerateCodeFromCompileUnit(unit, writer, options);
126        writer.Flush();
127        string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
128        StringBuilder builder = new StringBuilder();
129        for (int i = 0; i < source.Length; i++)
130          builder.AppendLine((i + 3).ToString("###") + "     " + source[i]);
131        builder.AppendLine();
132        builder.AppendLine();
133        builder.AppendLine();
134        foreach (CompilerError error in results.Errors) {
135          builder.Append("Line " + error.Line.ToString());
136          builder.Append(", Column " + error.Column.ToString());
137          builder.AppendLine(": " + error.ErrorText);
138        }
139        throw new Exception("Compile Errors:\n\n" + builder.ToString());
140      } else {
141        Assembly assembly = results.CompiledAssembly;
142        Type[] types = assembly.GetTypes();
143        executeMethod = types[0].GetMethod("Execute");
144      }
145    }
146
147    public override object Clone(IDictionary<Guid, object> clonedObjects) {
148      ProgrammableOperator clone = (ProgrammableOperator)base.Clone(clonedObjects);
149      clone.myDescription = Description;
150      clone.myCode = Code;
151      clone.executeMethod = executeMethod;
152      return clone;
153    }
154
155    public override IOperation Apply(IScope scope) {
156      lock (syncRoot) {
157        if (executeMethod == null) {
158          Compile();
159        }
160      }
161
162      // collect parameters
163      object[] parameters = new object[VariableInfos.Count + 2];
164      parameters[0] = this;
165      parameters[1] = scope;
166      int i = 2;
167      foreach (IVariableInfo info in VariableInfos) {
168        if ((info.Kind & VariableKind.New) == VariableKind.New) {
169          parameters[i] = GetVariableValue(info.FormalName, scope, false, false);
170          if (parameters[i] == null) {
171            IItem value = (IItem)Activator.CreateInstance(info.DataType);
172            if (info.Local) {
173              AddVariable(new Variable(info.ActualName, value));
174            } else {
175              scope.AddVariable(new Variable(scope.TranslateName(info.FormalName), value));
176            }
177            parameters[i] = value;
178          }
179        } else
180          parameters[i] = GetVariableValue(info.FormalName, scope, true);
181        i++;
182      }
183
184      return (IOperation)executeMethod.Invoke(null, parameters);
185    }
186
187    public override IView CreateView() {
188      return new ProgrammableOperatorView(this);
189    }
190
191    public event EventHandler DescriptionChanged;
192    protected virtual void OnDescriptionChanged() {
193      if (DescriptionChanged != null)
194        DescriptionChanged(this, new EventArgs());
195    }
196    public event EventHandler CodeChanged;
197    protected virtual void OnCodeChanged() {
198      if (CodeChanged != null)
199        CodeChanged(this, new EventArgs());
200    }
201
202    #region Persistence Methods
203    public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
204      XmlNode node = base.GetXmlNode(name, document, persistedObjects);
205      XmlNode descriptionNode = document.CreateNode(XmlNodeType.Element, "Description", null);
206      descriptionNode.InnerText = myDescription;
207      node.AppendChild(descriptionNode);
208      XmlNode codeNode = document.CreateNode(XmlNodeType.Element, "Code", null);
209      codeNode.InnerText = myCode;
210      node.AppendChild(codeNode);
211      return node;
212    }
213    public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
214      base.Populate(node, restoredObjects);
215      XmlNode descriptionNode = node.SelectSingleNode("Description");
216      myDescription = descriptionNode.InnerText;
217      XmlNode codeNode = node.SelectSingleNode("Code");
218      myCode = codeNode.InnerText;
219    }
220    #endregion
221  }
222}
Note: See TracBrowser for help on using the repository browser.