#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

754 lines
24 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Xml;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Workbench;
namespace ICSharpCode.SharpDevelop.Project
{
public static class ProjectService
{
volatile static Solution openSolution;
volatile static IProject currentProject;
public static Solution OpenSolution {
[System.Diagnostics.DebuggerStepThrough]
get {
return openSolution;
}
}
/// <summary>
/// Gets/Sets the active project.
/// Returns null if no project is active.
/// The getter is thread-safe; the setter may only be called on the main thread.
/// </summary>
public static IProject CurrentProject {
[System.Diagnostics.DebuggerStepThrough]
get {
return currentProject;
}
set {
SD.MainThread.VerifyAccess();
if (currentProject != value) {
LoggingService.Info("CurrentProject changed to " + (value == null ? "null" : value.Name));
currentProject = value;
OnCurrentProjectChanged(new ProjectEventArgs(currentProject));
CommandManager.InvalidateRequerySuggested();
}
}
}
/// <summary>
/// Gets an open project by the name of the project file.
/// </summary>
public static IProject GetProject(FileName projectFilename)
{
Solution sln = openSolution;
if (sln == null)
return null;
foreach (IProject project in sln.Projects) {
if (project.FileName == projectFilename) {
return project;
}
}
return null;
}
static bool initialized;
public static void InitializeService()
{
if (!initialized) {
initialized = true;
SD.Workbench.ActiveViewContentChanged += ActiveViewContentChanged;
FileService.FileRenamed += FileServiceFileRenamed;
FileService.FileRemoved += FileServiceFileRemoved;
var applicationStateInfoService = SD.GetService<ApplicationStateInfoService>();
if (applicationStateInfoService != null) {
applicationStateInfoService.RegisterStateGetter("ProjectService.OpenSolution", delegate { return OpenSolution; });
applicationStateInfoService.RegisterStateGetter("ProjectService.CurrentProject", delegate { return CurrentProject; });
}
}
}
/// <summary>
/// Returns if a project loader exists for the given file. This method works even in early
/// startup (before service initialization)
/// </summary>
public static bool HasProjectLoader(string fileName)
{
AddInTreeNode addinTreeNode = AddInTree.GetTreeNode("/SharpDevelop/Workbench/Combine/FileFilter");
foreach (Codon codon in addinTreeNode.Codons) {
string pattern = codon.Properties.Get("extensions", "");
if (FileUtility.MatchesPattern(fileName, pattern) && codon.Properties.Contains("class")) {
return true;
}
}
return false;
}
public static IProjectLoader GetProjectLoader(string fileName)
{
AddInTreeNode addinTreeNode = AddInTree.GetTreeNode("/SharpDevelop/Workbench/Combine/FileFilter");
foreach (Codon codon in addinTreeNode.Codons) {
string pattern = codon.Properties.Get("extensions", "");
if (FileUtility.MatchesPattern(fileName, pattern) && codon.Properties.Contains("class")) {
object binding = codon.AddIn.CreateObject(codon.Properties["class"]);
return binding as IProjectLoader;
}
}
return null;
}
public static void LoadSolutionOrProject(string fileName)
{
IProjectLoader loader = GetProjectLoader(fileName);
if (loader != null) {
loader.Load(fileName);
} else {
MessageService.ShowError(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.OpenCombine.InvalidProjectOrCombine}", new StringTagPair("FileName", fileName)));
}
}
static void FileServiceFileRenamed(object sender, FileRenameEventArgs e)
{
if (OpenSolution == null) {
return;
}
string oldName = e.SourceFile;
string newName = e.TargetFile;
long x = 0;
foreach (ISolutionFolderContainer container in OpenSolution.SolutionFolderContainers) {
foreach (SolutionItem item in container.SolutionItems.Items) {
string oldFullName = Path.Combine(OpenSolution.Directory, item.Name);
++x;
if (FileUtility.IsBaseDirectory(oldName, oldFullName)) {
string newFullName = FileUtility.RenameBaseDirectory(oldFullName, oldName, newName);
item.Name = item.Location = FileUtility.GetRelativePath(OpenSolution.Directory, newFullName);
}
}
}
long y = 0;
foreach (IProject project in OpenSolution.Projects) {
if (FileUtility.IsBaseDirectory(project.Directory, oldName)) {
foreach (ProjectItem item in project.Items) {
++y;
if (FileUtility.IsBaseDirectory(oldName, item.FileName)) {
OnProjectItemRemoved(new ProjectItemEventArgs(project, item));
item.FileName = FileUtility.RenameBaseDirectory(item.FileName, oldName, newName);
OnProjectItemAdded(new ProjectItemEventArgs(project, item));
}
}
}
}
}
static void FileServiceFileRemoved(object sender, FileEventArgs e)
{
if (OpenSolution == null) {
return;
}
string fileName = e.FileName;
foreach (ISolutionFolderContainer container in OpenSolution.SolutionFolderContainers) {
for (int i = 0; i < container.SolutionItems.Items.Count;) {
SolutionItem item = container.SolutionItems.Items[i];
if (FileUtility.IsBaseDirectory(fileName, Path.Combine(OpenSolution.Directory, item.Name))) {
container.SolutionItems.Items.RemoveAt(i);
} else {
++i;
}
}
}
foreach (IProject project in OpenSolution.Projects) {
if (FileUtility.IsBaseDirectory(project.Directory, fileName)) {
IProjectItemListProvider provider = project as IProjectItemListProvider;
if (provider != null) {
foreach (ProjectItem item in provider.Items.ToArray()) {
if (FileUtility.IsBaseDirectory(fileName, item.FileName)) {
provider.RemoveProjectItem(item);
OnProjectItemRemoved(new ProjectItemEventArgs(project, item));
}
}
}
}
}
}
static void ActiveViewContentChanged(object sender, EventArgs e)
{
IViewContent viewContent = SD.Workbench.ActiveViewContent;
if (OpenSolution == null || viewContent == null) {
return;
}
string fileName = viewContent.PrimaryFileName;
if (fileName == null) {
return;
}
CurrentProject = OpenSolution.FindProjectContainingFile(fileName) ?? CurrentProject;
}
public static void AddProject(ISolutionFolderNode solutionFolderNode, IProject newProject)
{
if (solutionFolderNode.Solution.SolutionFolders.Any(
folder => string.Equals(folder.IdGuid, newProject.IdGuid, StringComparison.OrdinalIgnoreCase)))
{
LoggingService.Warn("ProjectService.AddProject: Duplicate IdGuid detected");
newProject.IdGuid = Guid.NewGuid().ToString().ToUpperInvariant();
}
solutionFolderNode.Container.AddFolder(newProject);
OnProjectAdded(new ProjectEventArgs(newProject));
}
/// <summary>
/// Adds a project item to the project, raising the ProjectItemAdded event.
/// Make sure you call project.Save() after adding new items!
/// </summary>
public static void AddProjectItem(IProject project, ProjectItem item)
{
if (project == null) throw new ArgumentNullException("project");
if (item == null) throw new ArgumentNullException("item");
IProjectItemListProvider provider = project as IProjectItemListProvider;
if (provider != null) {
provider.AddProjectItem(item);
OnProjectItemAdded(new ProjectItemEventArgs(project, item));
}
}
/// <summary>
/// Removes a project item from the project, raising the ProjectItemRemoved event.
/// Make sure you call project.Save() after removing items!
/// No action (not even raising the event) is taken when the item was already removed form the project.
/// </summary>
public static void RemoveProjectItem(IProject project, ProjectItem item)
{
if (project == null) throw new ArgumentNullException("project");
if (item == null) throw new ArgumentNullException("item");
IProjectItemListProvider provider = project as IProjectItemListProvider;
if (provider != null) {
if (provider.RemoveProjectItem(item)) {
OnProjectItemRemoved(new ProjectItemEventArgs(project, item));
}
}
}
static void BeforeLoadSolution()
{
if (openSolution != null && !IsClosingCanceled()) {
SaveSolutionPreferences();
SD.Workbench.CloseAllViews();
CloseSolution();
}
}
public static void LoadSolution(string fileName)
{
FileUtility.ObservedLoad(LoadSolutionInternal, fileName);
}
static void LoadSolutionInternal(string fileName)
{
if (!Path.IsPathRooted(fileName))
throw new ArgumentException("Path must be rooted!");
if (IsClosingCanceled())
return;
BeforeLoadSolution();
OnSolutionLoading(fileName);
var solutionProperties = LoadSolutionPreferences(fileName);
try {
openSolution = Solution.Load(fileName, solutionProperties["ActiveConfiguration"], solutionProperties["ActivePlatform"]);
CommandManager.InvalidateRequerySuggested();
SD.ParserService.InvalidateCurrentSolutionSnapshot();
if (openSolution == null)
return;
} catch (IOException ex) {
LoggingService.Warn(ex);
MessageService.ShowError(ex.Message);
return;
} catch (UnauthorizedAccessException ex) {
LoggingService.Warn(ex);
MessageService.ShowError(ex.Message);
return;
}
(openSolution.Preferences as IMementoCapable).SetMemento(solutionProperties);
try {
ApplyConfigurationAndReadProjectPreferences();
} catch (Exception ex) {
MessageService.ShowException(ex);
}
SD.ParserService.InvalidateCurrentSolutionSnapshot();
SD.FileService.RecentOpen.AddRecentProject(openSolution.FileName);
Project.Converter.UpgradeViewContent.ShowIfRequired(openSolution);
// preferences must be read before OnSolutionLoad is called to enable
// the event listeners to read e.Solution.Preferences.Properties
OnSolutionLoaded(new SolutionEventArgs(openSolution));
}
static Properties LoadSolutionPreferences(string solutionFileName)
{
try {
string file = GetPreferenceFileName(solutionFileName);
if (FileUtility.IsValidPath(file) && File.Exists(file)) {
try {
return Properties.Load(file);
} catch (IOException) {
} catch (UnauthorizedAccessException) {
} catch (XmlException) {
// ignore errors about inaccessible or malformed files
}
}
} catch (Exception ex) {
MessageService.ShowException(ex);
}
return new Properties();
}
static void ApplyConfigurationAndReadProjectPreferences()
{
openSolution.ApplySolutionConfigurationAndPlatformToProjects();
foreach (IProject project in openSolution.Projects) {
string file = GetPreferenceFileName(project.FileName);
if (FileUtility.IsValidPath(file) && File.Exists(file)) {
Properties properties = null;
try {
properties = Properties.Load(file);
} catch (IOException) {
} catch (UnauthorizedAccessException) {
} catch (XmlException) {
// ignore errors about inaccessible or malformed files
}
if (properties != null)
project.SetMemento(properties);
}
}
}
/// <summary>
/// Load a single project as solution.
/// </summary>
public static void LoadProject(string fileName)
{
FileUtility.ObservedLoad(LoadProjectInternal, fileName);
}
static void LoadProjectInternal(string fileName)
{
if (!Path.IsPathRooted(fileName))
throw new ArgumentException("Path must be rooted!");
string solutionFile = Path.ChangeExtension(fileName, ".sln");
if (File.Exists(solutionFile)) {
LoadSolutionInternal(solutionFile);
if (openSolution != null) {
bool found = false;
foreach (IProject p in openSolution.Projects) {
if (FileUtility.IsEqualFileName(fileName, p.FileName)) {
found = true;
break;
}
}
if (found == false) {
var parseArgs = new[] { new StringTagPair("SolutionName", Path.GetFileName(solutionFile)), new StringTagPair("ProjectName", Path.GetFileName(fileName))};
int res = MessageService.ShowCustomDialog(MessageService.ProductName,
StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.OpenCombine.SolutionDoesNotContainProject}", parseArgs),
0, 2,
StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.OpenCombine.SolutionDoesNotContainProject.AddProjectToSolution}", parseArgs),
StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.OpenCombine.SolutionDoesNotContainProject.CreateNewSolution}", parseArgs),
"${res:Global.IgnoreButtonText}");
if (res == 0) {
// Add project to solution
Commands.AddExitingProjectToSolution.AddProject((ISolutionFolderNode)ProjectBrowserPad.Instance.SolutionNode, FileName.Create(fileName));
SaveSolution();
return;
} else if (res == 1) {
CloseSolution();
try {
File.Copy(solutionFile, Path.ChangeExtension(solutionFile, ".old.sln"), true);
} catch (IOException){}
} else {
// ignore, just open the solution
return;
}
} else {
// opened solution instead and correctly found the project
return;
}
} else {
// some problem during opening, abort
return;
}
}
Solution solution = new Solution(new ProjectChangeWatcher(solutionFile));
solution.Name = Path.GetFileNameWithoutExtension(fileName);
IProjectBinding binding = ProjectBindingService.GetBindingPerProjectFile(fileName);
IProject project;
if (binding != null) {
project = ProjectBindingService.LoadProject(new ProjectLoadInformation(solution, FileName.Create(fileName), solution.Name));
if (project is UnknownProject) {
if (((UnknownProject)project).WarningDisplayedToUser == false) {
((UnknownProject)project).ShowWarningMessageBox();
}
return;
}
} else {
MessageService.ShowError(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.OpenCombine.InvalidProjectOrCombine}", new StringTagPair("FileName", fileName)));
return;
}
solution.AddFolder(project);
if (FileUtility.ObservedSave((NamedFileOperationDelegate)solution.Save, solutionFile) == FileOperationResult.OK) {
// only load when saved succesfully
LoadSolution(solutionFile);
}
}
public static void SaveSolution()
{
if (openSolution != null) {
openSolution.Save();
foreach (IProject project in openSolution.Projects) {
project.Save();
}
OnSolutionSaved(new SolutionEventArgs(openSolution));
}
}
/// <summary>
/// Gets the list of file filters.
/// </summary>
public static IReadOnlyList<FileFilterDescriptor> GetFileFilters()
{
return AddInTree.BuildItems<FileFilterDescriptor>("/SharpDevelop/Workbench/FileFilter", null);
}
/// <summary>
/// Returns a File Dialog filter that can be used to filter on all registered project formats
/// </summary>
public static string GetAllProjectsFilter(object caller, bool includeSolutions)
{
IEnumerable<FileFilterDescriptor> filters = AddInTree.BuildItems<FileFilterDescriptor>("/SharpDevelop/Workbench/Combine/FileFilter", null);
if (!includeSolutions)
filters = filters.Where(f => !f.ContainsExtension(".sln"));
StringBuilder b = new StringBuilder(StringParser.Parse("${res:SharpDevelop.Solution.AllKnownProjectFormats}|"));
bool first = true;
foreach (var filter in filters) {
string ext = filter.Extensions;
if (ext != "*.*" && ext.Length > 0) {
if (!first) {
b.Append(';');
} else {
first = false;
}
b.Append(ext);
}
}
foreach (var filter in filters) {
b.Append('|');
b.Append(filter.ToString());
}
return b.ToString();
}
internal static string GetPreferenceFileName(string projectFileName)
{
string directory = Path.Combine(PropertyService.ConfigDirectory, "preferences");
return Path.Combine(directory,
Path.GetFileName(projectFileName)
+ "." + projectFileName.ToLowerInvariant().GetHashCode().ToString("x")
+ ".xml");
}
public static void SaveSolutionPreferences()
{
if (openSolution == null)
return;
string directory = Path.Combine(PropertyService.ConfigDirectory, "preferences");
if (!Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
if (SolutionPreferencesSaving != null)
SolutionPreferencesSaving(null, new SolutionEventArgs(openSolution));
Properties memento = (openSolution.Preferences as IMementoCapable).CreateMemento();
string fullFileName = GetPreferenceFileName(openSolution.FileName);
if (FileUtility.IsValidPath(fullFileName)) {
#if DEBUG
memento.Save(fullFileName);
#else
FileUtility.ObservedSave(new NamedFileOperationDelegate(memento.Save), fullFileName, FileErrorPolicy.Inform);
#endif
}
foreach (IProject project in OpenSolution.Projects) {
memento = project.CreateMemento();
if (memento == null) continue;
fullFileName = GetPreferenceFileName(project.FileName);
if (FileUtility.IsValidPath(fullFileName)) {
#if DEBUG
memento.Save(fullFileName);
#else
FileUtility.ObservedSave(new NamedFileOperationDelegate(memento.Save), fullFileName, FileErrorPolicy.Inform);
#endif
}
}
}
/// <summary>
/// Executes the OnBeforeSolutionClosing event.
/// </summary>
/// <remarks>This method must be used after CloseSolution is called.</remarks>
/// <returns><c>true</c>, if closing solution was canceled; <c>false</c>, otherwise.</returns>
internal static bool IsClosingCanceled()
{
// run onbefore closing
var beforeClosingArgs = new SolutionCancelEventArgs(openSolution);
OnBeforeSolutionClosing(beforeClosingArgs);
return beforeClosingArgs.Cancel;
}
/// <summary>
/// Closes the solution: cancels build, clears solution data, fires the SolutionClosing and SolutionClosed events.
/// <remarks>
/// Before invoking this method, one should check if the closing was canceled (<see cref="IsClosingCanceled"/>),
/// save solution and project data (e.g. files, bookmarks), then invoke CloseSolution().
/// </remarks>
/// </summary>
internal static void CloseSolution()
{
// If a build is running, cancel it.
// If we would let a build run but unload the MSBuild projects, the next project.StartBuild call
// could cause an exception.
SD.BuildService.CancelBuild();
if (openSolution != null) {
CurrentProject = null;
OnSolutionClosing(new SolutionEventArgs(openSolution));
openSolution.Dispose();
openSolution = null;
OnSolutionClosed(EventArgs.Empty);
CommandManager.InvalidateRequerySuggested();
}
}
static void OnCurrentProjectChanged(ProjectEventArgs e)
{
if (CurrentProjectChanged != null) {
CurrentProjectChanged(null, e);
}
}
static void OnSolutionClosed(EventArgs e)
{
if (SolutionClosed != null) {
SolutionClosed(null, e);
}
}
static void OnSolutionClosing(SolutionEventArgs e)
{
if (SolutionClosing != null) {
SolutionClosing(null, e);
}
}
static void OnBeforeSolutionClosing(SolutionCancelEventArgs e)
{
if (BeforeSolutionClosing != null) {
BeforeSolutionClosing(null, e);
}
}
static void OnSolutionLoading(string fileName)
{
if (SolutionLoading != null) {
SolutionLoading(fileName, EventArgs.Empty);
}
}
static void OnSolutionLoaded(SolutionEventArgs e)
{
if (SolutionLoaded != null) {
SolutionLoaded(null, e);
}
}
static void OnSolutionSaved(SolutionEventArgs e)
{
if (SolutionSaved != null) {
SolutionSaved(null, e);
}
}
internal static void OnSolutionConfigurationChanged(SolutionConfigurationEventArgs e)
{
if (SolutionConfigurationChanged != null) {
SolutionConfigurationChanged(null, e);
}
}
[Obsolete]
public static bool IsBuilding {
get {
return SD.BuildService.IsBuilding;
}
}
public static void RemoveSolutionFolder(string guid)
{
if (OpenSolution == null) {
return;
}
foreach (ISolutionFolder folder in OpenSolution.SolutionFolders) {
if (folder.IdGuid == guid) {
folder.Parent.RemoveFolder(folder);
OnSolutionFolderRemoved(new SolutionFolderEventArgs(folder));
HandleRemovedSolutionFolder(folder);
break;
}
}
}
static void HandleRemovedSolutionFolder(ISolutionFolder folder)
{
IProject project = folder as IProject;
if (project != null) {
OpenSolution.RemoveProjectConfigurations(project.IdGuid);
OnProjectRemoved(new ProjectEventArgs(project));
project.Dispose();
}
if (folder is ISolutionFolderContainer) {
// recurse into child folders that were also removed
((ISolutionFolderContainer)folder).Folders.ForEach(HandleRemovedSolutionFolder);
}
}
static void OnSolutionFolderRemoved(SolutionFolderEventArgs e)
{
if (SolutionFolderRemoved != null) {
SolutionFolderRemoved(null, e);
}
}
static void OnProjectItemAdded(ProjectItemEventArgs e)
{
if (ProjectItemAdded != null) {
ProjectItemAdded(null, e);
}
}
static void OnProjectItemRemoved(ProjectItemEventArgs e)
{
if (ProjectItemRemoved != null) {
ProjectItemRemoved(null, e);
}
}
static void OnProjectAdded(ProjectEventArgs e)
{
if (ProjectAdded != null) {
ProjectAdded(null, e);
}
}
static void OnProjectRemoved(ProjectEventArgs e)
{
if (ProjectRemoved != null) {
ProjectRemoved(null, e);
}
}
internal static void OnProjectCreated(ProjectEventArgs e)
{
if (ProjectCreated != null) {
ProjectCreated(null, e);
}
}
internal static void OnSolutionCreated(SolutionEventArgs e)
{
if (SolutionCreated != null) {
SolutionCreated(null, e);
}
}
/// <summary>
/// Is raised when a new project is created.
/// </summary>
public static event ProjectEventHandler ProjectCreated;
/// <summary>
/// Is raised when a new or existing project is added to the solution.
/// </summary>
public static event ProjectEventHandler ProjectAdded;
/// <summary>
/// Is raised when a project is removed from the solution.
/// </summary>
public static event ProjectEventHandler ProjectRemoved;
/// <summary>
/// Is raised when a solution folder is removed from the solution.
/// This might remove multiple projects from the solution.
/// </summary>
public static event SolutionFolderEventHandler SolutionFolderRemoved;
[Obsolete]
public static event EventHandler<BuildEventArgs> BuildStarted {
add { SD.BuildService.BuildStarted += value; }
remove { SD.BuildService.BuildFinished -= value; }
}
[Obsolete]
public static event EventHandler<BuildEventArgs> BuildFinished {
add { SD.BuildService.BuildFinished += value; }
remove { SD.BuildService.BuildFinished -= value; }
}
public static event SolutionConfigurationEventHandler SolutionConfigurationChanged;
public static event EventHandler<SolutionEventArgs> SolutionCreated;
public static event EventHandler SolutionLoading;
public static event EventHandler<SolutionEventArgs> SolutionLoaded;
public static event EventHandler<SolutionEventArgs> SolutionSaved;
public static event EventHandler<SolutionEventArgs> SolutionClosing;
public static event EventHandler SolutionClosed;
/// <summary>
/// Raised before SolutionClosing.
/// <remarks>
/// When one modifies the e.Cancel property, should have in mind that other consumers might want to cancel the closing.<br/>
/// Setting e.Cancel = false might override other consumers (if they exist) e.Cancel = true, and might break other functionalities.
/// </remarks>
/// </summary>
public static event EventHandler<SolutionCancelEventArgs> BeforeSolutionClosing;
/// <summary>
/// Raised before the solution preferences are being saved. Allows you to save
/// your additional properties in the solution preferences.
/// </summary>
public static event EventHandler<SolutionEventArgs> SolutionPreferencesSaving;
public static event ProjectEventHandler CurrentProjectChanged;
public static event EventHandler<ProjectItemEventArgs> ProjectItemAdded;
public static event EventHandler<ProjectItemEventArgs> ProjectItemRemoved;
}
}