Browse Source

Change IProject.Items to IMutableModelCollection; and get rid of the IProjectItemListProvider interface.

pull/32/merge
Daniel Grunwald 13 years ago
parent
commit
091d791b5b
  1. 2
      src/Main/Base/Project/Parser/ProjectContentContainer.cs
  2. 8
      src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs
  3. 4
      src/Main/Base/Project/Src/Project/AbstractProject.cs
  4. 9
      src/Main/Base/Project/Src/Project/Converter/LanguageConverter.cs
  5. 29
      src/Main/Base/Project/Src/Project/IProject.cs
  6. 163
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  7. 17
      src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs
  8. 3
      src/Main/Base/Test/Utils/ProjectHelper.cs
  9. 10
      src/Main/SharpDevelop/Project/Solution.cs

2
src/Main/Base/Project/Parser/ProjectContentContainer.cs

@ -279,7 +279,7 @@ namespace ICSharpCode.SharpDevelop.Parser
#region Initialize #region Initialize
void Initialize(IProgressMonitor progressMonitor, List<FileName> filesToParse) void Initialize(IProgressMonitor progressMonitor, List<FileName> filesToParse)
{ {
IReadOnlyCollection<ProjectItem> projectItems = project.Items; IReadOnlyCollection<ProjectItem> projectItems = project.Items.CreateSnapshot();
lock (lockObj) { lock (lockObj) {
if (disposed) { if (disposed) {
return; return;

8
src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs

@ -412,7 +412,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates
#region Create Project Items, Imports and Files #region Create Project Items, Imports and Files
// Add Project items // Add Project items
if (project is IProjectItemListProvider) if (!project.Items.IsReadOnly)
{ {
foreach (ProjectItem projectItem in projectItems) { foreach (ProjectItem projectItem in projectItems) {
ProjectItem newProjectItem = new UnknownProjectItem( ProjectItem newProjectItem = new UnknownProjectItem(
@ -431,7 +431,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates
else else
newProjectItem.SetEvaluatedMetadata(StringParser.Parse(metadataName), StringParser.Parse(metadataValue)); newProjectItem.SetEvaluatedMetadata(StringParser.Parse(metadataName), StringParser.Parse(metadataValue));
} }
((IProjectItemListProvider)project).AddProjectItem(newProjectItem); project.Items.Add(newProjectItem);
} }
} }
@ -482,7 +482,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates
} }
// Add Files // Add Files
if (project is IProjectItemListProvider) { if (!project.Items.IsReadOnly) {
foreach (FileDescriptionTemplate file in files) { foreach (FileDescriptionTemplate file in files) {
string fileName = Path.Combine(projectBasePath, StringParser.Parse(file.Name, new StringTagPair("ProjectName", projectCreateOptions.ProjectName))); string fileName = Path.Combine(projectBasePath, StringParser.Parse(file.Name, new StringTagPair("ProjectName", projectCreateOptions.ProjectName)));
@ -492,7 +492,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates
file.SetProjectItemProperties(projectFile); file.SetProjectItemProperties(projectFile);
((IProjectItemListProvider)project).AddProjectItem(projectFile); project.Items.Add(projectFile);
} }
} }

4
src/Main/Base/Project/Src/Project/AbstractProject.cs

@ -279,9 +279,9 @@ namespace ICSharpCode.SharpDevelop.Project
/// will create a new collection. /// will create a new collection.
/// </summary> /// </summary>
[Browsable(false)] [Browsable(false)]
public virtual IReadOnlyCollection<ProjectItem> Items { public virtual IMutableModelCollection<ProjectItem> Items {
get { get {
return new ProjectItem[0]; return new ImmutableModelCollection<ProjectItem>(Enumerable.Empty<ProjectItem>());
} }
} }

9
src/Main/Base/Project/Src/Project/Converter/LanguageConverter.cs

@ -110,11 +110,8 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
throw new ArgumentNullException("sourceProject"); throw new ArgumentNullException("sourceProject");
if (targetProject == null) if (targetProject == null)
throw new ArgumentNullException("targetProject"); throw new ArgumentNullException("targetProject");
IProjectItemListProvider targetProjectItems = targetProject as IProjectItemListProvider;
if (targetProjectItems == null)
throw new ArgumentNullException("targetProjectItems");
IReadOnlyCollection<ProjectItem> sourceItems = sourceProject.Items; IReadOnlyCollection<ProjectItem> sourceItems = sourceProject.Items.CreateSnapshot();
double totalWork = 0; double totalWork = 0;
foreach (ProjectItem item in sourceItems) { foreach (ProjectItem item in sourceItems) {
totalWork += GetRequiredWork(item); totalWork += GetRequiredWork(item);
@ -136,9 +133,9 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
throw new ConversionException("Error converting " + fileItem.FileName, ex); throw new ConversionException("Error converting " + fileItem.FileName, ex);
} }
} }
targetProjectItems.AddProjectItem(targetItem); targetProject.Items.Add(targetItem);
} else { } else {
targetProjectItems.AddProjectItem(item.CloneFor(targetProject)); targetProject.Items.Add(item.CloneFor(targetProject));
} }
monitor.CancellationToken.ThrowIfCancellationRequested(); monitor.CancellationToken.ThrowIfCancellationRequested();
monitor.Progress += GetRequiredWork(item) / totalWork; monitor.Progress += GetRequiredWork(item) / totalWork;

29
src/Main/Base/Project/Src/Project/IProject.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// The returned collection is guaranteed not to change - adding new items or removing existing items /// The returned collection is guaranteed not to change - adding new items or removing existing items
/// will create a new collection. /// will create a new collection.
/// </summary> /// </summary>
IReadOnlyCollection<ProjectItem> Items { get; } IMutableModelCollection<ProjectItem> Items { get; }
/// <summary> /// <summary>
/// Gets all items in the project that have the specified item type. /// Gets all items in the project that have the specified item type.
@ -310,31 +310,4 @@ namespace ICSharpCode.SharpDevelop.Project
event EventHandler Disposed; event EventHandler Disposed;
} }
/// <summary>
/// Interface for adding and removing items from a project. Not part of the IProject
/// interface because in nearly all cases, ProjectService.Add/RemoveProjectItem should
/// be used instead!
/// So IProject implementors should implement this interface, but only the SharpDevelop methods
/// ProjectService.AddProjectItem and RemoveProjectItem may call the interface members.
/// </summary>
public interface IProjectItemListProvider
{
/// <summary>
/// Gets a list of items in the project.
/// </summary>
IReadOnlyCollection<ProjectItem> Items {
get;
}
/// <summary>
/// Adds a new entry to the Items-collection
/// </summary>
void AddProjectItem(ProjectItem item);
/// <summary>
/// Removes an entry from the Items-collection
/// </summary>
bool RemoveProjectItem(ProjectItem item);
}
} }

163
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -13,8 +13,9 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Internal.Templates; using ICSharpCode.SharpDevelop.Internal.Templates;
using Microsoft.Build.Construction; using Microsoft.Build.Construction;
@ -32,7 +33,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// require locking on the SyncRoot. Methods that return underlying MSBuild objects require that /// require locking on the SyncRoot. Methods that return underlying MSBuild objects require that
/// the caller locks on the SyncRoot. /// the caller locks on the SyncRoot.
/// </summary> /// </summary>
public class MSBuildBasedProject : AbstractProject, IProjectItemListProvider public class MSBuildBasedProject : AbstractProject
{ {
/// <summary> /// <summary>
/// The project collection that contains this project. /// The project collection that contains this project.
@ -159,6 +160,7 @@ namespace ICSharpCode.SharpDevelop.Project
public MSBuildBasedProject(ProjectCreateInformation information) public MSBuildBasedProject(ProjectCreateInformation information)
: base(information) : base(information)
{ {
this.itemsCollection = new ProjectItemCollection(this);
this.projectFile = ProjectRootElement.Create(MSBuildProjectCollection); this.projectFile = ProjectRootElement.Create(MSBuildProjectCollection);
this.userProjectFile = ProjectRootElement.Create(MSBuildProjectCollection); this.userProjectFile = ProjectRootElement.Create(MSBuildProjectCollection);
@ -914,26 +916,114 @@ namespace ICSharpCode.SharpDevelop.Project
} }
#endregion #endregion
#region IProjectItemListProvider interface #region Item management
List<ProjectItem> items = new List<ProjectItem>(); class ProjectItemCollection : IMutableModelCollection<ProjectItem>
volatile IReadOnlyCollection<ProjectItem> itemsReadOnly; {
volatile IReadOnlyCollection<ItemType> availableFileItemTypes = ItemType.DefaultFileItems; readonly MSBuildBasedProject project;
/// <summary> public ProjectItemCollection(MSBuildBasedProject project)
/// Gets the list of items in the project. This member is thread-safe. {
/// The returned collection is guaranteed not to change - adding new items or removing existing items this.project = project;
/// will create a new collection. }
/// </summary>
public override IReadOnlyCollection<ProjectItem> Items { public IReadOnlyCollection<ProjectItem> CreateSnapshot()
get { {
IReadOnlyCollection<ProjectItem> c = itemsReadOnly; IReadOnlyCollection<ProjectItem> c = project.itemsReadOnly;
if (c == null) { if (c == null) {
lock (SyncRoot) { lock (project.SyncRoot) {
itemsReadOnly = c = items.ToArray(); project.itemsReadOnly = c = project.items.ToArray();
} }
} }
return c; return c;
} }
public event ModelCollectionChangedEventHandler<ProjectItem> CollectionChanged = delegate {};
public int Count {
get { return CreateSnapshot().Count; }
}
bool ICollection<ProjectItem>.IsReadOnly {
get { return false; }
}
void IMutableModelCollection<ProjectItem>.AddRange(IEnumerable<ProjectItem> items)
{
var newItems = items.ToList();
lock (project.SyncRoot) {
foreach (var item in newItems)
project.AddProjectItem(item);
}
CollectionChanged(EmptyList<ProjectItem>.Instance, newItems);
}
int IMutableModelCollection<ProjectItem>.RemoveAll(Predicate<ProjectItem> predicate)
{
List<ProjectItem> removed = new List<ProjectItem>();
foreach (var item in CreateSnapshot()) {
if (predicate(item)) {
if (project.RemoveProjectItem(item))
removed.Add(item);
}
}
CollectionChanged(removed, EmptyList<ProjectItem>.Instance);
return removed.Count;
}
IDisposable IMutableModelCollection<ProjectItem>.BatchUpdate()
{
return null; // not supported
}
IEnumerator<ProjectItem> IEnumerable<ProjectItem>.GetEnumerator()
{
return CreateSnapshot().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return CreateSnapshot().GetEnumerator();
}
void ICollection<ProjectItem>.Add(ProjectItem item)
{
project.AddProjectItem(item);
CollectionChanged(EmptyList<ProjectItem>.Instance, new[] { item });
}
void ICollection<ProjectItem>.Clear()
{
throw new NotImplementedException();
}
bool ICollection<ProjectItem>.Contains(ProjectItem item)
{
return CreateSnapshot().Contains(item);
}
void ICollection<ProjectItem>.CopyTo(ProjectItem[] array, int arrayIndex)
{
foreach (var item in CreateSnapshot())
array[arrayIndex++] = item;
}
bool ICollection<ProjectItem>.Remove(ProjectItem item)
{
if (project.RemoveProjectItem(item)) {
CollectionChanged(new[] { item }, EmptyList<ProjectItem>.Instance);
return true;
}
return false;
}
}
readonly List<ProjectItem> items = new List<ProjectItem>();
volatile IReadOnlyCollection<ProjectItem> itemsReadOnly;
readonly ProjectItemCollection itemsCollection;
volatile IReadOnlyCollection<ItemType> availableFileItemTypes = ItemType.DefaultFileItems;
public override IMutableModelCollection<ProjectItem> Items {
get { return itemsCollection; }
} }
/// <summary> /// <summary>
@ -973,6 +1063,7 @@ namespace ICSharpCode.SharpDevelop.Project
} }
ClearFindFileCache(); ClearFindFileCache();
// TODO: raise the CollectionChanged event with the appropriate arguments
} }
// refresh project browser to make sure references and other project items are still valid // refresh project browser to make sure references and other project items are still valid
@ -981,7 +1072,7 @@ namespace ICSharpCode.SharpDevelop.Project
ProjectBrowserPad.RefreshViewAsync(); ProjectBrowserPad.RefreshViewAsync();
} }
void IProjectItemListProvider.AddProjectItem(ProjectItem item) void AddProjectItem(ProjectItem item)
{ {
if (item == null) if (item == null)
throw new ArgumentNullException("item"); throw new ArgumentNullException("item");
@ -994,31 +1085,6 @@ namespace ICSharpCode.SharpDevelop.Project
using (var c = OpenCurrentConfiguration()) { using (var c = OpenCurrentConfiguration()) {
items.Add(item); items.Add(item);
itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call
/*foreach (var g in projectFile.ItemGroups) {
if (!string.IsNullOrEmpty(g.Condition) || g.Count == 0)
continue;
var firstItemInGroup = g.Items.First();
if (firstItemInGroup.Name == item.ItemType.ItemName) {
MSBuildInternals.AddItemToGroup(g, item);
return;
}
if (firstItemInGroup.ItemType == ItemType.Reference.ItemName)
continue;
if (ItemType.DefaultFileItems.Contains(new ItemType(firstItemInGroup.ItemType))) {
if (ItemType.DefaultFileItems.Contains(item.ItemType)) {
MSBuildInternals.AddItemToGroup(g, item);
return;
} else {
continue;
}
}
MSBuildInternals.AddItemToGroup(g, item);
return;
}
var newGroup = projectFile.AddItemGroup();
MSBuildInternals.AddItemToGroup(newGroup, item);*/
string newInclude = item.TreatIncludeAsLiteral ? MSBuildInternals.Escape(item.Include) : item.Include; string newInclude = item.TreatIncludeAsLiteral ? MSBuildInternals.Escape(item.Include) : item.Include;
var newMetadata = new Dictionary<string, string>(); var newMetadata = new Dictionary<string, string>();
@ -1031,9 +1097,12 @@ namespace ICSharpCode.SharpDevelop.Project
item.BuildItem = new MSBuildItemWrapper((MSBuildBasedProject)item.Project, newItems[0]); item.BuildItem = new MSBuildItemWrapper((MSBuildBasedProject)item.Project, newItems[0]);
Debug.Assert(item.IsAddedToProject); Debug.Assert(item.IsAddedToProject);
} }
IProjectServiceRaiseEvents re = SD.GetService<IProjectServiceRaiseEvents>();
if (re != null)
re.RaiseProjectItemAdded(new ProjectItemEventArgs(this, item));
} }
bool IProjectItemListProvider.RemoveProjectItem(ProjectItem item) bool RemoveProjectItem(ProjectItem item)
{ {
if (item == null) if (item == null)
throw new ArgumentNullException("item"); throw new ArgumentNullException("item");
@ -1048,11 +1117,14 @@ namespace ICSharpCode.SharpDevelop.Project
itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call
c.Project.RemoveItem(backend.MSBuildItem); c.Project.RemoveItem(backend.MSBuildItem);
item.BuildItem = null; // make the item free again item.BuildItem = null; // make the item free again
return true;
} else { } else {
throw new InvalidOperationException("Expected that the item is added to this project!"); throw new InvalidOperationException("Expected that the item is added to this project!");
} }
} }
IProjectServiceRaiseEvents re = SD.GetService<IProjectServiceRaiseEvents>();
if (re != null)
re.RaiseProjectItemRemoved(new ProjectItemEventArgs(this, item));
return true;
} }
#endregion #endregion
@ -1117,6 +1189,7 @@ namespace ICSharpCode.SharpDevelop.Project
public MSBuildBasedProject(ProjectLoadInformation loadInformation) public MSBuildBasedProject(ProjectLoadInformation loadInformation)
: base(loadInformation) : base(loadInformation)
{ {
this.itemsCollection = new ProjectItemCollection(this);
isLoading = true; isLoading = true;
bool success = false; bool success = false;
try { try {

17
src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

@ -60,13 +60,7 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
if (project == null) throw new ArgumentNullException("project"); if (project == null) throw new ArgumentNullException("project");
if (item == null) throw new ArgumentNullException("item"); if (item == null) throw new ArgumentNullException("item");
IProjectItemListProvider provider = project as IProjectItemListProvider; project.Items.Add(item);
if (provider != null) {
provider.AddProjectItem(item);
IProjectServiceRaiseEvents re = SD.GetService<IProjectServiceRaiseEvents>();
if (re != null)
re.RaiseProjectItemAdded(new ProjectItemEventArgs(project, item));
}
} }
/// <summary> /// <summary>
@ -78,14 +72,7 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
if (project == null) throw new ArgumentNullException("project"); if (project == null) throw new ArgumentNullException("project");
if (item == null) throw new ArgumentNullException("item"); if (item == null) throw new ArgumentNullException("item");
IProjectItemListProvider provider = project as IProjectItemListProvider; project.Items.Remove(item);
if (provider != null) {
if (provider.RemoveProjectItem(item)) {
IProjectServiceRaiseEvents re = SD.GetService<IProjectServiceRaiseEvents>();
if (re != null)
re.RaiseProjectItemRemoved(new ProjectItemEventArgs(project, item));
}
}
} }
[Obsolete("Use SD.ProjectService.OpenSolutionOrProject() instead")] [Obsolete("Use SD.ProjectService.OpenSolutionOrProject() instead")]

3
src/Main/Base/Test/Utils/ProjectHelper.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using Rhino.Mocks; using Rhino.Mocks;
@ -22,7 +23,7 @@ namespace ICSharpCode.SharpDevelop.Tests.Utils
Project Project
.Stub(p => p.Items) .Stub(p => p.Items)
.Return(null) .Return(null)
.WhenCalled(mi => mi.ReturnValue = new ReadOnlyCollection<ProjectItem>(ProjectItems)); .WhenCalled(mi => mi.ReturnValue = new ImmutableModelCollection<ProjectItem>(ProjectItems));
Project.Stub(p => p.ProjectSpecificProperties).Return(new Properties()); Project.Stub(p => p.ProjectSpecificProperties).Return(new Properties());

10
src/Main/SharpDevelop/Project/Solution.cs

@ -411,13 +411,9 @@ namespace ICSharpCode.SharpDevelop.Project
foreach (IProject project in this.Projects) { foreach (IProject project in this.Projects) {
if (FileUtility.IsBaseDirectory(project.Directory, fileName)) { if (FileUtility.IsBaseDirectory(project.Directory, fileName)) {
IProjectItemListProvider provider = project as IProjectItemListProvider; foreach (ProjectItem item in project.Items.ToArray()) {
if (provider != null) { if (FileUtility.IsBaseDirectory(fileName, item.FileName)) {
foreach (ProjectItem item in provider.Items.ToArray()) { project.Items.Remove(item);
if (FileUtility.IsBaseDirectory(fileName, item.FileName)) {
provider.RemoveProjectItem(item);
SD.GetRequiredService<IProjectServiceRaiseEvents>().RaiseProjectItemRemoved(new ProjectItemEventArgs(project, item));
}
} }
} }
} }

Loading…
Cancel
Save