#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Core.Networks;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Scripting;
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace HeuristicLab.Networks.Programmable {
[Item("ProgrammableNetworkItem", "Abstract base class for programmable items of a network.")]
[StorableClass]
public abstract class ProgrammableNetworkItem : Item, IProgrammableNetworkItem {
public static new Image StaticItemImage {
get { return HeuristicLab.Common.Resources.VSImageLibrary.Class; }
}
#region Item Members
public override string ItemName {
get { return CompiledNetworkItem.ItemName; }
}
public override string ItemDescription {
get { return CompiledNetworkItem.ItemDescription; }
}
public override Image ItemImage {
get { return CompiledNetworkItem.ItemImage; }
}
#endregion
#region NamedItem Members
[Storable]
private string name;
public string Name {
get { return CompiledNetworkItem.Name; }
set { CompiledNetworkItem.Name = value; }
}
public bool CanChangeName {
get { return CompiledNetworkItem.CanChangeName; }
}
[Storable]
private string description;
public string Description {
get { return CompiledNetworkItem.Description; }
set { CompiledNetworkItem.Description = value; }
}
public virtual bool CanChangeDescription {
get { return CompiledNetworkItem.CanChangeDescription; }
}
public event EventHandler> NameChanging;
private void OnNameChanging(CancelEventArgs e) {
var handler = NameChanging;
if (handler != null) handler(this, e);
}
public event EventHandler NameChanged;
private void OnNameChanged() {
var handler = NameChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler DescriptionChanged;
private void OnDescriptionChanged() {
var handler = DescriptionChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
#endregion
#region NetworkItem Members
public INetworkItem Parent {
get { return CompiledNetworkItem.Parent; }
}
public IEnumerable Children {
get { return CompiledNetworkItem.Children; }
}
#endregion
protected virtual string CodeTemplate {
get { return ReadCodeTemplate("HeuristicLab.Networks.Programmable.ProgrammableNetworkItemCode.cs"); }
}
[Storable]
private string code;
public string Code {
get { return code; }
set {
if (!CanChangeCode) throw new NotSupportedException("Code cannot be changed.");
if (value != code) {
code = value;
OnCodeChanged();
}
}
}
private CompilerErrorCollection compileErrors;
public CompilerErrorCollection CompileErrors {
get { return compileErrors; }
protected set {
compileErrors = value;
OnCompileErrorsChanged();
}
}
public virtual bool CanChangeCode {
get { return false; }
}
[Storable]
private VariableStore variableStore;
public VariableStore VariableStore {
get { return variableStore; }
}
private Type compiledNetworkItemType;
private CompiledProgrammableNetworkItem compiledNetworkItem;
protected CompiledProgrammableNetworkItem CompiledNetworkItem {
get {
if (compiledNetworkItem == null)
CompiledNetworkItem = (CompiledProgrammableNetworkItem)Activator.CreateInstance(compiledNetworkItemType, this);
return compiledNetworkItem;
}
private set {
if (compiledNetworkItem != value) {
if (compiledNetworkItem != null) {
DeregisterCompiledNetworkItemEvents();
compiledNetworkItem.DeregisterEvents();
}
compiledNetworkItem = value;
if (compiledNetworkItem != null) {
RegisterCompiledNetworkItemEvents();
compiledNetworkItem.RegisterEvents();
}
OnToStringChanged();
OnItemImageChanged();
OnNameChanged();
OnDescriptionChanged();
}
}
}
[StorableConstructor]
protected ProgrammableNetworkItem(bool deserializing) : base(deserializing) { }
protected ProgrammableNetworkItem(ProgrammableNetworkItem original, Cloner cloner)
: base(original, cloner) {
// name and description are cloned in CompiledProgrammableNetworkItem
code = original.code;
if (original.compileErrors != null)
compileErrors = new CompilerErrorCollection(original.compileErrors);
variableStore = cloner.Clone(original.variableStore);
CompiledNetworkItem = cloner.Clone(original.compiledNetworkItem);
}
protected ProgrammableNetworkItem()
: base() {
name = "ProgrammableNetworkItem";
description = string.Empty;
code = CodeTemplate;
variableStore = new VariableStore();
Compile();
}
protected ProgrammableNetworkItem(string name)
: base() {
this.name = string.IsNullOrEmpty(name) ? string.Empty : name;
description = string.Empty;
code = CodeTemplate;
variableStore = new VariableStore();
Compile();
}
protected ProgrammableNetworkItem(string name, string description)
: base() {
this.name = string.IsNullOrEmpty(name) ? string.Empty : name;
this.description = string.IsNullOrEmpty(description) ? string.Empty : description;
code = CodeTemplate;
variableStore = new VariableStore();
Compile();
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
Compile();
}
public override string ToString() {
return CompiledNetworkItem.ToString();
}
#region Compilation
public virtual void Compile() {
var codeProvider = new CSharpCodeProvider(
new Dictionary {
{"CompilerVersion", "v4.0"}, // support C# 4.0 syntax
}
);
var compilerParams = new CompilerParameters {
GenerateExecutable = false,
GenerateInMemory = true,
IncludeDebugInformation = true,
WarningLevel = 4
};
compilerParams.ReferencedAssemblies.AddRange(
GetAssemblies().Select(a => a.Location).ToArray()
);
var results = codeProvider.CompileAssemblyFromSource(compilerParams, code);
CompileErrors = results.Errors;
if (results.Errors.HasErrors) {
var sb = new StringBuilder();
foreach (CompilerError error in results.Errors) {
sb.Append(error.Line).Append(':')
.Append(error.Column).Append(": ")
.AppendLine(error.ErrorText);
}
throw new InvalidOperationException(string.Format("Compilation of \"{0}\" failed:{1}{2}",
Name, Environment.NewLine, sb.ToString()));
}
compiledNetworkItemType = results.CompiledAssembly.GetTypes()
.Single(x => typeof(CompiledProgrammableNetworkItem).IsAssignableFrom(x));
CompiledNetworkItem = null;
}
public virtual IEnumerable GetAssemblies() {
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic && File.Exists(a.Location))
.ToList();
assemblies.Add(typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly); // for dlr functionality
return assemblies;
}
#endregion
public event EventHandler CodeChanged;
protected virtual void OnCodeChanged() {
var handler = CodeChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public event EventHandler CompileErrorsChanged;
protected virtual void OnCompileErrorsChanged() {
var handler = CompileErrorsChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
#region CompiledNetworkItem Events
protected virtual void RegisterCompiledNetworkItemEvents() {
CompiledNetworkItem.ToStringChanged += CompiledNetworkItem_ToStringChanged;
CompiledNetworkItem.ItemImageChanged += CompiledNetworkItem_ItemImageChanged;
CompiledNetworkItem.NameChanging += CompiledNetworkItem_NameChanging;
CompiledNetworkItem.NameChanged += CompiledNetworkItem_NameChanged;
CompiledNetworkItem.DescriptionChanged += CompiledNetworkItem_DescriptionChanged;
}
protected virtual void DeregisterCompiledNetworkItemEvents() {
CompiledNetworkItem.ToStringChanged -= CompiledNetworkItem_ToStringChanged;
CompiledNetworkItem.ItemImageChanged -= CompiledNetworkItem_ItemImageChanged;
CompiledNetworkItem.NameChanging -= CompiledNetworkItem_NameChanging;
CompiledNetworkItem.NameChanged -= CompiledNetworkItem_NameChanged;
CompiledNetworkItem.DescriptionChanged -= CompiledNetworkItem_DescriptionChanged;
}
private void CompiledNetworkItem_ToStringChanged(object sender, EventArgs e) {
OnToStringChanged();
}
private void CompiledNetworkItem_ItemImageChanged(object sender, EventArgs e) {
OnItemImageChanged();
}
private void CompiledNetworkItem_NameChanging(object sender, CancelEventArgs e) {
OnNameChanging(e);
}
private void CompiledNetworkItem_NameChanged(object sender, EventArgs e) {
OnNameChanged();
}
private void CompiledNetworkItem_DescriptionChanged(object sender, EventArgs e) {
OnDescriptionChanged();
}
#endregion
#region CompiledProgrammableNetworkItem
[Item("CompiledProgrammableNetworkItem", "Abstract base class for compiled programmable items of a network.")]
public abstract class CompiledProgrammableNetworkItem : Item, INetworkItem {
public static new Image StaticItemImage {
get { return HeuristicLab.Common.Resources.VSImageLibrary.Class; }
}
#region NamedItem Members
public string Name {
get { return Context.name; }
set {
if (!CanChangeName) throw new NotSupportedException("Name cannot be changed.");
if (!(Context.name.Equals(value) || (value == null) && (Context.name == string.Empty))) {
CancelEventArgs e = value == null ? new CancelEventArgs(string.Empty) : new CancelEventArgs(value);
OnNameChanging(e);
if (!e.Cancel) {
Context.name = value == null ? string.Empty : value;
OnNameChanged();
}
}
}
}
public virtual bool CanChangeName {
get { return true; }
}
public string Description {
get { return Context.description; }
set {
if (!CanChangeDescription) throw new NotSupportedException("Description cannot be changed.");
if (!(Context.description.Equals(value) || (value == null) && (Context.description == string.Empty))) {
Context.description = value == null ? string.Empty : value;
OnDescriptionChanged();
}
}
}
public virtual bool CanChangeDescription {
get { return true; }
}
public event EventHandler> NameChanging;
protected virtual void OnNameChanging(CancelEventArgs e) {
var handler = NameChanging;
if (handler != null) handler(this, e);
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged() {
var handler = NameChanged;
if (handler != null) handler(this, EventArgs.Empty);
OnToStringChanged();
}
public event EventHandler DescriptionChanged;
protected virtual void OnDescriptionChanged() {
var handler = DescriptionChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
#endregion
#region NetworkItem Members
private INetworkItem parent;
public INetworkItem Parent {
get { return parent; }
protected set {
if (value != parent) {
parent = value;
}
}
}
public virtual IEnumerable Children {
get { return Enumerable.Empty(); }
}
#endregion
protected ProgrammableNetworkItem Context { get; private set; }
private Variables variables;
protected Variables Variables {
get {
if (variables == null) variables = new Variables(Context.variableStore);
return variables;
}
}
protected dynamic Vars {
get { return Variables; }
}
protected CompiledProgrammableNetworkItem(CompiledProgrammableNetworkItem original, Cloner cloner)
: base(original, cloner) {
Context = cloner.Clone(original.Context);
Context.name = original.Context.name;
Context.description = original.Context.description;
}
protected CompiledProgrammableNetworkItem(ProgrammableNetworkItem context)
: base() {
Context = context;
}
public virtual void Initialize() {
Variables.Clear();
}
public override string ToString() {
return Name;
}
#region Events
public virtual void RegisterEvents() { }
public virtual void DeregisterEvents() { }
#endregion
}
#endregion
#region Helpers
protected virtual string ReadCodeTemplate(string templateName) {
using (var stream = Assembly.GetAssembly(this.GetType()).GetManifestResourceStream(templateName))
using (var reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}
#endregion
}
}