Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 2533 was 2533, checked in by mkommend, 14 years ago

added try catch block while querying the loaded assemblies (ticket #807)

File size: 9.2 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        try {
113          parameters.ReferencedAssemblies.Add(loadedAssembly.Location);
114        }
115        catch (NotSupportedException) {
116          //NotSupportedException is thrown while accessing the Location property of the anonymously hosted dynamic methods assembly,
117          //which is related to LINQ queries
118        }
119      }
120      parameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location); // add reference to version 3.5 of System.dll
121      parameters.ReferencedAssemblies.Add(typeof(DataContext).Assembly.Location); // add reference System.Data.Linq.Dll
122      CodeDomProvider provider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });  // support C# 3.0 syntax
123      CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit);
124
125      executeMethod = null;
126      if (results.Errors.HasErrors) {
127        StringWriter writer = new StringWriter();
128        CodeGeneratorOptions options = new CodeGeneratorOptions();
129        options.BlankLinesBetweenMembers = false;
130        options.ElseOnClosing = true;
131        options.IndentString = "  ";
132        provider.GenerateCodeFromCompileUnit(unit, writer, options);
133        writer.Flush();
134        string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
135        StringBuilder builder = new StringBuilder();
136        for (int i = 0; i < source.Length; i++)
137          builder.AppendLine((i + 3).ToString("###") + "     " + source[i]);
138        builder.AppendLine();
139        builder.AppendLine();
140        builder.AppendLine();
141        foreach (CompilerError error in results.Errors) {
142          builder.Append("Line " + error.Line.ToString());
143          builder.Append(", Column " + error.Column.ToString());
144          builder.AppendLine(": " + error.ErrorText);
145        }
146        throw new Exception("Compile Errors:\n\n" + builder.ToString());
147      } else {
148        Assembly assembly = results.CompiledAssembly;
149        Type[] types = assembly.GetTypes();
150        executeMethod = types[0].GetMethod("Execute");
151      }
152    }
153
154    public override object Clone(IDictionary<Guid, object> clonedObjects) {
155      ProgrammableOperator clone = (ProgrammableOperator)base.Clone(clonedObjects);
156      clone.myDescription = Description;
157      clone.myCode = Code;
158      clone.executeMethod = executeMethod;
159      return clone;
160    }
161
162    public override IOperation Apply(IScope scope) {
163      lock (syncRoot) {
164        if (executeMethod == null) {
165          Compile();
166        }
167      }
168
169      // collect parameters
170      object[] parameters = new object[VariableInfos.Count + 2];
171      parameters[0] = this;
172      parameters[1] = scope;
173      int i = 2;
174      foreach (IVariableInfo info in VariableInfos) {
175        if ((info.Kind & VariableKind.New) == VariableKind.New) {
176          parameters[i] = GetVariableValue(info.FormalName, scope, false, false);
177          if (parameters[i] == null) {
178            IItem value = (IItem)Activator.CreateInstance(info.DataType);
179            if (info.Local) {
180              AddVariable(new Variable(info.ActualName, value));
181            } else {
182              scope.AddVariable(new Variable(scope.TranslateName(info.FormalName), value));
183            }
184            parameters[i] = value;
185          }
186        } else
187          parameters[i] = GetVariableValue(info.FormalName, scope, true);
188        i++;
189      }
190
191      return (IOperation)executeMethod.Invoke(null, parameters);
192    }
193
194    public override IView CreateView() {
195      return new ProgrammableOperatorView(this);
196    }
197
198    public event EventHandler DescriptionChanged;
199    protected virtual void OnDescriptionChanged() {
200      if (DescriptionChanged != null)
201        DescriptionChanged(this, new EventArgs());
202    }
203    public event EventHandler CodeChanged;
204    protected virtual void OnCodeChanged() {
205      if (CodeChanged != null)
206        CodeChanged(this, new EventArgs());
207    }
208
209    #region Persistence Methods
210    public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary<Guid, IStorable> persistedObjects) {
211      XmlNode node = base.GetXmlNode(name, document, persistedObjects);
212      XmlNode descriptionNode = document.CreateNode(XmlNodeType.Element, "Description", null);
213      descriptionNode.InnerText = myDescription;
214      node.AppendChild(descriptionNode);
215      XmlNode codeNode = document.CreateNode(XmlNodeType.Element, "Code", null);
216      codeNode.InnerText = myCode;
217      node.AppendChild(codeNode);
218      return node;
219    }
220    public override void Populate(XmlNode node, IDictionary<Guid, IStorable> restoredObjects) {
221      base.Populate(node, restoredObjects);
222      XmlNode descriptionNode = node.SelectSingleNode("Description");
223      myDescription = descriptionNode.InnerText;
224      XmlNode codeNode = node.SelectSingleNode("Code");
225      myCode = codeNode.InnerText;
226    }
227    #endregion
228  }
229}
Note: See TracBrowser for help on using the repository browser.