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 { } } }