#region License Information
/* HeuristicLab
* Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Linq;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text.RegularExpressions;
using HeuristicLab.Core;
using HeuristicLab.Data;
using System.Data.Linq;
namespace HeuristicLab.Operators.Programmable {
public class ProgrammableOperator : OperatorBase {
private MethodInfo executeMethod;
private string myDescription;
public override string Description {
get { return myDescription; }
}
private string myCode;
public string Code {
get { return myCode; }
set {
if (value != myCode) {
myCode = value;
executeMethod = null;
OnCodeChanged();
}
}
}
public ProgrammableOperator() {
myCode = "Result.Data = true;";
myDescription = "An operator that can be programmed for arbitrary needs.";
AddVariableInfo(new VariableInfo("Result", "A computed variable", typeof(BoolData), VariableKind.New | VariableKind.Out));
executeMethod = null;
}
public void SetDescription(string description) {
if (description == null)
throw new NullReferenceException("description must not be null");
if (description != myDescription) {
myDescription = description;
OnDescriptionChanged();
}
}
public virtual void Compile() {
CodeNamespace ns = new CodeNamespace("HeuristicLab.Operators.Programmable.CustomOperators");
CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Operator");
typeDecl.IsClass = true;
typeDecl.TypeAttributes = TypeAttributes.Public;
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "Execute";
method.ReturnType = new CodeTypeReference(typeof(IOperation));
method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IOperator), "op"));
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IScope), "scope"));
foreach (IVariableInfo info in VariableInfos)
method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName));
string code = myCode + "\r\n" + "return null;";
method.Statements.Add(new CodeSnippetStatement(code));
typeDecl.Members.Add(method);
ns.Types.Add(typeDecl);
ns.Imports.Add(new CodeNamespaceImport("System"));
ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
ns.Imports.Add(new CodeNamespaceImport("System.Text"));
ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
ns.Imports.Add(new CodeNamespaceImport("System.Data.Linq"));
ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core"));
foreach (IVariableInfo variableInfo in VariableInfos)
ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace));
CodeCompileUnit unit = new CodeCompileUnit();
unit.Namespaces.Add(ns);
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = false;
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly loadedAssembly in loadedAssemblies)
parameters.ReferencedAssemblies.Add(loadedAssembly.Location);
parameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location); // add reference to version 3.5 of System.dll
parameters.ReferencedAssemblies.Add(typeof(DataContext).Assembly.Location); // add reference System.Data.Linq.Dll
CodeDomProvider provider = new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v3.5" } }); // support C# 3.0 syntax
CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit);
executeMethod = null;
if (results.Errors.HasErrors) {
StringWriter writer = new StringWriter();
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BlankLinesBetweenMembers = false;
options.ElseOnClosing = true;
options.IndentString = " ";
provider.GenerateCodeFromCompileUnit(unit, writer, options);
writer.Flush();
string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < source.Length; i++)
builder.AppendLine((i + 3).ToString("###") + " " + source[i]);
builder.AppendLine();
builder.AppendLine();
builder.AppendLine();
foreach (CompilerError error in results.Errors) {
builder.Append("Line " + error.Line.ToString());
builder.Append(", Column " + error.Column.ToString());
builder.AppendLine(": " + error.ErrorText);
}
throw new Exception("Compile Errors:\n\n" + builder.ToString());
} else {
Assembly assembly = results.CompiledAssembly;
Type[] types = assembly.GetTypes();
executeMethod = types[0].GetMethod("Execute");
}
}
public override object Clone(IDictionary clonedObjects) {
ProgrammableOperator clone = (ProgrammableOperator)base.Clone(clonedObjects);
clone.myDescription = Description;
clone.myCode = Code;
clone.executeMethod = executeMethod;
return clone;
}
public override IOperation Apply(IScope scope) {
if (executeMethod == null) {
Compile();
}
// collect parameters
object[] parameters = new object[VariableInfos.Count + 2];
parameters[0] = this;
parameters[1] = scope;
int i = 2;
foreach (IVariableInfo info in VariableInfos) {
if ((info.Kind & VariableKind.New) == VariableKind.New) {
parameters[i] = GetVariableValue(info.FormalName, scope, false, false);
if (parameters[i] == null) {
IItem value = (IItem)Activator.CreateInstance(info.DataType);
if (info.Local) {
AddVariable(new Variable(info.ActualName, value));
} else {
scope.AddVariable(new Variable(scope.TranslateName(info.FormalName), value));
}
parameters[i] = value;
}
} else
parameters[i] = GetVariableValue(info.FormalName, scope, true);
i++;
}
return (IOperation)executeMethod.Invoke(null, parameters);
}
public override IView CreateView() {
return new ProgrammableOperatorView(this);
}
public event EventHandler DescriptionChanged;
protected virtual void OnDescriptionChanged() {
if (DescriptionChanged != null)
DescriptionChanged(this, new EventArgs());
}
public event EventHandler CodeChanged;
protected virtual void OnCodeChanged() {
if (CodeChanged != null)
CodeChanged(this, new EventArgs());
}
#region Persistence Methods
public override XmlNode GetXmlNode(string name, XmlDocument document, IDictionary persistedObjects) {
XmlNode node = base.GetXmlNode(name, document, persistedObjects);
XmlNode descriptionNode = document.CreateNode(XmlNodeType.Element, "Description", null);
descriptionNode.InnerText = myDescription;
node.AppendChild(descriptionNode);
XmlNode codeNode = document.CreateNode(XmlNodeType.Element, "Code", null);
codeNode.InnerText = myCode;
node.AppendChild(codeNode);
return node;
}
public override void Populate(XmlNode node, IDictionary restoredObjects) {
base.Populate(node, restoredObjects);
XmlNode descriptionNode = node.SelectSingleNode("Description");
myDescription = descriptionNode.InnerText;
XmlNode codeNode = node.SelectSingleNode("Code");
myCode = codeNode.InnerText;
}
#endregion
}
}