using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using RainmeterStudio.Documents; using RainmeterStudio.Model; using RainmeterStudio.Model.Events; using RainmeterStudio.Utils; namespace RainmeterStudio.Business { /// /// Document manager /// public class DocumentManager { #region Events /// /// Triggered when a document is opened /// public event EventHandler DocumentOpened; /// /// Triggered when a document is closed /// public event EventHandler DocumentClosed; #endregion #region Properties /// /// Gets a list of factories /// public IEnumerable Factories { get { return _factories; } } /// /// Gets a list of editors /// public IEnumerable Editors { get { return _editors; } } /// /// Gets a list of storages /// public IEnumerable Storages { get { return _storages; } } #endregion #region Private fields private List _factories = new List(); private List _editors = new List(); private List _storages = new List(); private List _templates = new List(); #endregion #region Initialization /// /// Initializes this document manager /// public DocumentManager() { } /// /// Registers all classes with the auto register flag /// /// We love linq public void PerformAutoRegister() { // Get all assemblies AppDomain.CurrentDomain.GetAssemblies() // Get all types .SelectMany(assembly => assembly.GetTypes()) // Select only the classes .Where(type => type.IsClass) // That have the AutoRegister attribute .Where(type => type.GetCustomAttributes(typeof(AutoRegisterAttribute), false).Length > 0) // That implement any of the types that can be registered .Where((type) => { bool res = false; res |= typeof(IDocumentEditorFactory).IsAssignableFrom(type); res |= typeof(IDocumentStorage).IsAssignableFrom(type); res |= typeof(DocumentTemplate).IsAssignableFrom(type); return res; }) // Obtain their default constructor .Select(type => type.GetConstructor(new Type[0])) // Invoke the default constructor .Select(constructor => constructor.Invoke(new object[0])) // Register .ForEach(obj => { // Try to register factory var factory = obj as IDocumentEditorFactory; if (factory != null) RegisterEditorFactory(factory); // Try to register as storage var storage = obj as IDocumentStorage; if (storage != null) RegisterStorage(storage); // Try to register as document template var doctemplate = obj as DocumentTemplate; if (doctemplate != null) RegisterTemplate(doctemplate); }); } /// /// Registers a document editor factory /// /// Document editor factory public void RegisterEditorFactory(IDocumentEditorFactory factory) { _factories.Add(factory); } /// /// Registers a document storage /// /// The storage public void RegisterStorage(IDocumentStorage storage) { _storages.Add(storage); } /// /// Registers a document template /// /// The document template public void RegisterTemplate(DocumentTemplate template) { _templates.Add(template); } #endregion #region Document operations /// /// Creates a new document in the specified path, with the specified format, and opens it /// /// /// public IDocumentEditor Create(DocumentTemplate format) { // Create document var document = format.CreateDocument(); document.IsDirty = true; // Find and create editor IDocumentEditor editor = CreateEditor(document); // Trigger event if (DocumentOpened != null) DocumentOpened(this, new DocumentOpenedEventArgs(editor)); return editor; } /// /// Opens the specified document /// /// The path to the file to open public IDocumentEditor Open(string path) { // Try to open IDocument document = Read(path); // Create factory var editor = CreateEditor(document); // Trigger event if (DocumentOpened != null) DocumentOpened(this, new DocumentOpenedEventArgs(editor)); return editor; } /// /// Saves a document /// /// The document public void Save(IDocument document) { // Find a storage var storage = FindStorage(document); if (document.Reference == null) throw new ArgumentException("Reference cannot be empty"); // Save storage.Write(document.Reference.Path, document); // Clear dirty flag document.IsDirty = false; } /// /// Saves the document as /// /// Path /// Document public void SaveAs(string path, IDocument document) { // Find a storage var storage = FindStorage(document); // Save storage.Write(path, document); // Update reference document.Reference.Name = Path.GetFileName(path); document.Reference.Path = path; // Clear dirty flag document.IsDirty = false; } /// /// Saves a copy of the document /// /// Path /// Document public void SaveACopy(string path, IDocument document) { // Find a storage var storage = FindStorage(document); // Save storage.Write(path, document); } /// /// Closes a document editor /// /// public void Close(IDocumentEditor editor) { // Remove from list of opened editors _editors.Remove(editor); // Close event if (DocumentClosed != null) DocumentClosed(this, new DocumentClosedEventArgs(editor)); } #endregion #region Private functions /// /// Attempts to create an editor for the document /// /// The document /// Thrown if failed to create editor /// The editor private IDocumentEditor CreateEditor(IDocument document) { IDocumentEditor editor = null; foreach (var factory in Factories) if (factory.CanEdit(document.GetType())) { editor = factory.CreateEditor(document); break; } if (editor == null) throw new ArgumentException("Failed to create editor."); _editors.Add(editor); return editor; } /// /// Attempts to read a document /// /// Path of file /// Thrown when failed to open the document /// private IDocument Read(string path) { IDocument document = null; foreach (var storage in Storages) if (storage.CanRead(path)) { document = storage.Read(path); break; } // Failed to open if (document == null) throw new ArgumentException("Failed to open document."); return document; } /// /// Attempts to find a storage for the specified document /// /// /// private IDocumentStorage FindStorage(IDocument document) { IDocumentStorage storage = null; foreach (var s in Storages) if (s.CanWrite(document.GetType())) { storage = s; break; } if (storage == null) throw new ArgumentException("Failed to find storage object."); return storage; } #endregion } }