Index: trunk/sources/HeuristicLab.Scripting/3.3/CSharpScript.cs
===================================================================
--- trunk/sources/HeuristicLab.Scripting/3.3/CSharpScript.cs	(revision 10731)
+++ trunk/sources/HeuristicLab.Scripting/3.3/CSharpScript.cs	(revision 10731)
@@ -0,0 +1,167 @@
+﻿#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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Scripting {
+  [Item("C# Script", "An empty C# script.")]
+  [Creatable("Scripts")]
+  [StorableClass]
+  public class CSharpScript : Script, IStorableContent {
+    #region Constants
+    protected const string ExecuteMethodName = "Execute";
+    protected override string CodeTemplate {
+      get {
+        return @"// use 'vars' to access variables in the script's variable store (e.g. vars.x = 5)
+// use 'vars.Contains(string)' to check if a variable exists
+// use 'vars.Clear()' to remove all variables
+// use 'foreach (KeyValuePair<string, object> v in vars) { ... }' to iterate over all variables
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+
+public class MyScript : HeuristicLab.Scripting.CSharpScriptBase {
+  public override void Main() {
+    // type your code here
+  }
+
+  // implement further classes and methods
+
+}";
+      }
+    }
+    #endregion
+
+    #region Fields & Properties
+    protected CSharpScriptBase CompiledScript;
+
+    public string Filename { get; set; }
+
+    [Storable]
+    private VariableStore variableStore;
+    public VariableStore VariableStore {
+      get { return variableStore; }
+    }
+    #endregion
+
+    #region Construction & Initialization
+    [StorableConstructor]
+    protected CSharpScript(bool deserializing) : base(deserializing) { }
+    protected CSharpScript(CSharpScript original, Cloner cloner)
+      : base(original, cloner) {
+      variableStore = new VariableStore();
+    }
+    public CSharpScript() {
+      variableStore = new VariableStore();
+    }
+    public CSharpScript(string code)
+      : base(code) {
+      variableStore = new VariableStore();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new CSharpScript(this, cloner);
+    }
+    #endregion
+
+    protected virtual void RegisterScriptEvents() {
+      if (CompiledScript != null)
+        CompiledScript.ConsoleOutputChanged += CompiledScriptOnConsoleOutputChanged;
+    }
+
+    protected virtual void DeregisterScriptEvents() {
+      if (CompiledScript != null)
+        CompiledScript.ConsoleOutputChanged -= CompiledScriptOnConsoleOutputChanged;
+    }
+
+    #region Compilation
+
+    public override Assembly Compile() {
+      CompiledScript = null;
+      var assembly = base.Compile();
+      var types = assembly.GetTypes();
+      DeregisterScriptEvents();
+      CompiledScript = (CSharpScriptBase)Activator.CreateInstance(types.First(x => typeof(CSharpScriptBase).IsAssignableFrom(x)));
+      RegisterScriptEvents();
+      return assembly;
+    }
+    #endregion
+
+    protected Thread ScriptThread;
+    public virtual void Execute() {
+      if (CompiledScript == null) return;
+      var executeMethod = typeof(CSharpScriptBase).GetMethod(ExecuteMethodName, BindingFlags.NonPublic | BindingFlags.Instance);
+      if (executeMethod != null) {
+        ScriptThread = new Thread(() => {
+          Exception ex = null;
+          try {
+            OnScriptExecutionStarted();
+            executeMethod.Invoke(CompiledScript, new object[] { VariableStore });
+          } catch (ThreadAbortException) {
+            // the execution was cancelled by the user
+          } catch (TargetInvocationException e) {
+            ex = e.InnerException;
+          } finally {
+            OnScriptExecutionFinished(ex);
+          }
+        });
+        ScriptThread.Start();
+      }
+    }
+
+    public virtual void Kill() {
+      if (ScriptThread.IsAlive)
+        ScriptThread.Abort();
+    }
+
+    protected virtual void CompiledScriptOnConsoleOutputChanged(object sender, EventArgs<string> e) {
+      OnConsoleOutputChanged(e.Value);
+    }
+
+    public event EventHandler ScriptExecutionStarted;
+    protected virtual void OnScriptExecutionStarted() {
+      var handler = ScriptExecutionStarted;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    public event EventHandler<EventArgs<Exception>> ScriptExecutionFinished;
+    protected virtual void OnScriptExecutionFinished(Exception e) {
+      var handler = ScriptExecutionFinished;
+      if (handler != null) handler(this, new EventArgs<Exception>(e));
+    }
+
+    public event EventHandler<EventArgs<string>> ConsoleOutputChanged;
+    protected virtual void OnConsoleOutputChanged(string args) {
+      var handler = ConsoleOutputChanged;
+      if (handler != null) handler(this, new EventArgs<string>(args));
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Scripting/3.3/CSharpScriptBase.cs
===================================================================
--- trunk/sources/HeuristicLab.Scripting/3.3/CSharpScriptBase.cs	(revision 10731)
+++ trunk/sources/HeuristicLab.Scripting/3.3/CSharpScriptBase.cs	(revision 10731)
@@ -0,0 +1,108 @@
+﻿#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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.IO;
+using System.Text;
+using HeuristicLab.Common;
+
+namespace HeuristicLab.Scripting {
+  public abstract class CSharpScriptBase {
+    protected dynamic vars;
+
+    private readonly EventWriter console;
+    protected EventWriter Console {
+      get { return console; }
+    }
+
+    protected CSharpScriptBase() {
+      console = new EventWriter(this);
+    }
+
+    public abstract void Main();
+
+    internal void Execute(VariableStore variableStore) {
+      vars = new Variables(variableStore);
+      Main();
+    }
+
+    protected internal event EventHandler<EventArgs<string>> ConsoleOutputChanged;
+    private void OnConsoleOutputChanged(string args) {
+      var handler = ConsoleOutputChanged;
+      if (handler != null) handler(null, new EventArgs<string>(args));
+    }
+
+    protected class EventWriter : TextWriter {
+      private readonly CSharpScriptBase usb;
+
+      public EventWriter(CSharpScriptBase usb) {
+        this.usb = usb;
+      }
+
+      public override Encoding Encoding {
+        get { return Encoding.UTF8; }
+      }
+
+      #region Write/WriteLine Overrides
+      #region Write
+      public override void Write(bool value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(char value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(char[] buffer) { usb.OnConsoleOutputChanged(new string(buffer)); }
+      public override void Write(char[] buffer, int index, int count) { usb.OnConsoleOutputChanged(new string(buffer, index, count)); }
+      public override void Write(decimal value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(double value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(float value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(int value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(long value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(object value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(string value) { usb.OnConsoleOutputChanged(value); }
+      public override void Write(string format, object arg0) { usb.OnConsoleOutputChanged(string.Format(format, arg0)); }
+      public override void Write(string format, object arg0, object arg1) { usb.OnConsoleOutputChanged(string.Format(format, arg0, arg0)); }
+      public override void Write(string format, object arg0, object arg1, object arg2) { usb.OnConsoleOutputChanged(string.Format(format, arg0, arg1, arg2)); }
+      public override void Write(string format, params object[] arg) { usb.OnConsoleOutputChanged(string.Format(format, arg)); }
+      public override void Write(uint value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      public override void Write(ulong value) { usb.OnConsoleOutputChanged(value.ToString()); }
+      #endregion
+
+      #region WriteLine
+      public override void WriteLine() { usb.OnConsoleOutputChanged(Environment.NewLine); }
+      public override void WriteLine(bool value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(char value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(char[] buffer) { usb.OnConsoleOutputChanged(new string(buffer) + Environment.NewLine); }
+      public override void WriteLine(char[] buffer, int index, int count) { usb.OnConsoleOutputChanged(new string(buffer, index, count) + Environment.NewLine); }
+      public override void WriteLine(decimal value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(double value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(float value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(int value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(long value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(object value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(string value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(string format, object arg0) { usb.OnConsoleOutputChanged(string.Format(format, arg0) + Environment.NewLine); }
+      public override void WriteLine(string format, object arg0, object arg1) { usb.OnConsoleOutputChanged(string.Format(format, arg0, arg1) + Environment.NewLine); }
+      public override void WriteLine(string format, object arg0, object arg1, object arg2) { usb.OnConsoleOutputChanged(string.Format(format, arg0, arg1, arg2) + Environment.NewLine); }
+      public override void WriteLine(string format, params object[] arg) { usb.OnConsoleOutputChanged(string.Format(format, arg) + Environment.NewLine); }
+      public override void WriteLine(uint value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      public override void WriteLine(ulong value) { usb.OnConsoleOutputChanged(value + Environment.NewLine); }
+      #endregion
+      #endregion
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj
===================================================================
--- trunk/sources/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj	(revision 10727)
+++ trunk/sources/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj	(revision 10731)
@@ -85,9 +85,10 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="CSharpScript.cs" />
     <Compile Include="Variables.cs" />
     <Compile Include="VariableStore.cs" />
     <None Include="Plugin.cs.frame" />
     <Compile Include="Script.cs" />
-    <Compile Include="UserScriptBase.cs" />
+    <Compile Include="CSharpScriptBase.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
Index: trunk/sources/HeuristicLab.Scripting/3.3/Script.cs
===================================================================
--- trunk/sources/HeuristicLab.Scripting/3.3/Script.cs	(revision 10727)
+++ trunk/sources/HeuristicLab.Scripting/3.3/Script.cs	(revision 10731)
@@ -29,6 +29,4 @@
 using System.Reflection;
 using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
 using HeuristicLab.Common;
 using HeuristicLab.Common.Resources;
@@ -38,38 +36,11 @@
 
 namespace HeuristicLab.Scripting {
-  [Item("C# Script", "An empty C# script.")]
-  [Creatable("Scripts")]
   [StorableClass]
-  public sealed class Script : NamedItem, IStorableContent {
-    #region Constants
-    private const string ExecuteMethodName = "Execute";
-    private const string CodeTemplate =
-@"// use 'vars' to access variables in the script's variable store (e.g. vars.x = 5)
-// use 'vars.Contains(string)' to check if a variable exists
-// use 'vars.Clear()' to remove all variables
-// use 'foreach (KeyValuePair<string, object> v in vars) { ... }' to iterate over all variables
-
-using System;
-using System.Linq;
-using System.Collections.Generic;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Data;
-
-public class UserScript : HeuristicLab.Scripting.UserScriptBase {
-  public override void Main() {
-    // type your code here
-  }
-
-  // implement further classes and methods
-
-}";
-    #endregion
+  public class Script : NamedItem {
+    protected virtual string CodeTemplate {
+      get { return string.Empty; }
+    }
 
     #region Fields & Properties
-    private UserScriptBase compiledScript;
-
-    public string Filename { get; set; }
-
     public static new Image StaticItemImage {
       get { return VSImageLibrary.Script; }
@@ -77,17 +48,10 @@
 
     [Storable]
-    private VariableStore variableStore;
-    public VariableStore VariableStore {
-      get { return variableStore; }
-    }
-
-    [Storable]
     private string code;
-    public string Code {
+    public virtual string Code {
       get { return code; }
       set {
         if (value == code) return;
         code = value;
-        compiledScript = null;
         OnCodeChanged();
       }
@@ -95,10 +59,10 @@
 
     private string compilationUnitCode;
-    public string CompilationUnitCode {
+    public virtual string CompilationUnitCode {
       get { return compilationUnitCode; }
     }
 
     private CompilerErrorCollection compileErrors;
-    public CompilerErrorCollection CompileErrors {
+    public virtual CompilerErrorCollection CompileErrors {
       get { return compileErrors; }
       private set {
@@ -111,9 +75,8 @@
     #region Construction & Initialization
     [StorableConstructor]
-    private Script(bool deserializing) : base(deserializing) { }
-    private Script(Script original, Cloner cloner)
+    protected Script(bool deserializing) : base(deserializing) { }
+    protected Script(Script original, Cloner cloner)
       : base(original, cloner) {
       code = original.code;
-      variableStore = new VariableStore();
       compilationUnitCode = original.compilationUnitCode;
       if (original.compileErrors != null)
@@ -124,5 +87,4 @@
       description = ItemDescription;
       code = CodeTemplate;
-      variableStore = new VariableStore();
     }
     public Script(string code)
@@ -136,22 +98,15 @@
     #endregion
 
-    private void RegisterScriptEvents() {
-      if (compiledScript == null) return;
-      compiledScript.ConsoleOutputChanged += compiledScript_ConsoleOutputChanged;
+    #region Compilation
+    protected virtual CSharpCodeProvider CodeProvider {
+      get {
+        return new CSharpCodeProvider(
+          new Dictionary<string, string> {
+                {"CompilerVersion", "v4.0"}, // support C# 4.0 syntax
+              });
+      }
     }
 
-    private void DeregisterScriptEvents() {
-      if (compiledScript == null) return;
-      compiledScript.ConsoleOutputChanged -= compiledScript_ConsoleOutputChanged;
-    }
-
-    #region Compilation
-    private CSharpCodeProvider codeProvider =
-      new CSharpCodeProvider(
-        new Dictionary<string, string> {
-          { "CompilerVersion", "v4.0" },  // support C# 4.0 syntax
-        });
-
-    private CompilerResults DoCompile() {
+    protected virtual CompilerResults DoCompile() {
       var parameters = new CompilerParameters {
         GenerateExecutable = false,
@@ -166,5 +121,5 @@
       var unit = CreateCompilationUnit();
       var writer = new StringWriter();
-      codeProvider.GenerateCodeFromCompileUnit(
+      CodeProvider.GenerateCodeFromCompileUnit(
         unit,
         writer,
@@ -174,10 +129,9 @@
         });
       compilationUnitCode = writer.ToString();
-      return codeProvider.CompileAssemblyFromDom(parameters, unit);
+      return CodeProvider.CompileAssemblyFromDom(parameters, unit);
     }
 
-    public void Compile() {
+    public virtual Assembly Compile() {
       var results = DoCompile();
-      compiledScript = null;
       CompileErrors = results.Errors;
       if (results.Errors.HasErrors) {
@@ -188,18 +142,12 @@
             .AppendLine(error.ErrorText);
         }
-        throw new Exception(string.Format(
-          "Compilation of \"{0}\" failed:{1}{2}",
-          Name, Environment.NewLine,
-          sb.ToString()));
+        throw new Exception(string.Format("Compilation of \"{0}\" failed:{1}{2}",
+          Name, Environment.NewLine, sb.ToString()));
       } else {
-        var assembly = results.CompiledAssembly;
-        var types = assembly.GetTypes();
-        DeregisterScriptEvents();
-        compiledScript = (UserScriptBase)Activator.CreateInstance(types[0]);
-        RegisterScriptEvents();
+        return results.CompiledAssembly;
       }
     }
 
-    public IEnumerable<Assembly> GetAssemblies() {
+    public virtual IEnumerable<Assembly> GetAssemblies() {
       var assemblies = new List<Assembly>();
       foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) {
@@ -217,58 +165,12 @@
     }
 
-    private readonly Regex SafeTypeNameCharRegex = new Regex("[_a-zA-Z0-9]+");
-    private readonly Regex SafeTypeNameRegex = new Regex("[_a-zA-Z][_a-zA-Z0-9]*");
-
-    private CodeCompileUnit CreateCompilationUnit() {
+    protected virtual CodeCompileUnit CreateCompilationUnit() {
       var unit = new CodeSnippetCompileUnit(code);
       return unit;
     }
-
-    public string CompiledTypeName {
-      get {
-        var sb = new StringBuilder();
-        var strings = SafeTypeNameCharRegex.Matches(Name)
-                                           .Cast<Match>()
-                                           .Select(m => m.Value);
-        foreach (string s in strings)
-          sb.Append(s);
-        return SafeTypeNameRegex.Match(sb.ToString()).Value;
-      }
-    }
     #endregion
 
-    private Thread scriptThread;
-    public void Execute() {
-      if (compiledScript == null) return;
-      var executeMethod = typeof(UserScriptBase).GetMethod(ExecuteMethodName, BindingFlags.NonPublic | BindingFlags.Instance);
-      if (executeMethod != null) {
-        scriptThread = new Thread(() => {
-          Exception ex = null;
-          try {
-            OnScriptExecutionStarted();
-            executeMethod.Invoke(compiledScript, new[] { VariableStore });
-          } catch (ThreadAbortException) {
-            // the execution was cancelled by the user
-          } catch (TargetInvocationException e) {
-            ex = e.InnerException;
-          } finally {
-            OnScriptExecutionFinished(ex);
-          }
-        });
-        scriptThread.Start();
-      }
-    }
-
-    public void Kill() {
-      if (scriptThread.IsAlive)
-        scriptThread.Abort();
-    }
-
-    private void compiledScript_ConsoleOutputChanged(object sender, EventArgs<string> e) {
-      OnConsoleOutputChanged(e.Value);
-    }
-
     public event EventHandler CodeChanged;
-    private void OnCodeChanged() {
+    protected virtual void OnCodeChanged() {
       var handler = CodeChanged;
       if (handler != null) handler(this, EventArgs.Empty);
@@ -276,26 +178,8 @@
 
     public event EventHandler CompileErrorsChanged;
-    private void OnCompileErrorsChanged() {
+    protected virtual void OnCompileErrorsChanged() {
       var handler = CompileErrorsChanged;
       if (handler != null) handler(this, EventArgs.Empty);
     }
-
-    public event EventHandler ScriptExecutionStarted;
-    private void OnScriptExecutionStarted() {
-      var handler = ScriptExecutionStarted;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-
-    public event EventHandler<EventArgs<Exception>> ScriptExecutionFinished;
-    private void OnScriptExecutionFinished(Exception e) {
-      var handler = ScriptExecutionFinished;
-      if (handler != null) handler(this, new EventArgs<Exception>(e));
-    }
-
-    public event EventHandler<EventArgs<string>> ConsoleOutputChanged;
-    private void OnConsoleOutputChanged(string args) {
-      var handler = ConsoleOutputChanged;
-      if (handler != null) handler(this, new EventArgs<string>(args));
-    }
   }
 }
