2009-09-30 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System / System.CodeDom / CodeTypeReference.cs
1 //
2 // System.CodeDom CodeTypeReferenceExpression Class implementation
3 //
4 // Author:
5 //   Daniel Stodden (stodden@in.tum.de)
6 //   Marek Safar (marek.safar@seznam.cz)
7 //
8 // (C) 2001 Ximian, Inc.
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Runtime.InteropServices;
33 using System.Text;
34
35 namespace System.CodeDom
36 {
37         [Serializable]
38         [ClassInterface(ClassInterfaceType.AutoDispatch)]
39         [ComVisible(true)]
40         public class CodeTypeReference : CodeObject
41         {
42                 private string baseType;
43                 private CodeTypeReference arrayElementType;
44                 private int arrayRank;
45                 private bool isInterface;
46                 bool needsFixup;
47
48 #if NET_2_0
49                 CodeTypeReferenceCollection typeArguments;
50                 CodeTypeReferenceOptions referenceOptions;
51 #endif
52
53                 //
54                 // Constructors
55                 //
56
57 #if NET_2_0
58                 public CodeTypeReference ()
59                 {
60                 }
61 #endif
62
63 #if NET_2_0
64                 [MonoTODO("We should parse basetype from right to left in 2.0 profile.")]
65 #endif
66                 public CodeTypeReference (string baseType)
67                 {
68                         Parse (baseType);
69                 }
70
71 #if NET_2_0
72                 [MonoTODO("We should parse basetype from right to left in 2.0 profile.")]
73 #endif
74                 public CodeTypeReference (Type baseType)
75                 {
76 #if NET_2_0
77                         if (baseType == null) {
78                                 throw new ArgumentNullException ("baseType");
79                         }
80
81                         if (baseType.IsGenericParameter) {
82                                 this.baseType = baseType.Name;
83                                 this.referenceOptions = CodeTypeReferenceOptions.GenericTypeParameter;
84                         }
85                         else if (baseType.IsGenericTypeDefinition)
86                                 this.baseType = baseType.FullName;
87                         else if (baseType.IsGenericType) {
88                                 this.baseType = baseType.GetGenericTypeDefinition ().FullName;
89                                 foreach (Type arg in baseType.GetGenericArguments ()) {
90                                         if (arg.IsGenericParameter)
91                                                 TypeArguments.Add (new CodeTypeReference (new CodeTypeParameter (arg.Name)));
92                                         else
93                                                 TypeArguments.Add (new CodeTypeReference (arg));
94                                 }
95                         }
96                         else
97 #endif
98                         if (baseType.IsArray) {
99                                 this.arrayRank = baseType.GetArrayRank ();
100                                 this.arrayElementType = new CodeTypeReference (baseType.GetElementType ());
101                                 this.baseType = arrayElementType.BaseType;
102                         } else {
103                                 Parse (baseType.FullName);
104                         }
105                         this.isInterface = baseType.IsInterface;
106                 }
107
108                 public CodeTypeReference( CodeTypeReference arrayElementType, int arrayRank )
109                 {
110                         this.baseType = null;
111                         this.arrayRank = arrayRank;
112                         this.arrayElementType = arrayElementType;
113                 }
114
115 #if NET_2_0
116                 [MonoTODO("We should parse basetype from right to left in 2.0 profile.")]
117 #endif
118                 public CodeTypeReference( string baseType, int arrayRank )
119                         : this (new CodeTypeReference (baseType), arrayRank)
120                 {
121                 }
122
123 #if NET_2_0
124                 public CodeTypeReference( CodeTypeParameter typeParameter ) :
125                         this (typeParameter.Name)
126                 {
127                         this.referenceOptions = CodeTypeReferenceOptions.GenericTypeParameter;
128                 }
129
130                 public CodeTypeReference( string typeName, CodeTypeReferenceOptions referenceOptions ) :
131                         this (typeName)
132                 {
133                         this.referenceOptions = referenceOptions;
134                 }
135
136                 public CodeTypeReference( Type type, CodeTypeReferenceOptions referenceOptions ) :
137                         this (type)
138                 {
139                         this.referenceOptions = referenceOptions;
140                 }
141
142                 public CodeTypeReference( string typeName, params CodeTypeReference[] typeArguments ) :
143                         this (typeName)
144                 {
145                         TypeArguments.AddRange (typeArguments);
146                         if (this.baseType.IndexOf ('`') < 0)
147                                 this.baseType += "`" + TypeArguments.Count;
148                 }
149 #endif
150
151                 //
152                 // Properties
153                 //
154
155                 public CodeTypeReference ArrayElementType
156                 {
157                         get {
158                                 return arrayElementType;
159                         }
160                         set {
161                                 arrayElementType = value;
162                         }
163                 }
164                 
165                 public int ArrayRank {
166                         get {
167                                 return arrayRank;
168                         }
169                         set {
170                                 arrayRank = value;
171                         }
172                 }
173
174                 public string BaseType {
175                         get {
176                                 if (arrayElementType != null && arrayRank > 0) {
177                                         return arrayElementType.BaseType;
178                                 }
179
180                                 if (baseType == null)
181                                         return String.Empty;
182
183                                 return baseType;
184                         }
185                         set {
186                                 baseType = value;
187                         }
188                 }
189
190                 internal bool IsInterface {
191                         get { return isInterface; }
192                 }
193
194                 private void Parse (string baseType)
195                 {
196                         if (baseType == null || baseType.Length == 0) {
197                                 this.baseType = typeof (void).FullName;
198                                 return;
199                         }
200
201 #if NET_2_0
202                         int array_start = baseType.IndexOf ('[');
203                         if (array_start == -1) {
204                                 this.baseType = baseType;
205                                 return;
206                         }
207
208                         int array_end = baseType.LastIndexOf (']');
209                         if (array_end < array_start) {
210                                 this.baseType = baseType;
211                                 return;
212                         }
213
214                         int lastAngle = baseType.LastIndexOf ('>');
215                         if (lastAngle != -1 && lastAngle > array_end) {
216                                 this.baseType = baseType;
217                                 return;
218                         }
219                         
220                         string[] args = baseType.Substring (array_start + 1, array_end - array_start - 1).Split (',');
221
222                         if ((array_end - array_start) != args.Length) {
223                                 this.baseType = baseType.Substring (0, array_start);
224                                 int escapeCount = 0;
225                                 int scanPos = array_start;
226                                 StringBuilder tb = new StringBuilder();
227                                 while (scanPos < baseType.Length) {
228                                         char currentChar = baseType[scanPos];
229                                         
230                                         switch (currentChar) {
231                                                 case '[':
232                                                         if (escapeCount > 1 && tb.Length > 0) {
233                                                                 tb.Append (currentChar);
234                                                         }
235                                                         escapeCount++;
236                                                         break;
237                                                 case ']':
238                                                         escapeCount--;
239                                                         if (escapeCount > 1 && tb.Length > 0) {
240                                                                 tb.Append (currentChar);
241                                                         }
242
243                                                         if (tb.Length != 0 && (escapeCount % 2) == 0) {
244                                                                 TypeArguments.Add (tb.ToString ());
245                                                                 tb.Length = 0;
246                                                         }
247                                                         break;
248                                                 case ',':
249                                                         if (escapeCount > 1) {
250                                                                 // skip anything after the type name until we 
251                                                                 // reach the next separator
252                                                                 while (scanPos + 1 < baseType.Length) {
253                                                                         if (baseType[scanPos + 1] == ']') {
254                                                                                 break;
255                                                                         }
256                                                                         scanPos++;
257                                                                 }
258                                                         } else if (tb.Length > 0) {
259                                                                 CodeTypeReference typeArg = new CodeTypeReference (tb.ToString ());
260                                                                 TypeArguments.Add (typeArg);
261                                                                 tb.Length = 0;
262                                                         }
263                                                         break;
264                                                 default:
265                                                         tb.Append (currentChar);
266                                                         break;
267                                         }
268                                         scanPos++;
269                                 }
270                         } else {
271                                 arrayElementType = new CodeTypeReference (baseType.Substring (0, array_start));
272                                 arrayRank = args.Length;
273                         }
274 #else
275                         int array_start = baseType.LastIndexOf ('[');
276                         if (array_start == -1) {
277                                 this.baseType = baseType;
278                                 return;
279                         }
280
281                         int array_end = baseType.LastIndexOf (']');
282                         if (array_end < array_start) {
283                                 this.baseType = baseType;
284                                 return;
285                         }
286
287                         string[] args = baseType.Substring (array_start + 1, array_end - array_start - 1).Split (',');
288
289                         bool isArray = true;
290                         foreach (string arg in args) {
291                                 if (arg.Length != 0) {
292                                         isArray = false;
293                                         break;
294                                 }
295                         }
296                         if (isArray) {
297                                 arrayElementType = new CodeTypeReference (baseType.Substring (0, array_start));
298                                 arrayRank = args.Length;
299                         } else {
300                                 this.baseType = baseType;
301                         }
302 #endif
303                 }
304
305 #if NET_2_0
306                 [ComVisible (false)]
307                 public CodeTypeReferenceOptions Options {
308                         get {
309                                 return referenceOptions;
310                         }
311                         set {
312                                 referenceOptions = value;
313                         }
314                 }
315
316                 [ComVisible (false)]
317                 public CodeTypeReferenceCollection TypeArguments {
318                         get {
319                                 if (typeArguments == null)
320                                         typeArguments = new CodeTypeReferenceCollection ();
321                                 return typeArguments;
322                         }
323                 }
324 #endif
325
326         }
327 }