#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.
 
 
 
 
 
 

842 lines
24 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.IO;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Xml;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Dom;
namespace ICSharpCode.Core
{
public class DefaultProjectContent : IProjectContent
{
protected readonly List<IProjectContent> referencedContents = new List<IProjectContent>();
// we use a list of Dictionaries because we need multiple dictionaries:
// each uses another StringComparer
// (C#: StringComparer.InvariantCulture, VB: StringComparer.InvariantCultureCaseInsensitive)
// new dictionaries are added to the list when required
List<Dictionary<string, IClass>> classLists = new List<Dictionary<string, IClass>>();
List<Dictionary<string, NamespaceStruct>> namespaces = new List<Dictionary<string, NamespaceStruct>>();
protected XmlDoc xmlDoc = new XmlDoc();
IUsing defaultImports;
public IUsing DefaultImports {
get {
return defaultImports;
}
set {
defaultImports = value;
}
}
public virtual IProject Project {
get {
return null;
}
}
public List<Dictionary<string, IClass>> ClassLists {
get {
if (classLists.Count == 0) {
classLists.Add(new Dictionary<string, IClass>(language.NameComparer));
}
return classLists;
}
}
public ICollection<string> NamespaceNames {
get {
return Namespaces[0].Keys;
}
}
protected List<Dictionary<string, NamespaceStruct>> Namespaces {
get {
if (namespaces.Count == 0) {
namespaces.Add(new Dictionary<string, NamespaceStruct>(language.NameComparer));
}
return namespaces;
}
}
protected struct NamespaceStruct
{
public readonly List<IClass> Classes;
public readonly List<string> SubNamespaces;
public NamespaceStruct(string name) // struct must have a parameter
{
this.Classes = new List<IClass>();
this.SubNamespaces = new List<string>();
}
}
/// <summary>
/// Gets the class dictionary that uses the name comparison rules of <paramref name="language"/>.
/// </summary>
protected Dictionary<string, IClass> GetClasses(LanguageProperties language)
{
for (int i = 0; i < classLists.Count; ++i) {
if (classLists[i].Comparer == language.NameComparer)
return classLists[i];
}
Dictionary<string, IClass> d;
if (classLists.Count > 0) {
Dictionary<string, IClass> oldList = classLists[0];
d = new Dictionary<string, IClass>(oldList.Count, language.NameComparer);
foreach (KeyValuePair<string, IClass> pair in oldList) {
d.Add(pair.Key, pair.Value);
}
} else {
d = new Dictionary<string, IClass>(language.NameComparer);
}
classLists.Add(d);
return d;
}
/// <summary>
/// Gets the namespace dictionary that uses the name comparison rules of <paramref name="language"/>.
/// </summary>
protected Dictionary<string, NamespaceStruct> GetNamespaces(LanguageProperties language)
{
for (int i = 0; i < namespaces.Count; ++i) {
if (namespaces[i].Comparer == language.NameComparer)
return namespaces[i];
}
Dictionary<string, NamespaceStruct> d;
if (namespaces.Count > 0) {
Dictionary<string, NamespaceStruct> oldList = namespaces[0];
d = new Dictionary<string, NamespaceStruct>(oldList.Count, language.NameComparer);
foreach (KeyValuePair<string, NamespaceStruct> pair in oldList) {
d.Add(pair.Key, pair.Value);
}
} else {
d = new Dictionary<string, NamespaceStruct>(language.NameComparer);
}
namespaces.Add(d);
return d;
}
public XmlDoc XmlDoc {
get {
return xmlDoc;
}
}
public ICollection<IClass> Classes {
get {
lock (namespaces) {
List<IClass> list = new List<IClass>(ClassLists[0].Count + 10);
foreach (IClass c in ClassLists[0].Values) {
if (c is GenericClassContainer) {
GenericClassContainer gcc = (GenericClassContainer)c;
list.AddRange(gcc.RealClasses);
} else {
list.Add(c);
}
}
return list;
}
}
}
public ICollection<IProjectContent> ReferencedContents {
get {
return referencedContents;
}
}
LanguageProperties language = LanguageProperties.CSharp;
/// <summary>
/// Gets/Sets the properties of the language this project content was written in.
/// </summary>
public LanguageProperties Language {
[DebuggerStepThrough]
get {
return language;
}
set {
if (value == null) throw new ArgumentNullException();
language = value;
}
}
public string GetXmlDocumentation(string memberTag)
{
string desc = xmlDoc.GetDocumentation(memberTag);
if (desc != null) {
return desc;
}
foreach (IProjectContent referencedContent in referencedContents) {
desc = referencedContent.XmlDoc.GetDocumentation(memberTag);
if (desc != null) {
return desc;
}
}
return null;
}
protected static string LookupLocalizedXmlDoc(string fileName)
{
string xmlFileName = Path.ChangeExtension(fileName, ".xml");
string localizedXmlDocFile = FileUtility.Combine(System.IO.Path.GetDirectoryName(fileName), Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName, System.IO.Path.GetFileName(xmlFileName));
if (File.Exists(localizedXmlDocFile)) {
return localizedXmlDocFile;
}
if (File.Exists(xmlFileName)) {
return xmlFileName;
}
return null;
}
public virtual void Dispose()
{
xmlDoc.Dispose();
}
public void AddClassToNamespaceList(IClass addClass)
{
lock (namespaces) {
AddClassToNamespaceListInternal(addClass);
}
}
/// <summary>
/// Container class that is used when multiple classes with different type parameter
/// count have the same class name.
/// </summary>
private class GenericClassContainer : DefaultClass
{
public GenericClassContainer(string fullyQualifiedName) : base(null, fullyQualifiedName) {}
IClass[] realClasses = new IClass[4];
public IEnumerable<IClass> RealClasses {
get {
foreach (IClass c in realClasses) {
if (c != null) yield return c;
}
}
}
public int RealClassCount {
get {
int count = 0;
foreach (IClass c in realClasses) {
if (c != null) count += 1;
}
return count;
}
}
public IClass Get(int typeParameterCount)
{
if (realClasses.Length > typeParameterCount)
return realClasses[typeParameterCount];
else
return null;
}
public IClass GetBest(int typeParameterCount)
{
IClass c;
for (int i = typeParameterCount; i < realClasses.Length; i++) {
c = Get(i);
if (c != null) return c;
}
for (int i = typeParameterCount - 1; i >= 0; i--) {
c = Get(i);
if (c != null) return c;
}
return null;
}
public void Set(IClass c)
{
int typeParameterCount = c.TypeParameters.Count;
if (realClasses.Length <= typeParameterCount) {
IClass[] newArray = new IClass[typeParameterCount + 2];
realClasses.CopyTo(newArray, 0);
realClasses = newArray;
}
realClasses[typeParameterCount] = c;
}
public void Remove(int typeParameterCount)
{
if (realClasses.Length > typeParameterCount)
realClasses[typeParameterCount] = null;
}
}
protected void AddClassToNamespaceListInternal(IClass addClass)
{
string fullyQualifiedName = addClass.FullyQualifiedName;
if (addClass.IsPartial) {
LoggingService.Debug("Adding partial class " + addClass.Name + " from " + Path.GetFileName(addClass.CompilationUnit.FileName));
CompoundClass compound = GetClassInternal(fullyQualifiedName, addClass.TypeParameters.Count, language) as CompoundClass;
if (compound != null) {
// possibly replace existing class (look for CU with same filename)
lock (compound) {
for (int i = 0; i < compound.Parts.Count; i++) {
if (compound.Parts[i].CompilationUnit.FileName == addClass.CompilationUnit.FileName) {
compound.Parts[i] = addClass;
compound.UpdateInformationFromParts();
LoggingService.Debug("Replaced old part!");
return;
}
}
compound.Parts.Add(addClass);
compound.UpdateInformationFromParts();
}
LoggingService.Debug("Added new part!");
return;
} else {
addClass = new CompoundClass(addClass);
LoggingService.Debug("Compound created!");
}
}
IClass oldDictionaryClass;
if (GetClasses(language).TryGetValue(fullyQualifiedName, out oldDictionaryClass)) {
GenericClassContainer gcc = oldDictionaryClass as GenericClassContainer;
if (gcc != null) {
gcc.Set(addClass);
return;
} else if (oldDictionaryClass.TypeParameters.Count != addClass.TypeParameters.Count) {
gcc = new GenericClassContainer(fullyQualifiedName);
gcc.Set(addClass);
gcc.Set(oldDictionaryClass);
addClass = gcc;
}
}
foreach (Dictionary<string, IClass> classes in ClassLists) {
classes[addClass.FullyQualifiedName] = addClass;
}
string nSpace = addClass.Namespace;
if (nSpace == null) {
nSpace = String.Empty;
}
CreateNamespace(nSpace);
List<IClass> classList = GetNamespaces(this.language)[nSpace].Classes;
for (int i = 0; i < classList.Count; i++) {
if (classList[i].FullyQualifiedName == addClass.FullyQualifiedName) {
classList[i] = addClass;
return;
}
}
classList.Add(addClass);
}
void CreateNamespace(string nSpace)
{
Dictionary<string, NamespaceStruct> dict = GetNamespaces(this.language);
if (dict.ContainsKey(nSpace))
return;
NamespaceStruct namespaceStruct = new NamespaceStruct(nSpace);
dict.Add(nSpace, namespaceStruct);
// use the same namespaceStruct for all dictionaries
foreach (Dictionary<string, NamespaceStruct> otherDict in namespaces) {
if (otherDict == dict) continue;
otherDict.Add(nSpace, namespaceStruct);
}
if (nSpace.Length == 0)
return;
// add to parent namespace
int pos = nSpace.LastIndexOf('.');
string parent;
string subNs;
if (pos < 0) {
parent = "";
subNs = nSpace;
} else {
parent = nSpace.Substring(0, pos);
subNs = nSpace.Substring(pos + 1);
}
CreateNamespace(parent);
dict[parent].SubNamespaces.Add(subNs);
}
/// <summary>
/// Removes the specified namespace from all namespace lists if the namespace is empty.
/// </summary>
void RemoveEmptyNamespace(string nSpace)
{
if (nSpace == null || nSpace.Length == 0) return;
Dictionary<string, NamespaceStruct> dict = GetNamespaces(this.language);
if (!dict.ContainsKey(nSpace))
return;
// remove only if really empty
if (dict[nSpace].Classes.Count > 0 || dict[nSpace].SubNamespaces.Count > 0)
return;
// remove the namespace from all dictionaries
foreach (Dictionary<string, NamespaceStruct> anyDict in namespaces) {
anyDict.Remove(nSpace);
}
// remove the namespace from parent's SubNamespaces list
int pos = nSpace.LastIndexOf('.');
string parent;
string subNs;
if (pos < 0) {
parent = "";
subNs = nSpace;
} else {
parent = nSpace.Substring(0, pos);
subNs = nSpace.Substring(pos + 1);
}
dict[parent].SubNamespaces.Remove(subNs);
RemoveEmptyNamespace(parent); // remove parent if also empty
}
public void RemoveCompilationUnit(ICompilationUnit unit)
{
lock (namespaces) {
foreach (IClass c in unit.Classes) {
RemoveClass(c);
}
}
}
public void UpdateCompilationUnit(ICompilationUnit oldUnit, ICompilationUnit parserOutput, string fileName, bool updateCommentTags)
{
if (updateCommentTags) {
TaskService.UpdateCommentTags(fileName, parserOutput.TagComments);
}
lock (namespaces) {
if (oldUnit != null) {
RemoveClasses(oldUnit, parserOutput);
}
foreach (IClass c in parserOutput.Classes) {
AddClassToNamespaceListInternal(c);
}
}
}
void RemoveClasses(ICompilationUnit oldUnit, ICompilationUnit newUnit)
{
foreach (IClass c in oldUnit.Classes) {
bool found = false;
foreach (IClass c2 in newUnit.Classes) {
if (c.FullyQualifiedName == c2.FullyQualifiedName) {
found = true;
break;
}
}
if (!found) {
RemoveClass(c);
}
}
}
void RemoveClass(IClass @class)
{
string fullyQualifiedName = @class.FullyQualifiedName;
if (@class.IsPartial) {
// remove a part of a partial class
// Use "as" cast to fix SD2-680: the stored class might be a part not marked as partial
CompoundClass compound = GetClassInternal(fullyQualifiedName, @class.TypeParameters.Count, language) as CompoundClass;
if (compound == null) return;
lock (compound) {
compound.Parts.Remove(@class);
if (compound.Parts.Count > 0) {
compound.UpdateInformationFromParts();
return;
} else {
@class = compound; // all parts removed, remove compound class
}
}
}
IClass classInDictionary;
if (!GetClasses(language).TryGetValue(fullyQualifiedName, out classInDictionary)) {
return;
}
GenericClassContainer gcc = classInDictionary as GenericClassContainer;
if (gcc != null) {
gcc.Remove(@class.TypeParameters.Count);
if (gcc.RealClassCount > 0) {
return;
}
}
foreach (Dictionary<string, IClass> classes in ClassLists) {
classes.Remove(fullyQualifiedName);
}
string nSpace = @class.Namespace;
if (nSpace == null) {
nSpace = String.Empty;
}
// Remove class from namespace lists
List<IClass> classList = GetNamespaces(this.language)[nSpace].Classes;
for (int i = 0; i < classList.Count; i++) {
if (language.NameComparer.Equals(classList[i].FullyQualifiedName, fullyQualifiedName)) {
classList.RemoveAt(i);
break;
}
}
if (classList.Count == 0) {
RemoveEmptyNamespace(nSpace);
}
}
#region Default Parser Layer dependent functions
public IClass GetClass(string typeName)
{
return GetClass(typeName, 0);
}
public IClass GetClass(string typeName, int typeParameterCount)
{
return GetClass(typeName, typeParameterCount, language, true);
}
protected IClass GetClassInternal(string typeName, int typeParameterCount, LanguageProperties language)
{
lock (namespaces) {
IClass c;
if (GetClasses(language).TryGetValue(typeName, out c)) {
GenericClassContainer gcc = c as GenericClassContainer;
if (gcc != null) {
return gcc.GetBest(typeParameterCount);
}
return c;
}
return null;
}
}
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, bool lookInReferences)
{
IClass c = GetClassInternal(typeName, typeParameterCount, language);
if (c != null && c.TypeParameters.Count == typeParameterCount) {
return c;
}
// Search in references:
if (lookInReferences) {
foreach (IProjectContent content in referencedContents) {
IClass contentClass = content.GetClass(typeName, typeParameterCount, language, false);
if (contentClass != null) {
if (contentClass.TypeParameters.Count == typeParameterCount) {
return contentClass;
} else {
c = contentClass;
}
}
}
}
if (c != null) {
return c;
}
// not found -> maybe nested type -> trying to find class that contains this one.
int lastIndex = typeName.LastIndexOf('.');
if (lastIndex > 0) {
string outerName = typeName.Substring(0, lastIndex);
IClass upperClass = GetClassInternal(outerName, typeParameterCount, language);
if (upperClass != null) {
List<IClass> innerClasses = upperClass.InnerClasses;
if (innerClasses != null) {
string innerName = typeName.Substring(lastIndex + 1);
foreach (IClass innerClass in innerClasses) {
if (language.NameComparer.Equals(innerClass.Name, innerName)) {
return innerClass;
}
}
}
}
}
return null;
}
public ArrayList GetNamespaceContents(string nameSpace)
{
ArrayList namespaceList = new ArrayList();
AddNamespaceContents(namespaceList, nameSpace, language, true);
return namespaceList;
}
/// <summary>
/// Adds the contents of the specified <paramref name="nameSpace"/> to the <paramref name="list"/>.
/// </summary>
public void AddNamespaceContents(ArrayList list, string nameSpace, LanguageProperties language, bool lookInReferences)
{
if (nameSpace == null) {
return;
}
if (lookInReferences) {
foreach (IProjectContent content in referencedContents) {
content.AddNamespaceContents(list, nameSpace, language, false);
}
}
Dictionary<string, NamespaceStruct> dict = GetNamespaces(language);
if (dict.ContainsKey(nameSpace)) {
NamespaceStruct ns = dict[nameSpace];
int newCapacity = list.Count + ns.Classes.Count + ns.SubNamespaces.Count;
if (list.Capacity < newCapacity)
list.Capacity = newCapacity;
foreach (IClass c in ns.Classes) {
if (c is GenericClassContainer) {
foreach (IClass realClass in ((GenericClassContainer)c).RealClasses) {
AddNamespaceContentsClass(list, realClass, language, lookInReferences);
}
} else {
AddNamespaceContentsClass(list, c, language, lookInReferences);
}
}
foreach (string subns in ns.SubNamespaces) {
if (!list.Contains(subns))
list.Add(subns);
}
}
}
void AddNamespaceContentsClass(ArrayList list, IClass c, LanguageProperties language, bool lookInReferences)
{
if (c.IsInternal && !lookInReferences) {
// internal class and we are looking at it from another project content
return;
}
if (language.ShowInNamespaceCompletion(c))
list.Add(c);
if (language.ImportModules && c.ClassType == ClassType.Module) {
foreach (IMember m in c.Methods) {
if (m.IsAccessible(null, false))
list.Add(m);
}
foreach (IMember m in c.Events) {
if (m.IsAccessible(null, false))
list.Add(m);
}
foreach (IMember m in c.Fields) {
if (m.IsAccessible(null, false))
list.Add(m);
}
foreach (IMember m in c.Properties) {
if (m.IsAccessible(null, false))
list.Add(m);
}
}
}
public bool NamespaceExists(string name)
{
return NamespaceExists(name, language, true);
}
public bool NamespaceExists(string name, LanguageProperties language, bool lookInReferences)
{
if (name == null) {
return false;
}
if (lookInReferences) {
foreach (IProjectContent content in referencedContents) {
if (content.NamespaceExists(name, language, false)) {
return true;
}
}
}
return GetNamespaces(language).ContainsKey(name);
}
public string SearchNamespace(string name, IClass curType, ICompilationUnit unit, int caretLine, int caretColumn)
{
if (NamespaceExists(name)) {
return name;
}
if (unit == null) {
return null;
}
foreach (IUsing u in unit.Usings) {
if (u != null) {
string nameSpace = u.SearchNamespace(name);
if (nameSpace != null) {
return nameSpace;
}
}
}
if (defaultImports != null) {
string nameSpace = defaultImports.SearchNamespace(name);
if (nameSpace != null) {
return nameSpace;
}
}
if (curType != null) {
// Try parent namespaces of the current class
string fullname = curType.Namespace;
if (fullname != null && fullname.Length > 0) {
string[] namespaces = fullname.Split('.');
StringBuilder curnamespace = new StringBuilder();
for (int i = 0; i < namespaces.Length; ++i) {
curnamespace.Append(namespaces[i]);
curnamespace.Append('.');
curnamespace.Append(name);
string nameSpace = curnamespace.ToString();
if (NamespaceExists(nameSpace)) {
return nameSpace;
}
// remove class name again to try next namespace
curnamespace.Length -= name.Length;
}
}
}
return null;
}
public IReturnType SearchType(string name, int typeParameterCount, IClass curType, int caretLine, int caretColumn)
{
if (curType == null) {
return SearchType(name, typeParameterCount, null, null, caretLine, caretColumn);
}
return SearchType(name, typeParameterCount, curType, curType.CompilationUnit, caretLine, caretColumn);
}
public IReturnType SearchType(string name, int typeParameterCount, IClass curType, ICompilationUnit unit, int caretLine, int caretColumn)
{
if (name == null || name.Length == 0) {
return null;
}
// Try if name is already the full type name
IClass c = GetClass(name, typeParameterCount);
if (c != null) {
return c.DefaultReturnType;
}
// fallback-class if the one with the right type parameter count is not found.
IReturnType fallbackClass = null;
if (curType != null) {
// Try parent namespaces of the current class
string fullname = curType.FullyQualifiedName;
string[] namespaces = fullname.Split('.');
StringBuilder curnamespace = new StringBuilder();
for (int i = 0; i < namespaces.Length; ++i) {
curnamespace.Append(namespaces[i]);
curnamespace.Append('.');
curnamespace.Append(name);
c = GetClass(curnamespace.ToString(), typeParameterCount);
if (c != null) {
if (c.TypeParameters.Count == typeParameterCount)
return c.DefaultReturnType;
else
fallbackClass = c.DefaultReturnType;
}
// remove class name again to try next namespace
curnamespace.Length -= name.Length;
}
if (name.IndexOf('.') < 0) {
// Try inner classes (in full inheritance tree)
// Don't use loop with cur = cur.BaseType because of inheritance cycles
foreach (IClass baseClass in curType.ClassInheritanceTree) {
if (baseClass.ClassType == ClassType.Class) {
foreach (IClass innerClass in baseClass.InnerClasses) {
if (language.NameComparer.Equals(innerClass.Name, name))
return innerClass.DefaultReturnType;
}
}
}
}
}
if (unit != null) {
// Combine name with usings
foreach (IUsing u in unit.Usings) {
if (u != null) {
IReturnType r = u.SearchType(name, typeParameterCount);
if (r != null) {
if (r.TypeParameterCount == typeParameterCount) {
return r;
} else {
fallbackClass = r;
}
}
}
}
}
if (defaultImports != null) {
IReturnType r = defaultImports.SearchType(name, typeParameterCount);
if (r != null) {
if (r.TypeParameterCount == typeParameterCount) {
return r;
} else {
fallbackClass = r;
}
}
}
return fallbackClass;
}
/// <summary>
/// Gets the position of a member in this project content (not a referenced one).
/// </summary>
/// <param name="fullMemberName">Fully qualified member name (always case sensitive).</param>
public Position GetPosition(string fullMemberName)
{
IClass curClass = GetClass(fullMemberName, 0, LanguageProperties.CSharp, false);
if (curClass != null) {
return new Position(curClass.CompilationUnit, curClass.Region.BeginLine, curClass.Region.BeginColumn);
}
int pos = fullMemberName.LastIndexOf('.');
if (pos > 0) {
string className = fullMemberName.Substring(0, pos);
string memberName = fullMemberName.Substring(pos + 1);
curClass = GetClass(className, 0, LanguageProperties.CSharp, false);
if (curClass != null) {
IMember member = curClass.SearchMember(memberName, LanguageProperties.CSharp);
if (member != null) {
return new Position(member.DeclaringType.CompilationUnit, member.Region.BeginLine, member.Region.BeginColumn);
}
}
}
return new Position(null, -1, -1);
}
#endregion
public event EventHandler ReferencedContentsChanged;
protected virtual void OnReferencedContentsChanged(EventArgs e)
{
if (ReferencedContentsChanged != null) {
ReferencedContentsChanged(this, e);
}
}
}
}