using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.ComponentModel;
using Chernobyl.Creation;
using Chernobyl.Event;
using Chernobyl.Plugin.Exceptions;
using Microsoft.CSharp;
namespace Chernobyl.Plugin
{
///
/// Creates plug-ins from a set of C# scripts.
///
public class CSharpScriptPluginFactory : PluginBuilder, IFactory, Object[]>
{
///
/// Constructor.
///
/// The instance that
/// gives and takes services and is given to the created plug-ins
/// if they have a constructor that takes an
/// instance.
public CSharpScriptPluginFactory(IEventCollection services)
: this(services, string.Empty)
{ }
///
/// Constructor.
///
/// The instance that
/// gives and takes services and is given to the created plug-ins
/// if they have a constructor that takes an
/// instance.
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this string to search for the source files within that
/// folder. Specify an empty to use the default
/// value which is "*.cs".
public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern)
: this(services, sourceFileSearchPattern, SearchOption.AllDirectories)
{ }
///
/// Constructor.
///
/// The instance that
/// gives and takes services and is given to the created plug-ins
/// if they have a constructor that takes an
/// instance.
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this string to search for the source files within that
/// folder. Specify an empty to use the default
/// value which is "*.cs".
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this to search for the source
/// files within that directory/subdirectory. By default, this value is
/// .
public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern,
SearchOption sourceFileSearchOption)
: this(services, sourceFileSearchPattern, sourceFileSearchOption, null)
{ }
///
/// Constructor.
///
/// The instance that
/// gives and takes services and is given to the created plug-ins
/// if they have a constructor that takes an
/// instance.
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this string to search for the source files within that
/// folder. Specify an empty to use the default
/// value which is "*.cs".
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this to search for the source
/// files within that directory/subdirectory. By default, this value is
/// .
/// The parameters for the compiler or
/// null if the default should be used.
public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern,
SearchOption sourceFileSearchOption, CompilerParameters compilerParameters)
: this(services, sourceFileSearchPattern, sourceFileSearchOption, compilerParameters, null)
{ }
///
/// Constructor.
///
/// The instance that
/// gives and takes services and is given to the created plug-ins
/// if they have a constructor that takes an
/// instance.
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this string to search for the source files within that
/// folder. Specify an empty to use the default
/// value which is "*.cs".
/// If this script plug-in factory
/// encounters a directory in the file paths passed to it in Create then
/// it will use this to search for the source
/// files within that directory/subdirectory. By default, this value is
/// .
/// The parameters for the compiler or
/// null if the default should be used.
/// The used
/// to compile the C# scripts or null if the default
/// should be used.
public CSharpScriptPluginFactory(IEventCollection services, string sourceFileSearchPattern,
SearchOption sourceFileSearchOption, CompilerParameters compilerParameters,
CSharpCodeProvider codeProvider)
: base(services)
{
if (sourceFileSearchPattern.Length == 0)
sourceFileSearchPattern = "*.cs";
SourceFileSearchPattern = sourceFileSearchPattern;
SourceFileSearchOption = sourceFileSearchOption;
if(compilerParameters == null)
{
compilerParameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true,
#if DEBUG
IncludeDebugInformation = true,
#else
IncludeDebugInformation = false,
#endif
TreatWarningsAsErrors = false
};
}
CompilerParameters = compilerParameters;
if(codeProvider == null)
{
Dictionary providerOptions = new Dictionary();
providerOptions.Add("CompilerVersion", "v3.5");
codeProvider = new CSharpCodeProvider(providerOptions);
}
CodeProvider = codeProvider;
}
///
/// Creates a set of objects, from a set of files, using the
/// method to choose which objects to create and
/// the method to do the actually creation.
///
/// This can be a list of one of the following
/// things:
///
/// - File paths to source files.
/// - Directory paths that contain source files.
/// - File paths to source files and directory paths that contain source files.
/// - Pure C# source code.
///
/// The objects that were created.
/// Thrown if the compilation of
/// the scripts fails.
public object[] Create(IEnumerable pathsOrSource)
{
try
{
CompilerResults compileResults;
object[] objects = Create(out compileResults, pathsOrSource.ToArray());
// Throw an exception for any errors received.
if (compileResults.Errors.Count > 0)
throw new CompileErrorException("Compilation of some C# " +
"scripts has failed.",
compileResults);
ReportCreation(objects);
return objects;
}
catch (Exception e)
{
ReportCreation(new CreationEventArgs(e));
throw;
}
}
///
/// If this script plug-in factory encounters a directory in the
/// file paths passed to it in Create then it will use this string
/// to search for the source files within that folder. By default,
/// this value is "*.cs".
///
public string SourceFileSearchPattern { get; set; }
///
/// If this script plug-in factory encounters a directory in the
/// file paths passed to it in Create then it will use this
/// to search for the source files within
/// that directory/subdirectory. By default, this value is
/// .
///
public SearchOption SourceFileSearchOption { get; set; }
///
/// The parameters for the compiler.
///
public CompilerParameters CompilerParameters { get; set; }
///
/// The C# code provider used to compile the C# scripts.
///
public CSharpCodeProvider CodeProvider { get; set; }
///
/// Creates a set of objects, from a set of files, using the
/// method to choose which objects to create and
/// the method to do the actually creation.
///
/// The results from the compilation of the
/// C# scripts.
/// This can be a list of one of the following
/// things:
///
/// - File paths to source files.
/// - Directory paths that contain source files.
/// - File paths to source files and directory paths that contain source files.
/// - Pure C# source code.
///
/// The objects that were created.
object[] Create(out CompilerResults compileResults, string[] pathsOrSource)
{
List interfaces = new List();
if (pathsOrSource.Length == 0)
{
compileResults = null;
return interfaces.ToArray();
}
// add in our assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
CompilerParameters.ReferencedAssemblies.Add(asm.Location);
if (File.Exists(pathsOrSource[0]) || Directory.Exists(pathsOrSource[0]))
{
// this is a set of files or directories
// make sure all of the files exist and we get the source files from directories.
List sourceFiles = new List();
foreach (string path in pathsOrSource)
{
// make sure the path is absolute or the compilation will
// fail
string finalPath = path;
if (Path.IsPathRooted(path) == false)
finalPath = Path.GetFullPath(path);
if (File.Exists(finalPath))
sourceFiles.Add(finalPath);
else if (Directory.Exists(finalPath))
sourceFiles.AddRange(Directory.GetFiles(finalPath, SourceFileSearchPattern, SourceFileSearchOption));
}
compileResults = CodeProvider.CompileAssemblyFromFile(CompilerParameters, sourceFiles.ToArray());
}
else // this is pure source code; compile it.
compileResults = CodeProvider.CompileAssemblyFromSource(CompilerParameters, pathsOrSource);
// get the interfaces we need from the compile.
if (compileResults.Errors.Count == 0)
{
// get the interfaces from the assembly of compiled scripts
foreach (Type type in compileResults.CompiledAssembly.GetTypes())
{
if (Predicate(type))
interfaces.Add(Creator(type));
}
}
return interfaces.ToArray();
}
}
}