using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using DelftTools.Shell.Core;
using DelftTools.Shell.Core.Dao;
using DelftTools.Shell.Core.Services;
using DelftTools.Utils.IO;
using DeltaShell.Core.Properties;
using log4net;
namespace DeltaShell.Core.Services
{
///
/// Implemens all common operations with the Project in the IApplication
///
/// TODO: this is a stateful class, rename to HybridProjectRepository
///
public class ProjectService : IProjectService
{
private static readonly ILog log = LogManager.GetLogger(typeof(ProjectService));
private IProjectRepositoryFactory projectRepositoryFactory;
public ProjectService()
: this(new ProjectRepositoryFactory())
{
}
public ProjectService(IProjectRepositoryFactory projectRepositoryFactory)
{
this.projectRepositoryFactory = projectRepositoryFactory;
}
public IProjectRepositoryFactory ProjectRepositoryFactory
{
get { return projectRepositoryFactory; }
set
{
projectRepositoryFactory = value;
projectRepository = null;
}
}
private IProjectRepository projectRepository;
public IProjectRepository ProjectRepository
{
get
{
if(ProjectRepositoryFactory != null && projectRepository == null)
{
// HACK: new repository should not be created here!!
ProjectRepository = projectRepositoryFactory.CreateNew();
}
return projectRepository;
}
private set { projectRepository = value; }
}
public event EventHandler ProjectSaving;
public event EventHandler ProjectSaved;
public event EventHandler ProjectSaveFailed;
public event EventHandler ProjectOpening;
public event EventHandler ProjectOpened;
public string ProjectDataDirectory
{
get { return GetProjectDataDirectory(ProjectRepository.Path); }
}
private static string GetProjectDataDirectory(string projectRepositoryPath)
{
return projectRepositoryPath != null ? projectRepositoryPath + "_data" : null;
}
///
/// Gets the external data directory.
/// Note that if the directory is not found then it will be created...
/// Maybe this need to be refactored because of the single responsibility principle
///
public string ProjectExternalDataDirectory
{
get
{
return CreateAndGetExternalDataDirectory(ProjectDataDirectory);
}
}
///
/// Gets the external data directory. The directory will be created if not there because it should :)
///
/// The base path to create the full path from
/// A full path to the external project data directory
public string CreateAndGetExternalDataDirectory(string basePath)
{
var externalDataDirectory = Path.Combine(basePath, "external_data");
FileUtils.CreateDirectoryIfNotExists(externalDataDirectory);
return externalDataDirectory;
}
///
/// Creates a new project and saves it in a temporary folder
///
///
public Project CreateNewProjectInTemporaryFolder()
{
log.InfoFormat(Resources.ProjectService_CreateNewProjectInTemporaryFolder_Creating_new_empty_project_in_temporary_folder____);
var path = Path.GetTempFileName();
ProjectRepository.Create(path);
log.InfoFormat(Resources.ProjectService_CreateNewProjectInTemporaryFolder_New_empty_project_created);
var project = ProjectRepository.GetProject();
FileUtils.CreateDirectoryIfNotExists(ProjectDataDirectory);
project.IsTemporary = true;
return project;
}
public void SaveProjectInTemporaryFolder(Project project)
{
var path = Path.GetTempFileName();
SaveProjectAs(project, path, true);
project.IsTemporary = true;
}
///
/// Creates a new project repository using provided the path
///
/// Path to project repository, usually file with .dsproj extension.
/// A new project instance
public Project Create(string path)
{
ProjectRepository.Create(path);
FileUtils.DeleteIfExists(ProjectDataDirectory);
var project = ProjectRepository.GetProject();
CollectMemory();
return project;
}
///
/// Collect all possible memory after Save/Load operation (improve fragmentation)
///
private void CollectMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers(); // wait until all collect operations are finished
}
///
/// Opens an existing repository using provided path.
///
/// Path to project repository, usually file with .dsproj extension.
public Project Open(string path)
{
log.InfoFormat(Resources.ProjectService_Open_Loading_project__0_____, path);
if (ProjectOpening != null)
{
var args = new CancelEventArgs();
ProjectOpening(path, args);
if (args.Cancel)
{
log.InfoFormat(Resources.ProjectService_Open_Stopped_loading_project__0_, path);
return null;
}
}
if (FileUtils.PathIsRelative(path))
{
var currentDir = Directory.GetCurrentDirectory();
path = Path.Combine(currentDir, path);
}
var project = ProjectRepository.Open(path);
if (project != null)
{
FileUtils.CreateDirectoryIfNotExists(ProjectDataDirectory);
CreateWorkingDirectories(project, ProjectDataDirectory);
}
CollectMemory();
if (ProjectOpened != null)
{
ProjectOpened(project, null);
}
return project;
}
///
/// Save a project with all its task's and call savestate on the models
///
/// the project to be saved
///
public void Save(Project project)
{
var saveAction = new Action(p =>
{
// create model working directory
var projectDataDirectory = GetProjectDataDirectory(ProjectRepository.Path);
FileUtils.CreateDirectoryIfNotExists(projectDataDirectory);
CreateWorkingDirectories(project, projectDataDirectory);
ProjectRepository.SaveOrUpdate(project);
});
DoSaving(saveAction, project, ProjectRepository.Path);
}
public void Close(Project project)
{
if (ProjectRepository.IsOpen)
{
var path = ProjectRepository.Path;
var dir = ProjectDataDirectory;
if (project != null)
{
foreach (var disposableItem in project.Items.OfType())
{
disposableItem.Dispose();
}
}
ProjectRepository.Close();
if (project != null && project.IsTemporary)
{
DeleteProjectFiles(path, dir);
}
}
CollectMemory();
}
///
/// Delete all projectfiles: the projectfile and the dir and files of the data directory( for example if project is temp)
///
///
///
private static void DeleteProjectFiles(string pathProject, string pathProjectData)
{
FileUtils.DeleteIfExists(pathProjectData);
if (File.Exists(pathProject))
{
File.Delete(pathProject);
}
}
///
/// Save a project with all its task's and call savestate on the models
///
/// the path where the model is saved
/// the project to be saved
///
public void SaveProjectAs(Project project, string path)
{
SaveProjectAs(project, path, false);
}
private void SaveProjectAs(Project project, string path, bool tempProject)
{
var oldProjectPath = ProjectRepository.Path;
var oldProjectDataDirectory = ProjectDataDirectory;
var oldProjectWasTemporary = project.IsTemporary;
if(!string.IsNullOrEmpty(oldProjectPath) && !string.IsNullOrEmpty(path)
&& Path.GetFullPath(oldProjectPath) == Path.GetFullPath(path))
{
Save(project); // path did not change, just save
return;
}
var saveAction = new Action(p =>
{
path = Path.GetFullPath(path);
var newProjectDataDirectory = GetProjectDataDirectory(path);
FileUtils.DeleteIfExists(newProjectDataDirectory);
FileUtils.CreateDirectoryIfNotExists(newProjectDataDirectory);
//create working directories for all models contained in the project
CreateWorkingDirectories(p, newProjectDataDirectory);
if ((ProjectRepository.IsOpen) && (p != ProjectRepository.GetProject()))
{
ProjectRepository.Close();
}
ProjectRepository.SaveAs(p, path);
if (oldProjectWasTemporary && !tempProject)
{
try
{
DeleteProjectFiles(oldProjectPath, oldProjectDataDirectory);
}
catch (Exception e)
{
log.ErrorFormat(Resources.ProjectService_SaveProjectAs_Error_during_delete_of_old_project_file_and_directory__skipping____, e);
}
}
p.IsTemporary = false;
});
DoSaving(saveAction, project, path);
}
private void DoSaving(Action saveAction, Project project, string path)
{
if (!FireProjectSaving(project, path))
{
return;
}
try
{
saveAction(project);
}
catch (Exception)
{
FireProjectSaveFailed(project);
throw;
}
FireProjectSaved(project);
CollectMemory();
}
private void CreateWorkingDirectories(Project project, string projectDataDirectory)
{
if (FileUtils.PathIsRelative(projectDataDirectory))
{
throw new InvalidDataException(Resources.ProjectService_CreateWorkingDirectories_Parameter_projectDataDirectory_should_be_absolute_path);
}
}
#region Event firing
private bool FireProjectSaving(Project project, string path)
{
log.InfoFormat(Resources.ProjectService_FireProjectSaving_Saving_project__0__as__1_, project.Name, path);
if (ProjectSaving != null)
{
var cancelEventArgs = new CancelEventArgs();
ProjectSaving(project, cancelEventArgs);
if (cancelEventArgs.Cancel)
{
return false;
}
}
return true;
}
private void FireProjectSaved(Project project)
{
if (ProjectSaved != null)
{
ProjectSaved(project, null);
}
log.InfoFormat(Resources.ProjectService_FireProjectSaved_Project__0__saved, project.Name);
}
private void FireProjectSaveFailed(Project project)
{
if (ProjectSaveFailed != null)
{
ProjectSaveFailed(project, null);
}
}
#endregion
public void Dispose()
{
if (ProjectRepository != null)
{
if (ProjectRepository.IsOpen)
{
Close(ProjectRepository.GetProject());
}
ProjectRepository.Dispose();
ProjectRepository = null;
}
projectRepositoryFactory = null;
}
}
}