Initial import of the Generic MCS tree.
[mono.git] / mcs / gmcs / namespace.cs
1 //
2 // namespace.cs: Tracks namespaces
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9 using System;
10 using System.Collections;
11
12 namespace Mono.CSharp {
13
14         /// <summary>
15         ///   Keeps track of the namespaces defined in the C# code.
16         /// </summary>
17         public class Namespace {
18                 static ArrayList all_namespaces = new ArrayList ();
19                 
20                 Namespace parent;
21                 string name, fullname;
22                 ArrayList entries;
23                 Hashtable namespaces;
24
25                 /// <summary>
26                 ///   Constructor Takes the current namespace and the
27                 ///   name.  This is bootstrapped with parent == null
28                 ///   and name = ""
29                 /// </summary>
30                 public Namespace (Namespace parent, string name)
31                 {
32                         this.name = name;
33                         this.parent = parent;
34
35                         string pname = parent != null ? parent.Name : "";
36                                 
37                         if (pname == "")
38                                 fullname = name;
39                         else
40                                 fullname = parent.Name + "." + name;
41
42                         entries = new ArrayList ();
43                         namespaces = new Hashtable ();
44
45                         all_namespaces.Add (this);
46                 }
47
48                 public static Namespace Root = new Namespace (null, "");
49
50                 public Namespace GetNamespace (string name, bool create)
51                 {
52                         int pos = name.IndexOf ('.');
53
54                         Namespace ns;
55                         string first;
56                         if (pos >= 0)
57                                 first = name.Substring (0, pos);
58                         else
59                                 first = name;
60
61                         ns = (Namespace) namespaces [first];
62                         if (ns == null) {
63                                 if (!create)
64                                         return null;
65
66                                 ns = new Namespace (this, first);
67                                 namespaces.Add (first, ns);
68                         }
69
70                         if (pos >= 0)
71                                 ns = ns.GetNamespace (name.Substring (pos + 1), create);
72
73                         return ns;
74                 }
75
76                 public static Namespace LookupNamespace (string name, bool create)
77                 {
78                         return Root.GetNamespace (name, create);
79                 }
80
81                 public void AddNamespaceEntry (NamespaceEntry entry)
82                 {
83                         entries.Add (entry);
84                 }
85
86                 static public ArrayList UserDefinedNamespaces {
87                         get {
88                                 return all_namespaces;
89                         }
90                 }
91
92                 /// <summary>
93                 ///   The qualified name of the current namespace
94                 /// </summary>
95                 public string Name {
96                         get {
97                                 return fullname;
98                         }
99                 }
100
101                 /// <summary>
102                 ///   The parent of this namespace, used by the parser to "Pop"
103                 ///   the current namespace declaration
104                 /// </summary>
105                 public Namespace Parent {
106                         get {
107                                 return parent;
108                         }
109                 }
110
111                 public static void DefineNamespaces (SymbolWriter symwriter)
112                 {
113                         foreach (Namespace ns in all_namespaces) {
114                                 foreach (NamespaceEntry entry in ns.entries)
115                                         entry.DefineNamespace (symwriter);
116                         }
117                 }
118
119                 /// <summary>
120                 ///   Used to validate that all the using clauses are correct
121                 ///   after we are finished parsing all the files.  
122                 /// </summary>
123                 public static void VerifyUsing ()
124                 {
125                         foreach (Namespace ns in all_namespaces) {
126                                 foreach (NamespaceEntry entry in ns.entries)
127                                         entry.VerifyUsing ();
128                         }
129                 }
130
131                 public override string ToString ()
132                 {
133                         if (this == Root)
134                                 return "Namespace (<root>)";
135                         else
136                                 return String.Format ("Namespace ({0})", Name);
137                 }
138         }
139
140         public class NamespaceEntry
141         {
142                 Namespace ns;
143                 NamespaceEntry parent, implicit_parent;
144                 SourceFile file;
145                 int symfile_id;
146                 Hashtable aliases;
147                 ArrayList using_clauses;
148                 public bool DeclarationFound = false;
149
150                 //
151                 // This class holds the location where a using definition is
152                 // done, and whether it has been used by the program or not.
153                 //
154                 // We use this to flag using clauses for namespaces that do not
155                 // exist.
156                 //
157                 public class UsingEntry {
158                         public readonly string Name;
159                         public readonly NamespaceEntry NamespaceEntry;
160                         public readonly Location Location;
161                         
162                         public UsingEntry (NamespaceEntry entry, string name, Location loc)
163                         {
164                                 Name = name;
165                                 NamespaceEntry = entry;
166                                 Location = loc;
167                         }
168
169                         Namespace resolved_ns;
170
171                         public Namespace Resolve ()
172                         {
173                                 if (resolved_ns != null)
174                                         return resolved_ns;
175
176                                 Namespace curr_ns = NamespaceEntry.NS;
177                                 while ((curr_ns != null) && (resolved_ns == null)) {
178                                         string full_name = DeclSpace.MakeFQN (curr_ns.Name, Name);
179                                         resolved_ns = curr_ns.GetNamespace (Name, false);
180
181                                         if (resolved_ns == null)
182                                                 curr_ns = curr_ns.Parent;
183                                 }
184
185                                 return resolved_ns;
186                         }
187                 }
188
189                 public class AliasEntry {
190                         public readonly string Name;
191                         public readonly string Alias;
192                         public readonly NamespaceEntry NamespaceEntry;
193                         public readonly Location Location;
194                         
195                         public AliasEntry (NamespaceEntry entry, string name, string alias, Location loc)
196                         {
197                                 Name = name;
198                                 Alias = alias;
199                                 NamespaceEntry = entry;
200                                 Location = loc;
201                         }
202
203                         object resolved;
204
205                         public object Resolve ()
206                         {
207                                 if (resolved != null)
208                                         return resolved;
209
210                                 int pos = Alias.IndexOf ('.');
211                                 if (pos >= 0) {
212                                         string first = Alias.Substring (0, pos);
213                                 }
214
215                                 NamespaceEntry curr_ns = NamespaceEntry;
216                                 while ((curr_ns != null) && (resolved == null)) {
217                                         string full_name = DeclSpace.MakeFQN (curr_ns.Name, Alias);
218                                         resolved = curr_ns.LookupName (Alias);
219
220                                         if (resolved == null)
221                                                 curr_ns = curr_ns.Parent;
222                                 }
223
224                                 return resolved;
225                         }
226                 }
227
228                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name)
229                         : this (parent, file, name, false)
230                 { }
231
232                 protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit)
233                 {
234                         this.parent = parent;
235                         this.file = file;
236                         this.IsImplicit = is_implicit;
237                         this.ID = ++next_id;
238
239                         if (!is_implicit && (parent != null))
240                                 ns = parent.NS.GetNamespace (name, true);
241                         else if (name != null)
242                                 ns = Namespace.LookupNamespace (name, true);
243                         else
244                                 ns = Namespace.Root;
245                         ns.AddNamespaceEntry (this);
246
247                         if ((parent != null) && (parent.NS != ns.Parent))
248                                 implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true);
249                         else
250                                 implicit_parent = parent;
251                 }
252
253                 static int next_id = 0;
254                 public readonly int ID;
255                 public readonly bool IsImplicit;
256
257                 public string Name {
258                         get {
259                                 return ns.Name;
260                         }
261                 }
262
263                 public Namespace NS {
264                         get {
265                                 return ns;
266                         }
267                 }
268
269                 public NamespaceEntry Parent {
270                         get {
271                                 return parent;
272                         }
273                 }
274
275                 public NamespaceEntry ImplicitParent {
276                         get {
277                                 return implicit_parent;
278                         }
279                 }
280
281                 /// <summary>
282                 ///   Records a new namespace for resolving name references
283                 /// </summary>
284                 public void Using (string ns, Location loc)
285                 {
286                         if (DeclarationFound){
287                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
288                                 return;
289                         }
290
291                         if (ns == Name)
292                                 return;
293                         
294                         if (using_clauses == null)
295                                 using_clauses = new ArrayList ();
296
297                         foreach (UsingEntry old_entry in using_clauses){
298                                 if (old_entry.Name == ns){
299                                         Report.Warning (105, loc, "The using directive for '" + ns +
300                                                         "' appeared previously in this namespace");
301                                         return;
302                                 }
303                         }
304                         
305                         UsingEntry ue = new UsingEntry (this, ns, loc);
306                         using_clauses.Add (ue);
307                 }
308
309                 public void UsingAlias (string alias, string namespace_or_type, Location loc)
310                 {
311                         if (aliases == null)
312                                 aliases = new Hashtable ();
313                         
314                         if (aliases.Contains (alias)){
315                                 Report.Error (1537, loc, "The using alias `" + alias +
316                                               "' appeared previously in this namespace");
317                                 return;
318                         }
319
320                         aliases [alias] = new AliasEntry (this, alias, namespace_or_type, loc);
321                 }
322
323                 protected AliasEntry GetAliasEntry (string alias)
324                 {
325                         AliasEntry entry = null;
326
327                         if (aliases != null)
328                                 entry = (AliasEntry) aliases [alias];
329                         if (entry == null && Parent != null)
330                                 entry = Parent.GetAliasEntry (alias);
331
332                         return entry;
333                 }
334
335                 public string LookupAlias (string alias)
336                 {
337                         AliasEntry entry = GetAliasEntry (alias);
338
339                         if (entry == null)
340                                 return null;
341
342                         object resolved = entry.Resolve ();
343                         if (resolved == null)
344                                 return null;
345                         else if (resolved is Namespace)
346                                 return ((Namespace) resolved).Name;
347                         else
348                                 return ((Type) resolved).FullName;
349                 }
350
351                 public object LookupName (string name)
352                 {
353                         Namespace ns = Namespace.LookupNamespace (name, false);
354                         if (ns != null)
355                                 return ns;
356
357                         int pos = name.IndexOf ('.');
358                         AliasEntry alias = null;
359                         if (pos >= 0) {
360                                 string first = name.Substring (0, pos);
361                                 string last = name.Substring (pos + 1);
362
363                                 alias = GetAliasEntry (first);
364                                 if (alias != null)
365                                         return LookupName (DeclSpace.MakeFQN (alias.Alias, last));
366                         }
367
368                         Type t = TypeManager.LookupType (name);
369                         if (t != null)
370                                 return t;
371
372                         alias = GetAliasEntry (name);
373                         if (alias != null)
374                                 return LookupName (alias.Alias);
375
376                         return null;
377                 }
378                 
379                 public Namespace[] GetUsingTable ()
380                 {
381                         ArrayList list = new ArrayList ();
382
383                         if (using_clauses == null)
384                                 return new Namespace [0];
385
386                         foreach (UsingEntry ue in using_clauses) {
387                                 Namespace using_ns = ue.Resolve ();
388                                 if (using_ns == null)
389                                         continue;
390
391                                 list.Add (using_ns);
392                         }
393
394                         Namespace[] retval = new Namespace [list.Count];
395                         list.CopyTo (retval, 0);
396                         return retval;
397                 }
398
399                 public void DefineNamespace (SymbolWriter symwriter)
400                 {
401                         if (symfile_id != 0)
402                                 return;
403                         if (parent != null)
404                                 parent.DefineNamespace (symwriter);
405
406                         string[] using_list;
407                         if (using_clauses != null) {
408                                 using_list = new string [using_clauses.Count];
409                                 for (int i = 0; i < using_clauses.Count; i++)
410                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name;
411                         } else {
412                                 using_list = new string [0];
413                         }
414
415                         int parent_id = parent != null ? parent.symfile_id : 0;
416                         symfile_id = symwriter.DefineNamespace (ns.Name, file, using_list, parent_id);
417                 }
418
419                 public int SymbolFileID {
420                         get {
421                                 return symfile_id;
422                         }
423                 }
424
425                 static void Msgtry (string s)
426                 {
427                         Console.WriteLine ("    Try using -r:" + s);
428                 }
429
430                 protected void error246 (Location loc, string name)
431                 {
432                         if (TypeManager.LookupType (name) != null)
433                                 Report.Error (138, loc, "The using keyword only lets you specify a namespace, " +
434                                               "`" + name + "' is a class not a namespace.");
435                         else {
436                                 Report.Error (246, loc, "The namespace `" + name +
437                                               "' can not be found (missing assembly reference?)");
438
439                                 switch (name){
440                                 case "Gtk": case "GtkSharp":
441                                         Msgtry ("gtk-sharp");
442                                         break;
443
444                                 case "Gdk": case "GdkSharp":
445                                         Msgtry ("gdk-sharp");
446                                         break;
447
448                                 case "Glade": case "GladeSharp":
449                                         Msgtry ("glade-sharp");
450                                         break;
451                                                         
452                                 case "System.Drawing":
453                                         Msgtry ("System.Drawing");
454                                         break;
455                                                         
456                                 case "System.Web.Services":
457                                         Msgtry ("System.Web.Services");
458                                         break;
459
460                                 case "System.Web":
461                                         Msgtry ("System.Web");
462                                         break;
463                                                         
464                                 case "System.Data":
465                                         Msgtry ("System.Data");
466                                         break;
467
468                                 case "System.Windows.Forms":
469                                         Msgtry ("System.Windows.Forms");
470                                         break;
471                                 }
472                         }
473                 }
474
475                 /// <summary>
476                 ///   Used to validate that all the using clauses are correct
477                 ///   after we are finished parsing all the files.  
478                 /// </summary>
479                 public void VerifyUsing ()
480                 {
481                         if (using_clauses != null){
482                                 foreach (UsingEntry ue in using_clauses){
483                                         if (ue.Resolve () != null)
484                                                 continue;
485
486                                         error246 (ue.Location, ue.Name);
487                                 }
488                         }
489
490                         if (aliases != null){
491                                 foreach (DictionaryEntry de in aliases){
492                                         AliasEntry alias = (AliasEntry) de.Value;
493
494                                         if (alias.Resolve () != null)
495                                                 continue;
496
497                                         error246 (alias.Location, alias.Alias);
498                                 }
499                         }
500                 }
501
502                 public override string ToString ()
503                 {
504                         if (NS == Namespace.Root)
505                                 return "NamespaceEntry (<root>)";
506                         else
507                                 return String.Format ("NamespaceEntry ({0},{1},{2})", Name, IsImplicit, ID);
508                 }
509         }
510 }