using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Chernobyl.Attribution;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Creation;
using Chernobyl.Dependency;
using Chernobyl.Plugin;
using Chernobyl.Reflection;
using Chernobyl.Reflection.Resources;
using Chernobyl.Reflection.Template;
using Chernobyl.Resources;
using Chernobyl.StateMachine;
using Chernobyl.Update;
using Chernobyl.Extensions;
namespace Chernobyl.Config
{
///
/// Represents the core of Chernobyl.
///
public class Core : State, IPlugin
{
///
/// Initializes a new instance of the class that uses
/// the default instance and the
/// object returned by
///
/// with a argument of .
///
public Core() : this(null)
{ }
///
/// Initializes a new instance of the class that uses
/// the object returned by
///
/// with a argument of .
///
/// The instance to
/// use when locating files or null if the default
/// should be used. The default search path
/// instance contains the following paths: ".", "../../Chernobyl", and
/// "../../Chernobyl/data"
public Core(SearchPaths searchPaths)
: this(searchPaths, ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None))
{ }
///
/// Initializes a new instance of the class.
///
/// The instance to
/// use when locating files or null if the default
/// should be used. The default search path
/// instance contains the following paths: "." and "../../"
/// The instance that
/// Chernobyl should pull settings from.
public Core(SearchPaths searchPaths, Configuration config)
{
Trace = new TraceSource("Chernobyl");
// Setup the search paths.
SearchPaths = searchPaths ?? new SearchPaths(".", "../../");
string searchPath = ConfigurationManager.AppSettings["Chernobyl.SearchPath"];
if (searchPath != null)
SearchPaths.Add(searchPath);
// Create the fallout processing chain. First we need to load in the
// main IEventCollection instance. We can't load anything else in for the
// time being because those instances or the IResourceProcessors
// processing them may need the IEventCollection instance we are loading in.
// First We'll need to have the ResourceRegistry and the
// FileStreamResourceMapper process this IEventCollection instance so
// that they will store it.
ResourceRegistry resourceRegistry = new ResourceRegistry();
FalloutFileMapProcessor = new FileStreamResourceMapper();
FileStreamProcessor contentFileStreamProcessor = new FileStreamProcessor(null);
CodeDomContentResourceProcessor codeDomContentResourceProcessor = new CodeDomContentResourceProcessor(null);
XmlContentResourceProcessor xmlContentProcessor = new XmlContentResourceProcessor(null);
ComponentProcessor = resourceRegistry;
resourceRegistry.NextResourceProcessor = FalloutFileMapProcessor;
FalloutFileMapProcessor.NextResourceProcessor = codeDomContentResourceProcessor;
codeDomContentResourceProcessor.NextResourceProcessor = contentFileStreamProcessor;
contentFileStreamProcessor.AddProcessor("fall", xmlContentProcessor);
using (_servicesStream = new FileStream(SearchPaths.FindFilePath("Chernobyl/services.fall"), FileMode.Open))
{
// The ServicesLoadedCallback method will finish the configuration
// of the Chernobyl core when the IEventCollection instance has been
// loaded.
ComponentProcessor.From(ServicesLoadedCallback, _servicesStream);
}
}
///
/// A method that is given to the
///
/// method of the during construction.
/// This method is invoked when the instance has
/// been loaded.
///
/// The result.
void ServicesLoadedCallback(IResourceProcessResult result)
{
Services = (IEventCollection) ((IInstance) result.Resource).TheInstance;
// Now that we've loaded in the IEventCollection instance we can get rid of
// that Stream that points to it so that other code can get access to
// the same file.
_servicesStream.Dispose();
// Now we'll need to replace some of the fallout processors with
// new fallout processors that reference the newly loaded
// IEventCollection instance. We'll then hook those new processors up
// to the ResourceRegistry and FileStreamResourceMapper we
// previously created and that now hold the IEventCollection instance
FileStreamProcessor contentFileStreamProcessor = new FileStreamProcessor(Services);
CodeDomContentResourceProcessor codeDomContentResourceProcessor = new CodeDomContentResourceProcessor(Services);
XmlContentResourceProcessor xmlContentProcessor = new XmlContentResourceProcessor(Services);
FalloutFileMapProcessor.NextResourceProcessor = codeDomContentResourceProcessor;
codeDomContentResourceProcessor.NextResourceProcessor = contentFileStreamProcessor;
contentFileStreamProcessor.AddProcessor("fall", xmlContentProcessor);
// Now that we have loaded the IEventCollection instance, we can finish
// the configuration of the Chernobyl core.
// configuration and set the current state
ConfigurationState = new ConfigState();
LoadingState = new LoadState();
RunningState = new RunState();
ChildState = ConfigurationState;
RootUpdateable = new RootUpdateable(true);
ObjectProcessor = new ObjectContentResourceProcessor(Services);
Services.Inject(this);
Services.Add(this);
// load plug-ins
IFactory, object[]> dllPluginFactory =
new AssemblyFilePluginFactory(Services, ProcessAssemblyPlugin);
string plugins = ConfigurationManager.AppSettings["Chernobyl.Plugins"];
if (plugins != null)
{
IEnumerable pluginList = from plugin in plugins.Split(new[] {';'})
where plugin.Length != 0
select new AssemblyName(plugin);
Plugins = dllPluginFactory.Create(pluginList).Cast().ToList();
}
else
{
Trace.TraceEvent(TraceEventType.Information, 0, "The application " +
"setting \"Chernobyl.Plugins\" (from \"appSettings\" in the " +
"application configuration file) could not be found. Therefore, " +
"no plug-ins will be loaded.");
}
ConfigurationState.Left += ConfigurationStateLeft;
}
///
/// Starts Chernobyl so that a plug-in that handles the rendering and
/// updating of the application (like Chernobyl.Xna) can begin working.
/// Note, you only need to call if you are using
/// Chernobyl to update or render your application. Otherwise, don't call
/// this method.
///
public void Run()
{
if (Runner == null)
throw new Exception("Unable to run Chernobyl as the Runner method " +
"has not been set. This could be caused by not having a plug-in installed " +
"that handles application rendering and updates. Note, if you do not need " +
"to update or render, then you don't need to call Chernobyl.Run().");
Runner();
}
Stream _servicesStream;
///
/// A used to output errors, warnings, information,
/// etc., that are specific to Chernobyl. This
/// will have the name "Chernobyl".
///
public static TraceSource Trace { get; private set; }
///
/// The method that starts the rendering and updating of the application.
/// This must be provided by a plug-in as Chernobyl does not provide this
/// method. The method is responsible for setting the
/// (located in the
/// instance) onto the of this
/// (also located in the
/// instance).
///
public Action Runner { get; set; }
///
/// The plug-ins that have been loaded.
///
public List Plugins { get; set; }
///
/// The services provided by this plug-in or null if no services are
/// provided.
///
public IEventCollection Services { get; private set; }
///
/// The default root updateable.
///
[Provide]
public IRootUpdateable RootUpdateable { get; set; }
///
/// The root component processor for Chernobyl.
///
[Provide]
public IResourceProcessor ComponentProcessor { get; set; }
///
/// The root object processor for Chernobyl.
///
[Provide]
public IResourceProcessor ObjectProcessor { get; set; }
///
/// The default ISearchPaths implementation used by other systems.
///
[Provide]
public ISearchPaths SearchPaths { get; set; }
///
/// The that represents the configuration or
/// initialization state of the application.
///
[Provide]
public IConfigurationState ConfigurationState { get; set; }
///
/// The that represents the loading state of the
/// application after the configuration state ( )
///
[Provide]
public ILoadingState LoadingState { get; set; }
///
/// The that represents the started running state
/// after the loading state ( ).
///
[Provide]
public IRunningState RunningState { get; set; }
///
/// The main for fallout files.
///
FileStreamResourceMapper FalloutFileMapProcessor { get; set; }
///
/// The main object that was loaded from the main Chernobyl
/// configuration file. This file is specified by the application
/// configuration file (specified as *.config) under "AppSettings" with
/// the key "Chernobyl.Configuration". This fall file contains an
/// instance named "main" which is the instance represented by this
/// property.
///
public object MainInstance { get; private set; }
///
/// Loads the
/// instances that process instances on the plugin
/// and invokes them.
///
/// The plugin whose
/// that process
/// instances are to be invoked.
void ProcessAssemblyPlugin(Assembly assembly)
{
foreach (IProcessAttribute assemblyAttribute in
assembly.GetCustomAttributes>(true))
assemblyAttribute.Process(assembly);
}
///
/// An event handler that is invoked when Chernobyl has left the
/// configuration state. At this point it is safe to configure
/// instances in the main configuration file.
///
/// The instance that generated the event.
/// The instance
/// containing the event data.
void ConfigurationStateLeft(object sender, EventArgs e)
{
// Get the main instance from the main configuration file.
Resource.From(Services, mainResult => MainInstance = mainResult.Resource,
ConfigurationManager.AppSettings["Chernobyl.Configuration"],
new ResourceName("main"));
}
///
/// An used as the default value for
/// the .
///
class ConfigState : State, IConfigurationState
{ }
///
/// An used as the default value for
/// the .
///
class LoadState : State, ILoadingState
{ }
///
/// An used as the default value for
/// the .
///
class RunState : State, IRunningState
{ }
}
}