// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision: 3675 $</version>
// </file>
using System;
using System.Linq;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop.Dom
{
public class DefaultClass : AbstractEntity, IClass, IComparable
{
ClassType classType;
DomRegion region;
ICompilationUnit compilationUnit;
IList<IReturnType> baseTypes;
IList<IClass> innerClasses;
IList<IField> fields;
IList<IProperty> properties;
IList<IMethod> methods;
IList<IEvent> events;
IList<ITypeParameter> typeParameters;
IUsingScope usingScope;
protected override void FreezeInternal()
{
baseTypes = FreezeList(baseTypes);
innerClasses = FreezeList(innerClasses);
fields = FreezeList(fields);
properties = FreezeList(properties);
methods = FreezeList(methods);
events = FreezeList(events);
typeParameters = FreezeList(typeParameters);
base.FreezeInternal();
}
/*
public virtual IClass Unfreeze()
{
DefaultClass copy = new DefaultClass(compilationUnit, DeclaringType);
copy.FullyQualifiedName = this.FullyQualifiedName;
copy.Attributes.AddRange(this.Attributes);
copy.BaseTypes.AddRange(this.BaseTypes);
copy.BodyRegion = this.BodyRegion;
copy.ClassType = this.ClassType;
copy.Documentation = this.Documentation;
copy.Events.AddRange(this.Events);
copy.Fields.AddRange(this.Fields);
copy.InnerClasses.AddRange(this.InnerClasses);
copy.Methods.AddRange(this.Methods);
copy.Modifiers = this.Modifiers;
copy.Properties.AddRange(this.Properties);
copy.Region = this.Region;
copy.TypeParameters.AddRange(this.TypeParameters);
copy.UserData = this.UserData;
return copy;
}
*/
byte flags;
const byte hasPublicOrInternalStaticMembersFlag = 0x02;
const byte hasExtensionMethodsFlag = 0x04;
internal byte Flags {
get {
if (flags == 0) {
flags = 1;
foreach (IMember m in this.Fields) {
if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
flags |= hasPublicOrInternalStaticMembersFlag;
}
}
foreach (IProperty m in this.Properties) {
if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
flags |= hasPublicOrInternalStaticMembersFlag;
}
if (m.IsExtensionMethod) {
flags |= hasExtensionMethodsFlag;
}
}
foreach (IMethod m in this.Methods) {
if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
flags |= hasPublicOrInternalStaticMembersFlag;
}
if (m.IsExtensionMethod) {
flags |= hasExtensionMethodsFlag;
}
}
foreach (IMember m in this.Events) {
if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
flags |= hasPublicOrInternalStaticMembersFlag;
}
}
foreach (IClass c in this.InnerClasses) {
if (c.IsPublic || c.IsInternal) {
flags |= hasPublicOrInternalStaticMembersFlag;
}
}
}
return flags;
}
set {
CheckBeforeMutation();
flags = value;
}
}
public bool HasPublicOrInternalStaticMembers {
get {
return (Flags & hasPublicOrInternalStaticMembersFlag) == hasPublicOrInternalStaticMembersFlag;
}
}
public bool HasExtensionMethods {
get {
return (Flags & hasExtensionMethodsFlag) == hasExtensionMethodsFlag;
}
}
/// <summary>
/// Gets the using scope of contains this class.
/// </summary>
public IUsingScope UsingScope {
get { return usingScope; }
set {
if (value == null)
throw new ArgumentNullException("UsingScope");
CheckBeforeMutation();
usingScope = value;
}
}
public DefaultClass(ICompilationUnit compilationUnit, string fullyQualifiedName) : base(null)
{
if (compilationUnit == null)
throw new ArgumentNullException("compilationUnit");
if (fullyQualifiedName == null)
throw new ArgumentNullException("fullyQualifiedName");
this.compilationUnit = compilationUnit;
this.FullyQualifiedName = fullyQualifiedName;
this.UsingScope = compilationUnit.UsingScope;
}
public DefaultClass(ICompilationUnit compilationUnit, IClass declaringType) : base(declaringType)
{
if (compilationUnit == null)
throw new ArgumentNullException("compilationUnit");
this.compilationUnit = compilationUnit;
this.UsingScope = compilationUnit.UsingScope;
}
public DefaultClass(ICompilationUnit compilationUnit, ClassType classType, ModifierEnum modifiers, DomRegion region, IClass declaringType) : base(declaringType)
{
if (compilationUnit == null)
throw new ArgumentNullException("compilationUnit");
this.compilationUnit = compilationUnit;
this.region = region;
this.classType = classType;
Modifiers = modifiers;
this.UsingScope = compilationUnit.UsingScope;
}
// fields must be volatile to ensure that the optimizer doesn't reorder accesses to it
// or causes DefaultReturnType to return null when the local copy of this.defaultReturnType is
// optimized away.
volatile IReturnType defaultReturnType;
bool hasCompoundClass;
public IReturnType DefaultReturnType {
get {
IReturnType defaultReturnType = this.defaultReturnType;
if (defaultReturnType == null) {
lock (this) {
this.defaultReturnType = defaultReturnType = CreateDefaultReturnType();
}
}
return defaultReturnType;
}
}
protected virtual IReturnType CreateDefaultReturnType()
{
if (hasCompoundClass) {
return new GetClassReturnType(ProjectContent, FullyQualifiedName, TypeParameters.Count);
} else {
return new DefaultReturnType(this);
}
}
bool IClass.HasCompoundClass {
get { return hasCompoundClass; }
set {
if (hasCompoundClass != value) {
lock (this) {
hasCompoundClass = value;
defaultReturnType = null;
}
}
}
}
public bool IsPartial {
get {
return (this.Modifiers & ModifierEnum.Partial) == ModifierEnum.Partial;
}
set {
CheckBeforeMutation();
if (value)
this.Modifiers |= ModifierEnum.Partial;
else
this.Modifiers &= ~ModifierEnum.Partial;
}
}
public IClass GetCompoundClass()
{
return this.DefaultReturnType.GetUnderlyingClass() ?? this;
}
protected override void OnFullyQualifiedNameChanged(EventArgs e)
{
base.OnFullyQualifiedNameChanged(e);
defaultReturnType = null; // re-create default return type
}
public ICompilationUnit CompilationUnit {
[System.Diagnostics.DebuggerStepThrough]
get {
return compilationUnit;
}
}
public IProjectContent ProjectContent {
[System.Diagnostics.DebuggerStepThrough]
get {
return CompilationUnit.ProjectContent;
}
}
public ClassType ClassType {
get {
return classType;
}
set {
CheckBeforeMutation();
classType = value;
}
}
public DomRegion Region {
get {
return region;
}
set {
CheckBeforeMutation();
region = value;
}
}
public override string DotNetName {
get {
string fullName;
if (this.DeclaringType != null) {
fullName = this.DeclaringType.DotNetName + "+" + this.Name;
} else {
fullName = this.FullyQualifiedName;
}
IList<ITypeParameter> typeParameters = this.TypeParameters;
if (typeParameters == null || typeParameters.Count == 0) {
return fullName;
} else {
return fullName + "`" + typeParameters.Count;
}
}
}
public override string DocumentationTag {
get {
return "T:" + DotNetName;
}
}
public IList<IReturnType> BaseTypes {
get {
if (baseTypes == null) {
baseTypes = new List<IReturnType>();
}
return baseTypes;
}
}
public virtual IList<IClass> InnerClasses {
get {
if (innerClasses == null) {
innerClasses = new List<IClass>();
}
return innerClasses;
}
}
public virtual IList<IField> Fields {
get {
if (fields == null) {
fields = new List<IField>();
}
return fields;
}
}
public virtual IList<IProperty> Properties {
get {
if (properties == null) {
properties = new List<IProperty>();
}
return properties;
}
}
public virtual IList<IMethod> Methods {
get {
if (methods == null) {
methods = new List<IMethod>();
}
return methods;
}
}
public virtual IList<IEvent> Events {
get {
if (events == null) {
events = new List<IEvent>();
}
return events;
}
}
public virtual IList<ITypeParameter> TypeParameters {
get {
if (typeParameters == null) {
typeParameters = new List<ITypeParameter>();
}
return typeParameters;
}
set {
CheckBeforeMutation();
typeParameters = value;
}
}
public virtual int CompareTo(IClass value)
{
int cmp;
if(0 != (cmp = base.CompareTo((IEntity)value))) {
return cmp;
}
if (FullyQualifiedName != null) {
cmp = FullyQualifiedName.CompareTo(value.FullyQualifiedName);
if (cmp != 0) {
return cmp;
}
return this.TypeParameters.Count - value.TypeParameters.Count;
}
return -1;
}
int IComparable.CompareTo(object o)
{
return CompareTo((IClass)o);
}
volatile List<IClass> inheritanceTreeCache;
public IEnumerable<IClass> ClassInheritanceTree {
get {
List<IClass> visitedList = inheritanceTreeCache;
if (visitedList != null)
return visitedList;
visitedList = new List<IClass>();
Queue<IReturnType> typesToVisit = new Queue<IReturnType>();
bool enqueuedLastBaseType = false;
bool hasErrors = false;
IClass currentClass = this;
IReturnType nextType;
do {
if (currentClass != null) {
if (!visitedList.Contains(currentClass)) {
visitedList.Add(currentClass);
foreach (IReturnType type in currentClass.BaseTypes) {
typesToVisit.Enqueue(type);
}
}
}
if (typesToVisit.Count > 0) {
nextType = typesToVisit.Dequeue();
} else {
nextType = enqueuedLastBaseType ? null : GetBaseTypeByClassType(this);
enqueuedLastBaseType = true;
}
if (nextType != null) {
currentClass = nextType.GetUnderlyingClass();
if (currentClass == null)
hasErrors = true;
}
} while (nextType != null);
// A SearchType request causes the inheritance tree to be generated, but if it was
// this classes' base type that caused the SearchType request, the GetUnderlyingClass()
// will fail and we will produce an incomplete inheritance tree.
// So we don't cache incomplete inheritance trees for parsed classes (fixes SD2-1474).
if (!hasErrors || KeepInheritanceTree) {
inheritanceTreeCache = visitedList;
if (!KeepInheritanceTree)
DomCache.RegisterForClear(delegate { inheritanceTreeCache = null; });
}
return visitedList;
}
}
/// <summary>
/// Specifies whether to keep the inheritance tree when the DomCache is cleared.
/// </summary>
protected bool KeepInheritanceTree = false;
public IReturnType GetBaseType(int index)
{
return BaseTypes[index];
}
IReturnType cachedBaseType;
public IReturnType BaseType {
get {
if (cachedBaseType == null) {
foreach (IReturnType baseType in this.BaseTypes) {
IClass baseClass = baseType.GetUnderlyingClass();
if (baseClass != null && baseClass.ClassType == this.ClassType) {
cachedBaseType = baseType;
break;
}
}
}
if (cachedBaseType == null) {
return GetBaseTypeByClassType(this);
} else {
return cachedBaseType;
}
}
}
internal static IReturnType GetBaseTypeByClassType(IClass c)
{
switch (c.ClassType) {
case ClassType.Class:
case ClassType.Interface:
if (c.FullyQualifiedName != "System.Object") {
return c.ProjectContent.SystemTypes.Object;
}
break;
case ClassType.Enum:
return c.ProjectContent.SystemTypes.Enum;
case ClassType.Delegate:
return c.ProjectContent.SystemTypes.Delegate;
case ClassType.Struct:
return c.ProjectContent.SystemTypes.ValueType;
}
return null;
}
public IClass BaseClass {
get {
foreach (IReturnType baseType in this.BaseTypes) {
IClass baseClass = baseType.GetUnderlyingClass();
if (baseClass != null && baseClass.ClassType == this.ClassType)
return baseClass;
}
IReturnType defaultBaseType = GetBaseTypeByClassType(this);
if (defaultBaseType != null)
return defaultBaseType.GetUnderlyingClass();
else
return null;
}
}
public bool IsTypeInInheritanceTree(IClass possibleBaseClass)
{
if (possibleBaseClass == null) {
return false;
}
foreach (IClass baseClass in this.ClassInheritanceTree) {
if (possibleBaseClass.FullyQualifiedName == baseClass.FullyQualifiedName)
return true;
}
return false;
}
/// <summary>
/// Searches the member with the specified name. Returns the first member/overload found.
/// </summary>
public IMember SearchMember(string memberName, LanguageProperties language)
{
if (memberName == null || memberName.Length == 0) {
return null;
}
StringComparer cmp = language.NameComparer;
foreach (IProperty p in Properties) {
if (cmp.Equals(p.Name, memberName)) {
return p;
}
}
foreach (IEvent e in Events) {
if (cmp.Equals(e.Name, memberName)) {
return e;
}
}
foreach (IField f in Fields) {
if (cmp.Equals(f.Name, memberName)) {
return f;
}
}
foreach (IMethod m in Methods) {
if (cmp.Equals(m.Name, memberName)) {
return m;
}
}
return null;
}
public IClass GetInnermostClass(int caretLine, int caretColumn)
{
foreach (IClass c in InnerClasses) {
if (c != null && IsInside(c, caretLine, caretColumn)) {
return c.GetInnermostClass(caretLine, caretColumn);
}
}
return this;
}
internal static bool IsInside(IClass c, int caretLine, int caretColumn)
{
return c.Region.IsInside(caretLine, caretColumn)
|| c.Attributes.Any((IAttribute a) => a.Region.IsInside(caretLine, caretColumn));
}
public List<IClass> GetAccessibleTypes(IClass callingClass)
{
List<IClass> types = new List<IClass>();
List<IClass> visitedTypes = new List<IClass>();
IClass currentClass = this;
do {
if (visitedTypes.Contains(currentClass))
break;
visitedTypes.Add(currentClass);
bool isClassInInheritanceTree = callingClass != null ? callingClass.IsTypeInInheritanceTree(currentClass) : false;
foreach (IClass c in currentClass.InnerClasses) {
if (c.IsAccessible(callingClass, isClassInInheritanceTree)) {
types.Add(c);
}
}
currentClass = currentClass.BaseClass;
} while (currentClass != null);
return types;
}
}
}