- Timestamp:
- 02/15/10 14:34:18 (14 years ago)
- Location:
- trunk/sources/HeuristicLab.Operators.Programmable/3.3
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.Operators.Programmable/3.3
- Property svn:ignore
-
old new 1 *.user 2 HeuristicLabOperatorsProgrammablePlugin.cs 1 3 bin 2 4 obj 3 *.user
-
- Property svn:ignore
-
trunk/sources/HeuristicLab.Operators.Programmable/3.3/ProgrammableOperator.cs
r2526 r2799 34 34 using HeuristicLab.Data; 35 35 using System.Data.Linq; 36 using System.Xml.XPath; 37 using HeuristicLab.PluginInfrastructure; 36 38 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; 37 39 38 40 namespace HeuristicLab.Operators.Programmable { 39 public class ProgrammableOperator : OperatorBase { 41 42 public class ProgrammableOperator : Operator { 43 44 #region Fields & Properties 45 40 46 private MethodInfo executeMethod; 41 47 public CompilerErrorCollection CompileErrors { get; private set; } 48 public string CompilationUnitCode { get; private set; } 49 42 50 [Storable] 43 private string myDescription; 44 public override string Description { 45 get { return myDescription; } 46 } 47 48 [Storable] 49 private string myCode; 51 private string code; 50 52 public string Code { 51 get { return myCode; }53 get { return code; } 52 54 set { 53 if (value != myCode) {54 myCode = value;55 if (value != code) { 56 code = value; 55 57 executeMethod = null; 56 58 OnCodeChanged(); … … 61 63 private object syncRoot = new object(); 62 64 63 public ProgrammableOperator() { 64 myCode = "Result.Data = true;"; 65 myDescription = "An operator that can be programmed for arbitrary needs."; 66 AddVariableInfo(new VariableInfo("Result", "A computed variable", typeof(BoolData), VariableKind.New | VariableKind.Out)); 67 executeMethod = null; 65 private static object initLock = new object(); 66 private static Dictionary<string, List<Assembly>> defaultPluginDict; 67 private static Dictionary<Assembly, bool> defaultAssemblyDict; 68 69 public readonly Dictionary<string, List<Assembly>> Plugins; 70 71 protected Dictionary<Assembly, bool> Assemblies; 72 73 [Storable] 74 private IEnumerable<string> _persistedAssemblyNames { 75 get { 76 return Assemblies.Keys.Select(a => a.FullName); 77 } 78 set { 79 var selectedAssemblyNames = new HashSet<string>(value); 80 foreach (var a in Assemblies.Keys.ToList()) { 81 Assemblies[a] = selectedAssemblyNames.Contains(a.FullName); 82 } 83 } 84 } 85 86 public IEnumerable<Assembly> AvailableAssemblies { 87 get { return Assemblies.Keys; } 88 } 89 90 public IEnumerable<Assembly> SelectedAssemblies { 91 get { return Assemblies.Where(kvp => kvp.Value).Select(kvp => kvp.Key); } 92 } 93 94 [Storable] 95 private HashSet<string> namespaces; 96 public IEnumerable<string> Namespaces { 97 get { return namespaces; } 98 } 99 100 #endregion 101 102 #region Extended Accessors 103 104 public void SelectAssembly(Assembly a) { 105 if (a != null && Assemblies.ContainsKey(a)) 106 Assemblies[a] = true; 107 } 108 109 public void UnselectAssembly(Assembly a) { 110 if (a != null && Assemblies.ContainsKey(a)) 111 Assemblies[a] = false; 112 } 113 114 public void SelectNamespace(string ns) { 115 namespaces.Add(ns); 116 } 117 118 public void UnselectNamespace(string ns) { 119 namespaces.Remove(ns); 68 120 } 69 121 70 122 public void SetDescription(string description) { 71 123 if (description == null) 72 throw new NullReferenceException("description must not be null"); 73 74 if (description != myDescription) { 75 myDescription = description; 76 OnDescriptionChanged(); 77 } 78 } 79 80 public virtual void Compile() { 81 CodeNamespace ns = new CodeNamespace("HeuristicLab.Operators.Programmable.CustomOperators"); 82 CodeTypeDeclaration typeDecl = new CodeTypeDeclaration("Operator"); 83 typeDecl.IsClass = true; 84 typeDecl.TypeAttributes = TypeAttributes.Public; 85 86 CodeMemberMethod method = new CodeMemberMethod(); 87 method.Name = "Execute"; 88 method.ReturnType = new CodeTypeReference(typeof(IOperation)); 89 method.Attributes = MemberAttributes.Public | MemberAttributes.Static; 90 method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IOperator), "op")); 91 method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IScope), "scope")); 92 foreach (IVariableInfo info in VariableInfos) 93 method.Parameters.Add(new CodeParameterDeclarationExpression(info.DataType, info.FormalName)); 94 string code = myCode + "\r\n" + "return null;"; 95 method.Statements.Add(new CodeSnippetStatement(code)); 96 typeDecl.Members.Add(method); 97 98 ns.Types.Add(typeDecl); 99 ns.Imports.Add(new CodeNamespaceImport("System")); 100 ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic")); 101 ns.Imports.Add(new CodeNamespaceImport("System.Text")); 102 ns.Imports.Add(new CodeNamespaceImport("System.Linq")); 103 ns.Imports.Add(new CodeNamespaceImport("System.Data.Linq")); 104 ns.Imports.Add(new CodeNamespaceImport("HeuristicLab.Core")); 105 foreach (IVariableInfo variableInfo in VariableInfos) 106 ns.Imports.Add(new CodeNamespaceImport(variableInfo.DataType.Namespace)); 107 108 CodeCompileUnit unit = new CodeCompileUnit(); 109 unit.Namespaces.Add(ns); 124 throw new NullReferenceException("description must not be null"); 125 Description = description; 126 } 127 128 public IEnumerable<string> GetAllNamespaces(bool selectedAssembliesOnly) { 129 var namespaces = new HashSet<string>(); 130 foreach (var a in Assemblies) { 131 if (!selectedAssembliesOnly || a.Value) { 132 foreach (var t in a.Key.GetTypes()) { 133 if (t.IsPublic) { 134 foreach (string ns in GetNamespaceHierachy(t.Namespace)) { 135 namespaces.Add(ns); 136 } 137 } 138 } 139 } 140 } 141 return namespaces; 142 } 143 144 private IEnumerable<string> GetNamespaceHierachy(string ns) { 145 for (int i = ns.Length; i != -1; i = ns.LastIndexOf('.', i - 1)) { 146 yield return ns.Substring(0, i); 147 } 148 } 149 150 #endregion 151 152 #region Construction & Initialization 153 154 public ProgrammableOperator() { 155 code = ""; 156 Description = "An operator that can be programmed for arbitrary needs."; 157 executeMethod = null; 158 ProgrammableOperator.StaticInitialize(); 159 Assemblies = defaultAssemblyDict; 160 Plugins = defaultPluginDict; 161 namespaces = new HashSet<string>(DiscoverNamespaces()); 162 } 163 164 private static void StaticInitialize() { 165 lock (initLock) { 166 if (defaultPluginDict != null || defaultAssemblyDict != null) return; 167 defaultAssemblyDict = DiscoverAssemblies(); 168 defaultPluginDict = GroupAssemblies(defaultAssemblyDict.Keys); 169 } 170 } 171 172 private static Dictionary<string, List<Assembly>> GroupAssemblies(IEnumerable<Assembly> assemblies) { 173 var plugins = new Dictionary<string, List<Assembly>>(); 174 var locationTable = assemblies.ToDictionary(a => a.Location, a => a); 175 foreach (var plugin in ApplicationManager.Manager.Plugins) { 176 var aList = new List<Assembly>(); 177 foreach (var aName in from file in plugin.Files 178 where file.Type == PluginFileType.Assembly 179 select file.Name) { 180 Assembly a; 181 locationTable.TryGetValue(aName, out a); 182 if (a != null) { 183 aList.Add(a); 184 locationTable.Remove(aName); 185 } 186 } 187 plugins[plugin.Name] = aList; 188 } 189 plugins["other"] = locationTable.Values.ToList(); 190 return plugins; 191 } 192 193 protected static List<Assembly> defaultAssemblies = new List<Assembly>() { 194 typeof(System.Linq.Enumerable).Assembly, // add reference to version 3.5 of System.dll 195 typeof(System.Collections.Generic.List<>).Assembly, 196 typeof(System.Text.StringBuilder).Assembly, 197 typeof(System.Data.Linq.DataContext).Assembly, 198 typeof(HeuristicLab.Core.Item).Assembly, 199 typeof(HeuristicLab.Data.IntData).Assembly, 200 }; 201 202 protected static Dictionary<Assembly, bool> DiscoverAssemblies() { 203 var assemblies = new Dictionary<Assembly, bool>(); 204 foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { 205 try { 206 if (File.Exists(a.Location)) { 207 assemblies.Add(a, false); 208 } 209 } 210 catch (NotSupportedException) { 211 // NotSupportedException is thrown while accessing 212 // the Location property of the anonymously hosted 213 // dynamic methods assembly, which is related to 214 // LINQ queries 215 } 216 } 217 foreach (var a in defaultAssemblies) { 218 if (assemblies.ContainsKey(a)) { 219 assemblies[a] = true; 220 } else { 221 assemblies.Add(a, true); 222 } 223 } 224 return assemblies; 225 } 226 227 protected static List<string> DiscoverNamespaces() { 228 return new List<string>() { 229 "System", 230 "System.Collections.Generic", 231 "System.Text", 232 "System.Linq", 233 "System.Data.Linq", 234 "HeuristicLab.Core", 235 "HeuristicLab.Data", 236 }; 237 } 238 239 #endregion 240 241 #region Compilation 242 243 private static CSharpCodeProvider codeProvider = 244 new CSharpCodeProvider( 245 new Dictionary<string, string>() { 246 { "CompilerVersion", "v3.5" }, // support C# 3.0 syntax 247 }); 248 249 private CompilerResults DoCompile() { 110 250 CompilerParameters parameters = new CompilerParameters(); 111 251 parameters.GenerateExecutable = false; 112 252 parameters.GenerateInMemory = true; 113 253 parameters.IncludeDebugInformation = false; 114 Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 115 foreach (Assembly loadedAssembly in loadedAssemblies) 116 parameters.ReferencedAssemblies.Add(loadedAssembly.Location); 117 parameters.ReferencedAssemblies.Add(typeof(Enumerable).Assembly.Location); // add reference to version 3.5 of System.dll 118 parameters.ReferencedAssemblies.Add(typeof(DataContext).Assembly.Location); // add reference System.Data.Linq.Dll 119 CodeDomProvider provider = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); // support C# 3.0 syntax 120 CompilerResults results = provider.CompileAssemblyFromDom(parameters, unit); 121 254 parameters.ReferencedAssemblies.AddRange(SelectedAssemblies.Select(a => a.Location).ToArray()); 255 var unit = CreateCompilationUnit(); 256 var writer = new StringWriter(); 257 codeProvider.GenerateCodeFromCompileUnit( 258 unit, 259 writer, 260 new CodeGeneratorOptions() { 261 BracingStyle = "C", 262 ElseOnClosing = true, 263 IndentString = " ", 264 }); 265 CompilationUnitCode = writer.ToString(); 266 return codeProvider.CompileAssemblyFromDom(parameters, unit); 267 } 268 269 public virtual void Compile() { 270 var results = DoCompile(); 122 271 executeMethod = null; 123 272 if (results.Errors.HasErrors) { 124 StringWriter writer = new StringWriter(); 125 CodeGeneratorOptions options = new CodeGeneratorOptions(); 126 options.BlankLinesBetweenMembers = false; 127 options.ElseOnClosing = true; 128 options.IndentString = " "; 129 provider.GenerateCodeFromCompileUnit(unit, writer, options); 130 writer.Flush(); 131 string[] source = writer.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None); 132 StringBuilder builder = new StringBuilder(); 133 for (int i = 0; i < source.Length; i++) 134 builder.AppendLine((i + 3).ToString("###") + " " + source[i]); 135 builder.AppendLine(); 136 builder.AppendLine(); 137 builder.AppendLine(); 273 CompileErrors = results.Errors; 274 StringBuilder sb = new StringBuilder(); 138 275 foreach (CompilerError error in results.Errors) { 139 builder.Append("Line " + error.Line.ToString()); 140 builder.Append(", Column " + error.Column.ToString()); 141 builder.AppendLine(": " + error.ErrorText); 142 } 143 throw new Exception("Compile Errors:\n\n" + builder.ToString()); 276 sb.Append(error.Line).Append(':') 277 .Append(error.Column).Append(": ") 278 .AppendLine(error.ErrorText); 279 } 280 throw new Exception(string.Format( 281 "Compilation of \"{0}\" failed:{1}{2}", 282 Name, Environment.NewLine, 283 sb.ToString())); 144 284 } else { 285 CompileErrors = null; 145 286 Assembly assembly = results.CompiledAssembly; 146 287 Type[] types = assembly.GetTypes(); … … 149 290 } 150 291 151 public override IItem Clone(ICloner cloner) { 152 ProgrammableOperator clone = (ProgrammableOperator)base.Clone(cloner); 153 clone.myDescription = Description; 154 clone.myCode = Code; 155 clone.executeMethod = executeMethod; 156 return clone; 157 } 158 159 public override IOperation Apply(IScope scope) { 292 private CodeCompileUnit CreateCompilationUnit() { 293 CodeNamespace ns = new CodeNamespace("HeuristicLab.Operators.Programmable.CustomOperators"); 294 ns.Types.Add(CreateType()); 295 ns.Imports.AddRange( 296 GetSelectedAndValidNamespaces() 297 .Select(n => new CodeNamespaceImport(n)) 298 .ToArray()); 299 CodeCompileUnit unit = new CodeCompileUnit(); 300 unit.Namespaces.Add(ns); 301 return unit; 302 } 303 304 public IEnumerable<string> GetSelectedAndValidNamespaces() { 305 var possibleNamespaces = new HashSet<string>(GetAllNamespaces(true)); 306 foreach (var ns in Namespaces) 307 if (possibleNamespaces.Contains(ns)) 308 yield return ns; 309 } 310 311 public static readonly Regex SafeTypeNameCharRegex = new Regex("[_a-zA-Z0-9]+"); 312 public static readonly Regex SafeTypeNameRegex = new Regex("[_a-zA-Z][_a-zA-Z0-9]*"); 313 314 public string CompiledTypeName { 315 get { 316 var sb = new StringBuilder(); 317 foreach (string s in SafeTypeNameCharRegex.Matches(Name).Cast<Match>().Select(m => m.Value)) { 318 sb.Append(s); 319 } 320 return SafeTypeNameRegex.Match(sb.ToString()).Value; 321 } 322 } 323 324 private CodeTypeDeclaration CreateType() { 325 CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(CompiledTypeName) { 326 IsClass = true, 327 TypeAttributes = TypeAttributes.Public, 328 }; 329 typeDecl.Members.Add(CreateMethod()); 330 return typeDecl; 331 } 332 333 public string Signature { 334 get { 335 var sb = new StringBuilder() 336 .Append("public static IOperation Execute(IOperator op, IScope scope"); 337 foreach (IParameter param in Parameters) { 338 sb.Append(String.Format(", {0} {1}", param.DataType.Name, param.Name)); 339 } 340 return sb.Append(")").ToString(); 341 } 342 } 343 344 private static Regex lineSplitter = new Regex(@"\r\n|\r|\n"); 345 346 private CodeMemberMethod CreateMethod() { 347 CodeMemberMethod method = new CodeMemberMethod(); 348 method.Name = "Execute"; 349 method.ReturnType = new CodeTypeReference(typeof(HeuristicLab.Core.IExecutionSequence)); 350 method.Attributes = MemberAttributes.Public | MemberAttributes.Static; 351 method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IOperator), "op")); 352 // method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IScope), "scope")); 353 foreach (var param in Parameters) 354 method.Parameters.Add(new CodeParameterDeclarationExpression(param.DataType, param.Name)); 355 string[] codeLines = lineSplitter.Split(code); 356 for (int i = 0; i < codeLines.Length; i++) { 357 codeLines[i] = string.Format("#line {0} \"ProgrammableOperator\"{1}{2}", i + 1, "\r\n", codeLines[i]); 358 } 359 method.Statements.Add(new CodeSnippetStatement( 360 string.Join("\r\n", codeLines) + 361 "\r\nreturn null;")); 362 return method; 363 } 364 365 #endregion 366 367 #region HeuristicLab interfaces 368 369 public override IExecutionSequence Apply() { 160 370 lock (syncRoot) { 161 371 if (executeMethod == null) { … … 164 374 } 165 375 166 // collect parameters 167 object[] parameters = new object[VariableInfos.Count + 2]; 168 parameters[0] = this; 169 parameters[1] = scope; 170 int i = 2; 171 foreach (IVariableInfo info in VariableInfos) { 172 if ((info.Kind & VariableKind.New) == VariableKind.New) { 173 parameters[i] = GetVariableValue(info.FormalName, scope, false, false); 174 if (parameters[i] == null) { 175 IItem value = (IItem)Activator.CreateInstance(info.DataType); 176 if (info.Local) { 177 AddVariable(new Variable(info.ActualName, value)); 178 } else { 179 scope.AddVariable(new Variable(scope.TranslateName(info.FormalName), value)); 180 } 181 parameters[i] = value; 182 } 183 } else 184 parameters[i] = GetVariableValue(info.FormalName, scope, true); 185 i++; 186 } 187 188 return (IOperation)executeMethod.Invoke(null, parameters); 189 } 190 191 public event EventHandler DescriptionChanged; 192 protected virtual void OnDescriptionChanged() { 193 if (DescriptionChanged != null) 194 DescriptionChanged(this, new EventArgs()); 195 } 376 var parameters = new List<object>() { this }; 377 parameters.AddRange(Parameters.Select(p => (object)p.ActualValue)); 378 return (IExecutionSequence)executeMethod.Invoke(null, parameters.ToArray()); 379 } 380 196 381 public event EventHandler CodeChanged; 197 382 protected virtual void OnCodeChanged() { … … 199 384 CodeChanged(this, new EventArgs()); 200 385 } 386 387 #endregion 388 389 #region Cloning 390 391 public override IDeepCloneable Clone(Cloner cloner) { 392 ProgrammableOperator clone = (ProgrammableOperator)base.Clone(cloner); 393 clone.Description = Description; 394 clone.code = Code; 395 clone.executeMethod = executeMethod; 396 clone.Assemblies = Assemblies.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); 397 clone.namespaces = namespaces; 398 clone.CompilationUnitCode = CompilationUnitCode; 399 clone.CompileErrors = CompileErrors; 400 return clone; 401 } 402 403 #endregion 404 201 405 } 202 406 }
Note: See TracChangeset
for help on using the changeset viewer.