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