* Fixed missing ')' character.
[mono.git] / mcs / doctools / docstub.cs
1 //      docstub.cs
2 //
3 //      Adam Treat (manyoso@yahoo.com)
4 //      (C) 2002 Adam Treat
5 //
6 //      DocStub is based heavily upon the NDoc project
7 //      ndoc.sourceforge.net
8 //
9 //      This program is free software; you can redistribute it and/or modify
10 //      it under the terms of the GNU General Public License as published by
11 //      the Free Software Foundation; either version 2 of the License, or
12 //      (at your option) any later version.
13
14
15 using System;
16 using System.Collections;
17 using System.Collections.Specialized;
18 using System.Diagnostics;
19 using System.IO;
20 using System.Reflection;
21 using System.Xml;
22 using System.Text;
23
24 namespace Mono.Util
25 {
26         class DocStub
27         {
28                 Assembly assembly;
29                 bool nested;
30                 string assembly_file, directory, language, classname, currentNamespace, docname;
31
32                 void Usage()
33                 {
34                         Console.Write (
35                                 "docstub -l <lang> -d <directory> -a <assembly>\n\n" +
36                                 "   -d || /-d || --dir       <directory>             The directory to write the xml files to.\n" +
37                                 "   -a || /-a || --assembly  <assembly>              Specifies the target assembly to load and parse.\n" +
38                                 "   -l || /-l || --language  <two-letter ISO code>   Specifies the language encoding.\n\n");
39                 }
40
41                 public static void Main(string[] args)
42                 {
43                         DocStub stub = new DocStub(args);
44                 }
45
46                 public DocStub(string [] args)
47                 {
48                         assembly_file = null;
49                         directory = null;
50                         int argc = args.Length;
51
52                         for(int i = 0; i < argc; i++)
53                         {
54                                 string arg = args[i];
55
56                                 // The "/" switch is there for wine users, like me ;-)
57                                 if(arg.StartsWith("-") || arg.StartsWith("/"))
58                                 {
59                                         switch(arg)
60                                         {
61
62                                         case "-d": case "/-d": case "--directory":
63                                                 if((i + 1) >= argc)
64                                                 {
65                                                         Usage();
66                                                         return;
67                                                 }
68                                                 directory = args[++i];
69                                                 continue;
70
71                                         case "-a": case "/-a": case "--assembly":
72                                                 if((i + 1) >= argc)
73                                                 {
74                                                         Usage();
75                                                         return;
76                                                 }
77                                                 assembly_file = args[++i];
78                                                 continue;
79                                         case "-l": case "/-l": case "--language":
80                                                 if((i + 1) >= argc)
81                                                 {
82                                                         Usage();
83                                                         return;
84                                                 }
85                                                 language = args[++i];
86                                                 continue;
87
88                                         default:
89                                                 Usage();
90                                                 return;
91                                         }
92                                 }
93                         }
94
95                         if(assembly_file == null)
96                         {
97                                 Usage();
98                                 return;
99                         } else if(directory == null)
100                         {
101                                 Usage();
102                                 return;
103                         }
104
105                         if (!Directory.Exists(directory) && directory != null)
106                         {
107                 Directory.CreateDirectory(directory);
108             }
109
110                         // Call the main driver to get some things done
111                         MakeXml();
112                 }
113
114                 // Builds an XmlDocument with the reflected metadata
115                 private void MakeXml()
116                 {
117                         try
118                         {
119                                 assembly = LoadAssembly(Path.GetFullPath(assembly_file));
120                         }
121                         catch (Exception e)
122                         {
123                                 Console.WriteLine(e.Message);
124                         }
125                         Write();
126                 }
127
128                 private void Write()
129                 {
130                         foreach(Module module in assembly.GetModules())
131                         {
132                                 WriteNamespaces(module);
133                         }
134                 }
135
136                 private void WriteNamespaces(Module module)
137                 {
138                         Type[] types = module.GetTypes();
139                         StringCollection namespaceNames = GetNamespaceNames(types);
140                         XmlTextWriter dummy = new XmlTextWriter(".temp.xml", null);
141                         foreach (string namespaceName in namespaceNames)
142                         {
143                                 currentNamespace = namespaceName;
144                                 WriteClasses(dummy, types);
145                                 WriteInterfaces(dummy, types);
146                                 WriteStructures(dummy, types);
147                                 WriteDelegates(dummy, types);
148                                 WriteEnumerations(dummy, types);
149                         }
150
151             dummy.Close();
152                         File.Delete(".temp.xml");
153                 }
154
155                 private XmlTextWriter StartDocument()
156                 {
157                         if (!Directory.Exists(directory+"/"+currentNamespace) && directory != null)
158                         {
159                 Directory.CreateDirectory(directory+"/"+currentNamespace);
160             }
161                         string filename = directory+"/"+currentNamespace+"/"+docname+".xml";
162                         XmlTextWriter writer = new XmlTextWriter (filename, null);
163                         writer.Formatting = Formatting.Indented;
164                         writer.Indentation=4;
165                         writer.WriteStartDocument();
166                         writer.WriteStartElement("monodoc");
167                         writer.WriteAttributeString("language",language);
168                         return writer;
169                 }
170
171                 private void EndDocument(XmlTextWriter writer)
172                 {
173                         writer.WriteEndElement();
174                         writer.WriteEndDocument();
175                         nested = false;
176                         writer.Close();
177                 }
178
179                 private bool IsDelegate(Type type)
180                 {
181                         return type.BaseType.FullName == "System.Delegate" ||
182                                 type.BaseType.FullName == "System.MulticastDelegate";
183                 }
184
185                 private string GetTypeName(Type type)
186                 {
187                         return type.FullName.Replace('+', '.');
188                 }
189
190                 private StringCollection GetNamespaceNames(Type[] types)
191                 {
192                         StringCollection namespaceNames = new StringCollection();
193
194                         foreach (Type type in types)
195                         {
196                                 if (namespaceNames.Contains(type.Namespace) == false)
197                                 {
198                                         namespaceNames.Add(type.Namespace);
199                                 }
200                         }
201
202                         return namespaceNames;
203                 }
204
205                 private bool IsAlsoAnEvent(Type type, string fullName)
206                 {
207                         bool isEvent = false;
208
209                         BindingFlags bindingFlags =
210                                 BindingFlags.Instance |
211                                 BindingFlags.Static |
212                                 BindingFlags.Public |
213                                 BindingFlags.NonPublic |
214                                 BindingFlags.DeclaredOnly;
215
216                         foreach (EventInfo eventInfo in type.GetEvents(bindingFlags))
217                         {
218                                 if (eventInfo.EventHandlerType.FullName == fullName)
219                                 {
220                                         isEvent = true;
221                                         break;
222                                 }
223                         }
224
225                         return isEvent;
226                 }
227
228                 private bool IsAlsoAnEvent(FieldInfo field)
229                 {
230                         return IsAlsoAnEvent(field.DeclaringType, field.FieldType.FullName);
231                 }
232
233                 private bool IsAlsoAnEvent(PropertyInfo property)
234                 {
235                         return IsAlsoAnEvent(property.DeclaringType, property.PropertyType.FullName);
236                 }
237
238                 // Loads an assembly.
239                 public static Assembly LoadAssembly(string filename)
240                 {
241                         if (!File.Exists(filename))
242                         {
243                                 throw new ApplicationException("can't find assembly " + filename);
244                         }
245
246                         FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read);
247                         byte[] buffer = new byte[fs.Length];
248                         fs.Read(buffer, 0, (int)fs.Length);
249                         fs.Close();
250
251                         return Assembly.Load(buffer);
252                 }
253
254                 private string GetParameterTypes(ParameterInfo[] parameters)
255                 {
256                         if (parameters.Length != 0) {
257                                 StringBuilder sb = new StringBuilder();
258                                 sb.Append("(");
259                                 foreach (ParameterInfo parameter in parameters)
260                                 {
261                                         sb.Append(GetTypeName(parameter.ParameterType) + ", ");
262                                 }
263                                 sb.Remove(sb.Length-2, 2);
264                                 sb.Append(")");
265                                 return sb.ToString();
266                         } else {
267                                 return "";
268                         }
269                 }
270
271                 private void WriteClasses(XmlTextWriter writer, Type[] types)
272                 {
273                         foreach (Type type in types)
274                         {
275                                 if (type.IsClass && !IsDelegate(type) && type.Namespace.Equals(currentNamespace))
276                                 {
277                                         classname = type.FullName;
278                                         docname = type.Name;
279                                         if (!nested)
280                                         {
281                                                 writer = StartDocument();
282                                                 WriteClass(writer, type);
283                                                 EndDocument(writer);
284                                         } else {
285                                                 WriteClass(writer, type);
286                                         }
287                                 }
288                         }
289                 }
290
291                 private void WriteInterfaces(XmlTextWriter writer, Type[] types)
292                 {
293                         foreach (Type type in types)
294                         {
295                                 if (type.IsInterface && type.Namespace.Equals(currentNamespace))
296                                 {
297                                         classname = type.FullName;
298                                         docname = type.Name;
299                                         if (!nested)
300                                         {
301                                                 writer = StartDocument();
302                                                 WriteInterface(writer, type);
303                                                 EndDocument(writer);
304                                         } else {
305                                                 WriteInterface(writer, type);
306                                         }
307                                 }
308                         }
309                 }
310
311                 private void WriteStructures(XmlTextWriter writer, Type[] types)
312                 {
313                         foreach (Type type in types)
314                         {
315                                 if (type.IsValueType && !type.IsEnum && type.Namespace.Equals(currentNamespace))
316                                 {
317                                         classname = type.FullName;
318                                         docname = type.Name;
319                                         if (!nested)
320                                         {
321                                                 writer = StartDocument();
322                                                 WriteClass(writer, type);
323                                                 EndDocument(writer);
324                                         } else {
325                                                 WriteClass(writer, type);
326                                         }
327                                 }
328                         }
329                 }
330
331                 private void WriteDelegates(XmlTextWriter writer, Type[] types)
332                 {
333                         foreach (Type type in types)
334                         {
335                                 if (type.IsClass && IsDelegate(type) && type.Namespace.Equals(currentNamespace))
336                                 {
337                                         classname = type.FullName;
338                                         docname = type.Name;
339                                         if (!nested)
340                                         {
341                                                 writer = StartDocument();
342                                                 WriteDelegate(writer, type);
343                                                 EndDocument(writer);
344                                         } else {
345                                                 WriteDelegate(writer, type);
346                                         }
347                                 }
348                         }
349                 }
350
351                 private void WriteEnumerations(XmlTextWriter writer, Type[] types)
352                 {
353                         foreach (Type type in types)
354                         {
355                                 if (type.IsEnum && type.Namespace.Equals(currentNamespace))
356                                 {
357                                         classname = type.FullName;
358                                         docname = type.Name;
359                                         if (!nested)
360                                         {
361                                                 writer = StartDocument();
362                                                 WriteEnumeration(writer, type);
363                                                 EndDocument(writer);
364                                         } else {
365                                                 WriteEnumeration(writer, type);
366                                         }
367                                 }
368                         }
369                 }
370
371                 // Writes XML documenting a class or struct.
372                 private void WriteClass(XmlTextWriter writer, Type type)
373                 {
374                         Type[] types = type.GetNestedTypes();
375                         AssemblyName assemblyName = assembly.GetName();
376                         bool isStruct = type.IsValueType;
377                         nested = true;
378
379                         writer.WriteStartElement(isStruct ? "struct" : "class");
380                         writer.WriteAttributeString("name", type.FullName);
381                         writer.WriteAttributeString("assembly", assemblyName.Name);
382                         writer.WriteElementString("summary","TODO");
383                         writer.WriteElementString("remarks","TODO");
384
385                         WriteClasses(writer, types);
386                         WriteInterfaces(writer, types);
387                         WriteStructures(writer, types);
388                         WriteDelegates(writer, types);
389                         WriteEnumerations(writer, types);
390
391                         WriteConstructors(writer, type);
392                         WriteFields(writer, type);
393                         WriteProperties(writer, type);
394                         WriteMethods(writer, type);
395                         WriteOperators(writer, type);
396                         WriteEvents(writer, type);
397
398                         writer.WriteEndElement();
399                 }
400
401                 // Writes XML documenting an interface.
402                 private void WriteInterface(XmlTextWriter writer, Type type)
403                 {
404                         Type[] types = type.GetNestedTypes();
405                         AssemblyName assemblyName = assembly.GetName();
406
407                         writer.WriteStartElement("interface");
408                         writer.WriteAttributeString("name", type.FullName);
409                         writer.WriteAttributeString("assembly", assemblyName.Name);
410                         writer.WriteElementString("summary","TODO");
411                         writer.WriteElementString("remarks","TODO");
412                         writer.WriteEndElement();
413                 }
414
415                 // Writes XML documenting a delegate.
416                 private void WriteDelegate(XmlTextWriter writer, Type type)
417                 {
418                         Type[] types = type.GetNestedTypes();
419                         AssemblyName assemblyName = assembly.GetName();
420
421                         writer.WriteStartElement("delegate");
422                         writer.WriteAttributeString("name", type.FullName);
423                         writer.WriteAttributeString("assembly", assemblyName.Name);
424                         writer.WriteElementString("summary","TODO");
425                         writer.WriteElementString("remarks","TODO");
426
427                         //param
428
429                         writer.WriteEndElement();
430                 }
431
432                 // Writes XML documenting an enumeration.
433                 private void WriteEnumeration(XmlTextWriter writer, Type type)
434                 {
435                         Type[] types = type.GetNestedTypes();
436                         AssemblyName assemblyName = assembly.GetName();
437
438                         writer.WriteStartElement("enum");
439                         writer.WriteAttributeString("name", type.FullName);
440                         writer.WriteAttributeString("assembly", assemblyName.Name);
441                         writer.WriteElementString("summary","TODO");
442                         writer.WriteElementString("remarks","TODO");
443
444                         writer.WriteStartElement("member");
445                         writer.WriteAttributeString("name", "TODO");
446                         writer.WriteEndElement();
447
448                         writer.WriteEndElement();
449                 }
450
451                 private void WriteConstructors(XmlTextWriter writer, Type type)
452                 {
453                         BindingFlags bindingFlags =
454                                 BindingFlags.Instance |
455                                 BindingFlags.Public |
456                                 BindingFlags.NonPublic;
457
458                         ConstructorInfo[] constructors = type.GetConstructors(bindingFlags);
459
460                         foreach (ConstructorInfo constructor in constructors)
461                         {
462                                 WriteConstructor(writer, constructor);
463                         }
464                 }
465
466                 private void WriteFields(XmlTextWriter writer, Type type)
467                 {
468                         BindingFlags bindingFlags =
469                                 BindingFlags.Instance |
470                                 BindingFlags.Static |
471                                 BindingFlags.Public |
472                                 BindingFlags.NonPublic;
473
474                         foreach (FieldInfo field in type.GetFields(bindingFlags))
475                         {
476                                 if (!IsAlsoAnEvent(field))
477                                 {
478                                         WriteField(writer, field);
479                                 }
480                         }
481                 }
482
483                 private void WriteProperties(XmlTextWriter writer, Type type)
484                 {
485                         BindingFlags bindingFlags =
486                                 BindingFlags.Instance |
487                                 BindingFlags.Static |
488                                 BindingFlags.Public |
489                                 BindingFlags.NonPublic;
490
491                         PropertyInfo[] properties = type.GetProperties(bindingFlags);
492
493                         foreach (PropertyInfo property in properties)
494                         {
495                                 MethodInfo getMethod = property.GetGetMethod(true);
496                                 MethodInfo setMethod = property.GetSetMethod(true);
497
498                                 bool hasGetter = (getMethod != null);
499                                 bool hasSetter = (setMethod != null);
500
501                                 if ((hasGetter || hasSetter) && !IsAlsoAnEvent(property))
502                                 {
503                                         WriteProperty(writer, property, property.DeclaringType.FullName != type.FullName);
504                                 }
505                         }
506                 }
507
508                 private void WriteMethods(XmlTextWriter writer, Type type)
509                 {
510                         BindingFlags bindingFlags =
511                                 BindingFlags.Instance |
512                                 BindingFlags.Static |
513                                 BindingFlags.Public |
514                                 BindingFlags.NonPublic;
515
516                         MethodInfo[] methods = type.GetMethods(bindingFlags);
517
518                         foreach (MethodInfo method in methods)
519                         {
520                                 if (!(method.Name.StartsWith("get_")) &&
521                                         !(method.Name.StartsWith("set_")) &&
522                                         !(method.Name.StartsWith("add_")) &&
523                                         !(method.Name.StartsWith("remove_")) &&
524                                         !(method.Name.StartsWith("op_")))
525                                 {
526                                         WriteMethod(writer, method, method.DeclaringType.FullName != type.FullName);
527                                 }
528                         }
529                 }
530
531                 private void WriteOperators(XmlTextWriter writer, Type type)
532                 {
533                         BindingFlags bindingFlags =
534                                 BindingFlags.Instance |
535                                 BindingFlags.Static |
536                                 BindingFlags.Public |
537                                 BindingFlags.NonPublic;
538
539                         MethodInfo[] methods = type.GetMethods(bindingFlags);
540
541                         foreach (MethodInfo method in methods)
542                         {
543                                 if (method.Name.StartsWith("op_"))
544                                 {
545                                         WriteOperator(writer, method);
546                                 }
547                         }
548                 }
549
550                 private void WriteEvents(XmlTextWriter writer, Type type)
551                 {
552                         BindingFlags bindingFlags =
553                                 BindingFlags.Instance |
554                                 BindingFlags.Static |
555                                 BindingFlags.Public |
556                                 BindingFlags.NonPublic |
557                                 BindingFlags.DeclaredOnly;
558
559                         foreach (EventInfo eventInfo in type.GetEvents(bindingFlags))
560                         {
561                                 MethodInfo addMethod = eventInfo.GetAddMethod(true);
562
563                                 if (addMethod != null)
564                                 {
565                                         WriteEvent(writer, eventInfo);
566                                 }
567                         }
568                 }
569
570                 // Writes XML documenting a field.
571                 private void WriteField(XmlTextWriter writer, FieldInfo field)
572                 {
573                         writer.WriteStartElement("field");
574                         writer.WriteAttributeString("name", field.Name);
575                         writer.WriteElementString("summary","TODO");
576                         writer.WriteElementString("remarks","TODO");
577                         writer.WriteEndElement();
578                 }
579
580                 // Writes XML documenting an event.
581                 private void WriteEvent(XmlTextWriter writer, EventInfo eventInfo)
582                 {
583                         writer.WriteStartElement("event");
584                         writer.WriteAttributeString("name", eventInfo.Name);
585                         writer.WriteElementString("summary","TODO");
586                         writer.WriteElementString("remarks","TODO");
587                         writer.WriteElementString("data","TODO");
588
589                         writer.WriteEndElement();
590                 }
591
592                 // Writes XML documenting a constructor.
593                 private void WriteConstructor(XmlTextWriter writer, ConstructorInfo constructor)
594                 {
595                         writer.WriteStartElement("constructor");
596                         writer.WriteAttributeString("name", docname + GetParameterTypes(constructor.GetParameters()));
597                         writer.WriteElementString("summary","TODO");
598                         writer.WriteElementString("remarks","TODO");
599
600                         foreach (ParameterInfo parameter in constructor.GetParameters())
601                         {
602                                 WriteParameter(writer, parameter);
603                         }
604
605
606                         writer.WriteEndElement();
607                 }
608
609                 // Writes XML documenting a property.
610                 private void WriteProperty(XmlTextWriter writer, PropertyInfo property, bool inherited )
611                 {
612                         if (!inherited)
613                         {
614                                 writer.WriteStartElement("property");
615                                 writer.WriteAttributeString("name", property.Name);
616                                 writer.WriteElementString("summary","TODO");
617                                 writer.WriteElementString("remarks","TODO");
618                                 writer.WriteElementString("value","TODO");
619
620                                 writer.WriteEndElement();
621                         }
622                 }
623
624                 // Writes XML documenting an operator.
625                 private void WriteOperator(XmlTextWriter writer, MethodInfo method)
626                 {
627                         if (method != null)
628                         {
629                                 writer.WriteStartElement("operator");
630                                 writer.WriteAttributeString("name", method.Name + GetParameterTypes(method.GetParameters()));
631                                 writer.WriteElementString("summary","TODO");
632                                 writer.WriteElementString("remarks","TODO");
633
634                                 foreach (ParameterInfo parameter in method.GetParameters())
635                                 {
636                                         WriteParameter(writer, parameter);
637                                 }
638
639                                 writer.WriteElementString("returns", "TODO");
640
641                                 writer.WriteEndElement();
642                         }
643                 }
644
645                 // Writes XML documenting a method.
646                 private void WriteMethod(XmlTextWriter writer, MethodInfo method, bool inherited)
647                 {
648                         if (!inherited && method != null)
649                         {
650                                 writer.WriteStartElement("method");
651                                 writer.WriteAttributeString("name", method.Name + GetParameterTypes(method.GetParameters()));
652                                 writer.WriteElementString("summary","TODO");
653                                 writer.WriteElementString("remarks","TODO");
654
655                                 foreach (ParameterInfo parameter in method.GetParameters())
656                                 {
657                                         WriteParameter(writer, parameter);
658                                 }
659
660                                 writer.WriteElementString("returns", "TODO");
661
662                                 writer.WriteEndElement();
663                         }
664                 }
665
666                 private void WriteParameter(XmlTextWriter writer, ParameterInfo parameter)
667                 {
668                         writer.WriteStartElement("param");
669                         writer.WriteAttributeString("name", parameter.Name);
670                         writer.WriteString("TODO");
671                         writer.WriteEndElement();
672                 }
673         }
674 }
675