PageRenderTime 56ms CodeModel.GetById 20ms app.highlight 27ms RepoModel.GetById 2ms app.codeStats 0ms

/NRefactory/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs

http://github.com/icsharpcode/ILSpy
C# | 391 lines | 307 code | 11 blank | 73 comment | 103 complexity | d85f764819e12d9fdc20635007a97a34 MD5 | raw file
  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}