2002-02-20 Ravi Pratap <ravi@ximian.com>
[mono.git] / mcs / doctools / docstub.cs
1 // docstub.cs
2 //
3 // Adam Treat (manyoso@yahoo.com)
4 //
5 // (C) 2002 Adam Treat
6 //
7 // Licensed under the terms of the GNU GPL
8
9 using System;
10 using System.Reflection;
11 using System.Collections;
12 using System.IO;
13 using System.Xml;
14
15 namespace Mono.Util
16 {
17
18         class DocStub
19         {
20                 Type currentType;
21                 MemberInfo[] members;
22                 XmlTextWriter writer;
23                 //XmlDocument xmldoc;
24                 string output_file, assembly_file, directory;
25
26                 void Usage()
27                 {
28                         Console.Write (
29                                 "docstub (-d <directory> || -o <file>) -a <assembly>\n\n" +
30                                 "   -d || --dir <directory>                     The directory to write the xml files to.\n" +
31                                 "   -o || --output <file>                       Specifies that docstub should write to one large output file.\n" +
32                                 "   -a || --assembly <assembly>         Specifies the target assembly to load and parse.\n\n");
33                 }
34
35                 public static void Main(string[] args)
36                 {
37                         DocStub stub = new DocStub(args);
38                 }
39
40                 public DocStub(string [] args)
41                 {
42                         output_file = null;
43                         assembly_file = null;
44                         directory = null;
45                         int argc = args.Length;
46
47                         for(int i = 0; i < argc; i++)
48                         {
49                                 string arg = args[i];
50
51                                 // The "/" switch is there for wine users, like me ;-)
52                                 if(arg.StartsWith("-") || arg.StartsWith("/"))
53                                 {
54                                         switch(arg)
55                                         {
56
57                                         case "-d": case "/-d": case "--directory":
58                                                 if((i + 1) >= argc)
59                                                 {
60                                                         Usage();
61                                                         return;
62                                                 }
63                                                 directory = args[++i];
64                                                 continue;
65
66                                         case "-o": case "/-o": case "--output":
67                                                 if((i + 1) >= argc)
68                                                 {
69                                                         Usage();
70                                                         return;
71                                                 }
72                                                 output_file = args[++i];
73                                                 continue;
74
75                                         case "-a": case "/-a": case "--assembly":
76                                                 if((i + 1) >= argc)
77                                                 {
78                                                         Usage();
79                                                         return;
80                                                 }
81                                                 assembly_file = args[++i];
82                                                 continue;
83
84                                         default:
85                                                 Usage();
86                                                 return;
87                                         }
88                                 }
89                         }
90
91                         if(assembly_file == null)
92                         {
93                                 Usage();
94                                 return;
95                         } else if(directory == null && output_file == null)
96                         {
97                                 Usage();
98                                 return;
99                         }
100
101                         if(directory != null)
102                         {
103                                 // This specifies that writing to a directory is the default behavior
104                                 // If someone attempts to write to both a directory and an output file
105                                 // Only the directory will be written to...
106                                 output_file = null;
107                         }
108
109                         if (!Directory.Exists(directory) && directory != null)
110                         {
111                 Directory.CreateDirectory(directory);
112             }
113
114                         // Call the main driver to get some things done
115                         MainDriver();
116                 }
117
118                 //
119                 // This method loads the assembly and generates a types list
120                 // It also takes care of the end product, ie writing the xml
121                 // to the given filename, or filenames...
122                 //
123                 void MainDriver()
124                 {
125                         Type[] assemblyTypes;
126                         Assembly assembly = null;
127                         ArrayList TypesList = new ArrayList();
128
129                         assembly = LoadAssembly(Path.GetFullPath(assembly_file));
130
131                         // GetTypes() doesn't seem to like loading some dll's, but then
132                         // the exception holds all the types in the dll anyway, some
133                         // are in Types and some are in the LoaderExceptions array.
134                         try
135                         {
136                                 assemblyTypes = assembly.GetTypes();
137                         }
138                         catch(ReflectionTypeLoadException e)
139                         {
140                                 assemblyTypes = e.Types;
141                                 foreach(TypeLoadException loadException in e.LoaderExceptions)
142                                 {
143                                         TypesList.Add(loadException.TypeName);
144                                 }
145                         }
146
147                         // Create an xml document to check the output [debugging purposes]
148                         //xmldoc = new XmlDocument();
149                         //xmldoc.PreserveWhitespace = true;
150
151
152                         // GenerateXML will take care of converting all the Types info
153                         // into XML and return a string for writing out to file, files
154
155                         if(output_file != null)
156                         {
157                                 writer = new XmlTextWriter (output_file, null);
158                                 writer.Formatting = Formatting.Indented;
159                                 writer.WriteStartDocument();
160                                 writer.WriteDocType("MonoDocStub", null, null, null);
161                                 writer.WriteStartElement("assembly");
162
163                                 foreach(Type name in assemblyTypes)
164                                 {
165                                         if (name != null)
166                                         {
167                                                 GenerateXML(name);
168                                         }
169                                 }
170
171                                 writer.WriteEndElement();
172                                 writer.WriteEndDocument();
173                                 writer.Flush();
174                                 writer.Close();
175                         // Load the file and check it is wellformed [debugging purposes]
176                         //xmldoc.Load(output_file);
177
178                         }
179                         else if (directory != null)
180                         {
181                                 foreach(Type name in assemblyTypes)
182                                 {
183                                         if (name != null)
184                                         {
185                                                 string filename = directory+"/"+name+".xml";
186                                         writer = new XmlTextWriter (filename, null);
187                                                 writer.Formatting = Formatting.Indented;
188                                                 writer.WriteStartDocument();
189                                                 writer.WriteDocType("MonoDocStub", null, null, null);
190                                                 writer.WriteStartElement("assembly");
191                                                 GenerateXML(name);
192                                                 writer.WriteEndElement();
193                                                 writer.WriteEndDocument();
194                                                 writer.Flush();
195                                                 writer.Close();
196                                                 // Load the file and check it is wellformed [debugging purposes]
197                                         //xmldoc.Load(filename);
198                                         }
199                                 }
200                         }
201                         return;
202 \r               }
203
204                 // This method loads an assembly into memory. If you
205                 // use Assembly.Load or Assembly.LoadFrom the assembly file locks.
206                 // This method doesn't lock the assembly file.
207                 Assembly LoadAssembly(string filename)
208                 {
209                         try
210                         {
211                                 FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read);
212                                 byte[] buffer = new byte[fs.Length];
213                                 fs.Read(buffer, 0, (int)fs.Length);
214                                 fs.Close();
215
216                                 return Assembly.Load(buffer);
217                         }
218                         catch(FileNotFoundException)
219                         {
220                                 Console.WriteLine("Could not find assembly file: {0}", assembly_file);
221                                 return null;
222                         }
223                 }
224
225                 //
226                 // The main xml generation method!
227                 //
228                 void GenerateXML(Type name)
229                 {
230                         try
231                         {
232                                 currentType = name;
233                                 //This is what the try block is for
234                                 members = currentType.GetMembers();
235                                 // This is where we generate xml for the class/type
236                                 writer.WriteStartElement("class");
237                                 writer.WriteStartElement("name");
238                                 writer.WriteString(currentType.FullName);
239                                 writer.WriteEndElement();
240                                 getTypeInfoXml();
241
242                                 // Generate xml for members (constructors, fields, methods etc)
243                                 if(members.Length > 0)
244                                 {
245                                         foreach(MemberInfo m in members)
246                                         {
247                                                 if (m is ConstructorInfo)
248                                                 {
249                                                         writer.WriteStartElement("member");
250                                                         writer.WriteStartElement("name");
251                                                 writer.WriteString(currentType.Name);
252                                                         writer.WriteEndElement();
253                                                         writer.WriteStartElement("type");
254                                                         writer.WriteString("constructor");
255                                                         writer.WriteEndElement();
256                                                         getConstructorXml((ConstructorInfo)m);
257                                                         writer.WriteEndElement();
258                                                 }
259                                                 else if (m is EventInfo)
260                                                 {
261                                                         writer.WriteStartElement("member");
262                                                         writer.WriteStartElement("name");
263                                                 writer.WriteString(m.Name);
264                                                         writer.WriteEndElement();
265                                                         writer.WriteStartElement("type");
266                                                 writer.WriteString("event");
267                                                         writer.WriteEndElement();
268                                                         getEventXml((EventInfo)m);
269                                                         writer.WriteEndElement();
270                                                 }
271                                                 else if (m is FieldInfo)
272                                                 {
273                                                         writer.WriteStartElement("member");
274                                                         writer.WriteStartElement("name");
275                                                 writer.WriteString(m.Name);
276                                                         writer.WriteEndElement();
277                                                         writer.WriteStartElement("type");
278                                                         writer.WriteString("field");
279                                                         writer.WriteEndElement();
280                                                         getFieldXml((FieldInfo)m);
281                                                         writer.WriteEndElement();
282                                                 }
283                                                 else if (m is PropertyInfo)
284                                                 {
285                                                         writer.WriteStartElement("member");
286                                                         writer.WriteStartElement("name");
287                                                 writer.WriteString(m.Name);
288                                                         writer.WriteEndElement();
289                                                         writer.WriteStartElement("type");
290                                                 writer.WriteString("property");
291                                                         writer.WriteEndElement();
292                                                         getPropertyXml((PropertyInfo)m);
293                                                         writer.WriteEndElement();
294                                                 }
295                                                 else if (m is MethodInfo)
296                                                 {
297                                                         writer.WriteStartElement("member");
298                                                         writer.WriteStartElement("name");
299                                                 writer.WriteString(m.Name);
300                                                         writer.WriteEndElement();
301                                                         writer.WriteStartElement("type");
302                                                 writer.WriteString("method");
303                                                         writer.WriteEndElement();
304                                                         getMethodXml((MethodInfo)m);
305                                                         writer.WriteEndElement();
306                                                 }
307                                                 else{}
308                                         }
309                                 }
310                                 // Don't forget to close the xml ;-)
311                                 writer.WriteEndElement();
312                         }
313                         catch(TypeLoadException e)
314                         {
315                                 // Todo Mono's corlib keeps failing here because System.Object
316                                 // Doesn't have any parents
317                                 Console.WriteLine("Class: "+e.TypeName+" has caused a TypeLoad Exception."
318                                                                  +" No XML will be generated for this type.");
319                         }
320                 }
321
322                 //
323                 // This just calls the methods for elements within a constructor
324                 //
325                 void getConstructorXml(ConstructorInfo construct)
326                 {
327                         getMethodBaseInfoXml(construct);
328                         getParameterInfoXml(construct);
329                         getInheritInfoXml(construct);
330                 }
331
332                 //
333                 // Calls the methods for elements within an event member
334                 //
335                 void getEventXml(EventInfo eve)
336                 {
337                         getEventInfoXml(eve);
338                         getInheritInfoXml(eve);
339                 }
340
341                 //
342                 // Calls the methods for xml elements within a field
343                 //
344                 void getFieldXml(FieldInfo field)
345                 {
346                         writer.WriteStartElement("field_type");
347                         writer.WriteString(field.FieldType.Name);
348                         writer.WriteEndElement();
349                         getFieldInfoXml(field);
350                         getInheritInfoXml(field);
351                 }
352
353                 //
354                 // Calls the methods for xml elements within a property
355                 //
356                 void getPropertyXml(PropertyInfo property)
357                 {
358                         writer.WriteStartElement("property_type");
359                         writer.WriteString(property.PropertyType.Name);
360                         writer.WriteEndElement();
361                         getPropertyInfoXml(property);
362                         getInheritInfoXml(property);
363                 }
364
365                 //
366                 // Calls the methods for xml elements within a method
367                 //
368                 void getMethodXml(MethodInfo method)
369                 {
370                         getMethodBaseInfoXml(method);
371                         getParameterInfoXml(method);
372                         getReturnInfoXml(method);
373                         getInheritInfoXml(method);
374                 }
375
376                 //
377                 // Probably should just go in the getMethodXml, but here for asthetic reasons ;-)
378                 //
379                 void getReturnInfoXml(MethodInfo method)
380                 {
381                         try
382                         {
383                                 writer.WriteStartElement("return");
384                                 writer.WriteString(method.ReturnType.Name);
385                                 writer.WriteEndElement();
386                         }
387                         catch(Exception)
388                         {
389                                 // Mysteriously this is also causing some corlib types
390                                 // to spit out some Object doesn't have a parent errors
391                                 //Console.WriteLine(e.Message);
392                                 writer.WriteEndElement();
393                         }
394                 }
395
396                 //
397                 // Checks to see if a member is inherited and output xml
398                 //
399                 void getInheritInfoXml(MemberInfo member)
400                 {
401                         if(member.DeclaringType != currentType)
402                         {
403                                 writer.WriteStartElement("inherit");
404                                 writer.WriteString(member.DeclaringType.Name);
405                                 writer.WriteEndElement();
406                                 writer.WriteStartElement("inheritfull");
407                                 writer.WriteString(member.DeclaringType.FullName);
408                                 writer.WriteEndElement();
409                         }
410                 }
411
412                 //
413                 // Checks for get or set in properties
414                 //
415                 void getPropertyInfoXml(PropertyInfo property)
416                 {
417                         if(property.CanRead)
418                         {
419                                 writer.WriteStartElement("attribute");
420                                 writer.WriteString("get");
421                                 writer.WriteEndElement();
422                         }
423                         if(property.CanWrite)
424                         {
425                                 writer.WriteStartElement("attribute");
426                                 writer.WriteString("set");
427                                 writer.WriteEndElement();
428                         }
429                 }
430
431                 //
432                 // Self explanatory
433                 //
434                 void getParameterInfoXml(MethodBase method)
435                 {
436                         try
437                         {
438                                 ParameterInfo[] parameters = method.GetParameters();
439                                 if(parameters.Length != 0)
440                                 {
441                                         foreach(ParameterInfo p in parameters)
442                                         {
443                                                 writer.WriteStartElement("param");
444                                                 writer.WriteStartElement("name");
445                                                 writer.WriteString(p.Name);
446                                                 writer.WriteEndElement();
447                                                 writer.WriteStartElement("type");
448                                                 writer.WriteString(p.ParameterType.Name);
449                                                 writer.WriteEndElement();
450                                                 if(p.DefaultValue != DBNull.Value)
451                                                 {
452                                                         writer.WriteStartElement("default");
453                                                         writer.WriteString(p.DefaultValue.ToString());
454                                                         writer.WriteEndElement();
455                                                 }
456                                                 writer.WriteStartElement("position");
457                                                 writer.WriteString(p.Position.ToString());
458                                                 writer.WriteEndElement();
459                                                 writer.WriteEndElement();
460                                         }
461                                 }
462                         }
463                         catch(Exception)
464                         {
465                                 // Mysteriously this is also causing some corlib types
466                                 // to spit out some Object doesn't have a parent errors
467                                 //Console.WriteLine(e.Message);
468                         }
469                 }
470
471                 //
472                 // Find Attributes for events
473                 //
474                 void getEventInfoXml(EventInfo _obj)
475                 {
476                         if(_obj.IsMulticast)
477                         {
478                                 writer.WriteStartElement("attribute");
479                                 writer.WriteString("multicast");
480                                 writer.WriteEndElement();
481                         }
482                         writer.WriteStartElement("eventhandler");
483                         writer.WriteString(_obj.EventHandlerType.Name);
484                         writer.WriteEndElement();
485                 }
486
487                 //
488                 // Find Attributes for fields
489                 //
490                 void getFieldInfoXml(FieldInfo _obj)
491                 {
492                         if(_obj.IsPrivate)
493                         {
494                                 writer.WriteStartElement("attribute");
495                                 writer.WriteString("private");
496                                 writer.WriteEndElement();
497                         }
498
499                         if(_obj.IsPublic)
500                         {
501                                 writer.WriteStartElement("attribute");
502                                 writer.WriteString("public");
503                                 writer.WriteEndElement();
504                         }
505
506                         if(_obj.IsStatic)
507                         {
508                                 writer.WriteStartElement("attribute");
509                                 writer.WriteString("static");
510                                 writer.WriteEndElement();
511                         }
512                 }
513
514                 //
515                 // Find Attributes for constructors and methods
516                 //
517                 void getMethodBaseInfoXml(MethodBase _obj)
518                 {
519
520                         if(_obj.IsAbstract)
521                         {
522                                 writer.WriteStartElement("attribute");
523                                 writer.WriteString("abstract");
524                                 writer.WriteEndElement();
525                         }
526
527                         if(_obj.IsFinal)
528                         {
529                                 writer.WriteStartElement("attribute");
530                                 writer.WriteString("final");
531                                 writer.WriteEndElement();
532                         }
533
534                         if(_obj.IsPrivate)
535                         {
536                                 writer.WriteStartElement("attribute");
537                                 writer.WriteString("private");
538                                 writer.WriteEndElement();
539                         }
540
541                         if(_obj.IsPublic)
542                         {
543                                 writer.WriteStartElement("attribute");
544                                 writer.WriteString("public");
545                                 writer.WriteEndElement();
546                         }
547
548                         if(_obj.IsStatic)
549                         {
550                                 writer.WriteStartElement("attribute");
551                                 writer.WriteString("static");
552                                 writer.WriteEndElement();
553                         }
554
555                         if(_obj.IsVirtual)
556                         {
557                                 writer.WriteStartElement("attribute");
558                                 writer.WriteString("virtual");
559                                 writer.WriteEndElement();
560                         }
561                 }
562
563                 //
564                 // Put this at the end because it's ugly, but gets the job done
565                 //
566                 void getTypeInfoXml()
567                 {
568                         try
569                         {
570                                 writer.WriteStartElement("inherit");
571                                 writer.WriteString(currentType.BaseType.Name);
572                                 writer.WriteEndElement();
573                                 writer.WriteStartElement("inheritfull");
574                                 writer.WriteString(currentType.BaseType.FullName);
575                                 writer.WriteEndElement();
576
577                                 if(currentType.IsAbstract)
578                                 {
579                                         writer.WriteStartElement("attribute");
580                                         writer.WriteString("abstract");
581                                         writer.WriteEndElement();
582                                 }
583
584                                 if(currentType.IsEnum)
585                                 {
586                                         writer.WriteStartElement("attribute");
587                                         writer.WriteString("enum");
588                                         writer.WriteEndElement();
589                                 }
590
591                                 if(currentType.IsInterface)
592                                 {
593                                         writer.WriteStartElement("attribute");
594                                         writer.WriteString("interface");
595                                         writer.WriteEndElement();
596                                 }
597
598                                 if(currentType.IsPrimitive)
599                                 {
600                                         writer.WriteStartElement("attribute");
601                                         writer.WriteString("primitive");
602                                         writer.WriteEndElement();
603                                 }
604
605                                 if(currentType.IsPublic)
606                                 {
607                                         writer.WriteStartElement("attribute");
608                                         writer.WriteString("public");
609                                         writer.WriteEndElement();
610                                 }
611
612                                 if(currentType.IsSealed)
613                                 {
614                                         writer.WriteStartElement("attribute");
615                                         writer.WriteString("sealed");
616                                         writer.WriteEndElement();
617                                 }
618
619                                 if(currentType.IsSerializable)
620                                 {
621                                         writer.WriteStartElement("attribute");
622                                         writer.WriteString("serializable");
623                                         writer.WriteEndElement();
624                                 }
625
626                         }
627                         catch(Exception) {
628                                 Console.WriteLine("A Class has caused an Exception."
629                                                                  +" This occurred whilst trying to retrieve said types inheritable information.");
630                                 writer.WriteEndElement();
631                         }
632                 }
633         }
634 }