Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory-5.5.0/Documentation/IdStringProvider.cs @ 14815

Last change on this file since 14815 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 13.8 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Linq;
21using System.Collections.Generic;
22using System.Text;
23using ICSharpCode.NRefactory.TypeSystem;
24using ICSharpCode.NRefactory.TypeSystem.Implementation;
25
26namespace ICSharpCode.NRefactory.Documentation
27{
28  /// <summary>
29  /// Provides ID strings for entities. (C# 4.0 spec, §A.3.1)
30  /// ID strings are used to identify members in XML documentation files.
31  /// </summary>
32  public static class IdStringProvider
33  {
34    #region GetIdString
35    /// <summary>
36    /// Gets the ID string (C# 4.0 spec, §A.3.1) for the specified entity.
37    /// </summary>
38    public static string GetIdString(this IEntity entity)
39    {
40      StringBuilder b = new StringBuilder();
41      switch (entity.SymbolKind) {
42        case SymbolKind.TypeDefinition:
43          b.Append("T:");
44          AppendTypeName(b, (ITypeDefinition)entity, false);
45          return b.ToString();
46        case SymbolKind.Field:
47          b.Append("F:");
48          break;
49        case SymbolKind.Property:
50        case SymbolKind.Indexer:
51          b.Append("P:");
52          break;
53        case SymbolKind.Event:
54          b.Append("E:");
55          break;
56        default:
57          b.Append("M:");
58          break;
59      }
60      IMember member = (IMember)entity;
61      AppendTypeName(b, member.DeclaringType, false);
62      b.Append('.');
63      if (member.IsExplicitInterfaceImplementation && member.Name.IndexOf('.') < 0 && member.ImplementedInterfaceMembers.Count == 1) {
64        AppendTypeName(b, member.ImplementedInterfaceMembers[0].DeclaringType, true);
65        b.Append('#');
66      }
67      b.Append(member.Name.Replace('.', '#'));
68      IMethod method = member as IMethod;
69      if (method != null && method.TypeParameters.Count > 0) {
70        b.Append("``");
71        b.Append(method.TypeParameters.Count);
72      }
73      IParameterizedMember parameterizedMember = member as IParameterizedMember;
74      if (parameterizedMember != null && parameterizedMember.Parameters.Count > 0) {
75        b.Append('(');
76        var parameters = parameterizedMember.Parameters;
77        for (int i = 0; i < parameters.Count; i++) {
78          if (i > 0) b.Append(',');
79          AppendTypeName(b, parameters[i].Type, false);
80        }
81        b.Append(')');
82      }
83      if (member.SymbolKind == SymbolKind.Operator && (member.Name == "op_Implicit" || member.Name == "op_Explicit")) {
84        b.Append('~');
85        AppendTypeName(b, member.ReturnType, false);
86      }
87      return b.ToString();
88    }
89    #endregion
90   
91    #region GetTypeName
92    public static string GetTypeName(IType type)
93    {
94      if (type == null)
95        throw new ArgumentNullException("type");
96      StringBuilder b = new StringBuilder();
97      AppendTypeName(b, type, false);
98      return b.ToString();
99    }
100   
101    static void AppendTypeName(StringBuilder b, IType type, bool explicitInterfaceImpl)
102    {
103      switch (type.Kind) {
104        case TypeKind.Dynamic:
105          b.Append(explicitInterfaceImpl ? "System#Object" : "System.Object");
106          break;
107        case TypeKind.TypeParameter:
108          ITypeParameter tp = (ITypeParameter)type;
109          if (explicitInterfaceImpl) {
110            b.Append(tp.Name);
111          } else {
112            b.Append('`');
113            if (tp.OwnerType == SymbolKind.Method)
114              b.Append('`');
115            b.Append(tp.Index);
116          }
117          break;
118        case TypeKind.Array:
119          ArrayType array = (ArrayType)type;
120          AppendTypeName(b, array.ElementType, explicitInterfaceImpl);
121          b.Append('[');
122          if (array.Dimensions > 1) {
123            for (int i = 0; i < array.Dimensions; i++) {
124              if (i > 0)
125                b.Append(explicitInterfaceImpl ? '@' : ',');
126              if (!explicitInterfaceImpl)
127                b.Append("0:");
128            }
129          }
130          b.Append(']');
131          break;
132        case TypeKind.Pointer:
133          AppendTypeName(b, ((PointerType)type).ElementType, explicitInterfaceImpl);
134          b.Append('*');
135          break;
136        case TypeKind.ByReference:
137          AppendTypeName(b, ((ByReferenceType)type).ElementType, explicitInterfaceImpl);
138          b.Append('@');
139          break;
140        default:
141          IType declType = type.DeclaringType;
142          if (declType != null) {
143            AppendTypeName(b, declType, explicitInterfaceImpl);
144            b.Append(explicitInterfaceImpl ? '#' : '.');
145            b.Append(type.Name);
146            AppendTypeParameters(b, type, declType.TypeParameterCount, explicitInterfaceImpl);
147          } else {
148            if (explicitInterfaceImpl)
149              b.Append(type.FullName.Replace('.', '#'));
150            else
151              b.Append(type.FullName);
152            AppendTypeParameters(b, type, 0, explicitInterfaceImpl);
153          }
154          break;
155      }
156    }
157   
158    static void AppendTypeParameters(StringBuilder b, IType type, int outerTypeParameterCount, bool explicitInterfaceImpl)
159    {
160      int tpc = type.TypeParameterCount - outerTypeParameterCount;
161      if (tpc > 0) {
162        ParameterizedType pt = type as ParameterizedType;
163        if (pt != null) {
164          b.Append('{');
165          var ta = pt.TypeArguments;
166          for (int i = outerTypeParameterCount; i < ta.Count; i++) {
167            if (i > outerTypeParameterCount)
168              b.Append(explicitInterfaceImpl ? '@' : ',');
169            AppendTypeName(b, ta[i], explicitInterfaceImpl);
170          }
171          b.Append('}');
172        } else {
173          b.Append('`');
174          b.Append(tpc);
175        }
176      }
177    }
178    #endregion
179   
180    #region ParseMemberName
181    /// <summary>
182    /// Parse the ID string into a member reference.
183    /// </summary>
184    /// <param name="memberIdString">The ID string representing the member (with "M:", "F:", "P:" or "E:" prefix).</param>
185    /// <returns>A member reference that represents the ID string.</returns>
186    /// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
187    /// <remarks>
188    /// The member reference will look in <see cref="ITypeResolveContext.CurrentAssembly"/> first,
189    /// and if the member is not found there,
190    /// it will look in all other assemblies of the compilation.
191    /// </remarks>
192    public static IMemberReference ParseMemberIdString(string memberIdString)
193    {
194      if (memberIdString == null)
195        throw new ArgumentNullException("memberIdString");
196      if (memberIdString.Length < 2 || memberIdString[1] != ':')
197        throw new ReflectionNameParseException(0, "Missing type tag");
198      char typeChar = memberIdString[0];
199      int parenPos = memberIdString.IndexOf('(');
200      if (parenPos < 0)
201        parenPos = memberIdString.LastIndexOf('~');
202      if (parenPos < 0)
203        parenPos = memberIdString.Length;
204      int dotPos = memberIdString.LastIndexOf('.', parenPos - 1);
205      if (dotPos < 0)
206        throw new ReflectionNameParseException(0, "Could not find '.' separating type name from member name");
207      string typeName = memberIdString.Substring(0, dotPos);
208      int pos = 2;
209      ITypeReference typeReference = ParseTypeName(typeName, ref pos);
210      if (pos != typeName.Length)
211        throw new ReflectionNameParseException(pos, "Expected end of type name");
212//      string memberName = memberIDString.Substring(dotPos + 1, parenPos - (dotPos + 1));
213//      pos = memberName.LastIndexOf("``");
214//      if (pos > 0)
215//        memberName = memberName.Substring(0, pos);
216//      memberName = memberName.Replace('#', '.');
217      return new IdStringMemberReference(typeReference, typeChar, memberIdString);
218    }
219    #endregion
220   
221    #region ParseTypeName
222    /// <summary>
223    /// Parse the ID string type name into a type reference.
224    /// </summary>
225    /// <param name="typeName">The ID string representing the type (the "T:" prefix is optional).</param>
226    /// <returns>A type reference that represents the ID string.</returns>
227    /// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
228    /// <remarks>
229    /// <para>
230    /// The type reference will look in <see cref="ITypeResolveContext.CurrentAssembly"/> first,
231    /// and if the type is not found there,
232    /// it will look in all other assemblies of the compilation.
233    /// </para>
234    /// <para>
235    /// If the type is open (contains type parameters '`0' or '``0'),
236    /// an <see cref="ITypeResolveContext"/> with the appropriate CurrentTypeDefinition/CurrentMember is required
237    /// to resolve the reference to the ITypeParameter.
238    /// </para>
239    /// </remarks>
240    public static ITypeReference ParseTypeName(string typeName)
241    {
242      if (typeName == null)
243        throw new ArgumentNullException("typeName");
244      int pos = 0;
245      if (typeName.StartsWith("T:", StringComparison.Ordinal))
246        pos = 2;
247      ITypeReference r = ParseTypeName(typeName, ref pos);
248      if (pos < typeName.Length)
249        throw new ReflectionNameParseException(pos, "Expected end of type name");
250      return r;
251    }
252   
253    static bool IsIDStringSpecialCharacter(char c)
254    {
255      switch (c) {
256        case ':':
257        case '{':
258        case '}':
259        case '[':
260        case ']':
261        case '(':
262        case ')':
263        case '`':
264        case '*':
265        case '@':
266        case ',':
267          return true;
268        default:
269          return false;
270      }
271    }
272   
273    static ITypeReference ParseTypeName(string typeName, ref int pos)
274    {
275      string reflectionTypeName = typeName;
276      if (pos == typeName.Length)
277        throw new ReflectionNameParseException(pos, "Unexpected end");
278      ITypeReference result;
279      if (reflectionTypeName[pos] == '`') {
280        // type parameter reference
281        pos++;
282        if (pos == reflectionTypeName.Length)
283          throw new ReflectionNameParseException(pos, "Unexpected end");
284        if (reflectionTypeName[pos] == '`') {
285          // method type parameter reference
286          pos++;
287          int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos);
288          result = TypeParameterReference.Create(SymbolKind.Method, index);
289        } else {
290          // class type parameter reference
291          int index = ReflectionHelper.ReadTypeParameterCount(reflectionTypeName, ref pos);
292          result = TypeParameterReference.Create(SymbolKind.TypeDefinition, index);
293        }
294      } else {
295        // not a type parameter reference: read the actual type name
296        List<ITypeReference> typeArguments = new List<ITypeReference>();
297        int typeParameterCount;
298        string typeNameWithoutSuffix = ReadTypeName(typeName, ref pos, true, out typeParameterCount, typeArguments);
299        result = new GetPotentiallyNestedClassTypeReference(typeNameWithoutSuffix, typeParameterCount);
300        while (pos < typeName.Length && typeName[pos] == '.') {
301          pos++;
302          string nestedTypeName = ReadTypeName(typeName, ref pos, false, out typeParameterCount, typeArguments);
303          result = new NestedTypeReference(result, nestedTypeName, typeParameterCount);
304        }
305        if (typeArguments.Count > 0) {
306          result = new ParameterizedTypeReference(result, typeArguments);
307        }
308      }
309      while (pos < typeName.Length) {
310        switch (typeName[pos]) {
311          case '[':
312            int dimensions = 1;
313            do {
314              pos++;
315              if (pos == typeName.Length)
316                throw new ReflectionNameParseException(pos, "Unexpected end");
317              if (typeName[pos] == ',')
318                dimensions++;
319            } while (typeName[pos] != ']');
320            result = new ArrayTypeReference(result, dimensions);
321            break;
322          case '*':
323            result = new PointerTypeReference(result);
324            break;
325          case '@':
326            result = new ByReferenceTypeReference(result);
327            break;
328          default:
329            return result;
330        }
331        pos++;
332      }
333      return result;
334    }
335   
336    static string ReadTypeName(string typeName, ref int pos, bool allowDottedName, out int typeParameterCount, List<ITypeReference> typeArguments)
337    {
338      int startPos = pos;
339      // skip the simple name portion:
340      while (pos < typeName.Length && !IsIDStringSpecialCharacter(typeName[pos]) && (allowDottedName || typeName[pos] != '.'))
341        pos++;
342      if (pos == startPos)
343        throw new ReflectionNameParseException(pos, "Expected type name");
344      string shortTypeName = typeName.Substring(startPos, pos - startPos);
345      // read type arguments:
346      typeParameterCount = 0;
347      if (pos < typeName.Length && typeName[pos] == '`') {
348        // unbound generic type
349        pos++;
350        typeParameterCount = ReflectionHelper.ReadTypeParameterCount(typeName, ref pos);
351      } else if (pos < typeName.Length && typeName[pos] == '{') {
352        // bound generic type
353        typeArguments = new List<ITypeReference>();
354        do {
355          pos++;
356          typeArguments.Add(ParseTypeName(typeName, ref pos));
357          typeParameterCount++;
358          if (pos == typeName.Length)
359            throw new ReflectionNameParseException(pos, "Unexpected end");
360        } while (typeName[pos] == ',');
361        if (typeName[pos] != '}')
362          throw new ReflectionNameParseException(pos, "Expected '}'");
363        pos++;
364      }
365      return shortTypeName;
366    }
367    #endregion
368   
369    #region FindEntity
370    /// <summary>
371    /// Finds the entity in the given type resolve context.
372    /// </summary>
373    /// <param name="idString">ID string of the entity.</param>
374    /// <param name="context">Type resolve context</param>
375    /// <returns>Returns the entity, or null if it is not found.</returns>
376    /// <exception cref="ReflectionNameParseException">The syntax of the ID string is invalid</exception>
377    public static IEntity FindEntity(string idString, ITypeResolveContext context)
378    {
379      if (idString == null)
380        throw new ArgumentNullException("idString");
381      if (context == null)
382        throw new ArgumentNullException("context");
383      if (idString.StartsWith("T:", StringComparison.Ordinal)) {
384        return ParseTypeName(idString.Substring(2)).Resolve(context).GetDefinition();
385      } else {
386        return ParseMemberIdString(idString).Resolve(context);
387      }
388    }
389    #endregion
390  }
391}
Note: See TracBrowser for help on using the repository browser.