1d3c9fb2efae3455488623a86e81eaaa66b6f9b9
[mono.git] / mcs / tools / monkeydoc / MonkeyDoc / providers / EcmaDoc.cs
1 using System;
2 using System.Linq;
3 using System.IO;
4 using System.Text;
5 using System.Xml;
6 using System.Xml.Linq;
7 using System.Collections.Generic;
8
9 namespace MonkeyDoc.Providers
10 {
11         // Common functionality between ecma-provider and ecmauncompiled-provider
12         internal class EcmaDoc
13         {
14                 public static void PopulateTreeFromIndexFile (string indexFilePath,
15                                                               Tree tree,
16                                                               IDocStorage storage,
17                                                               Dictionary<string, XElement> nsSummaries,
18                                                               Func<XElement, string> indexGenerator = null)
19                 {
20                         var root = tree.RootNode;
21                         int resID = 0;
22                         var asm = Path.GetDirectoryName (indexFilePath);
23
24                         storage = storage ?? new Storage.NullStorage ();
25                         // nsSummaries is allowed to be null if the user doesn't care about it
26                         nsSummaries = nsSummaries ?? new Dictionary<string, XElement> ();
27                         // default index generator uses a counter
28                         indexGenerator = indexGenerator ?? (_ => resID++.ToString ());
29
30                         using (var reader = XmlReader.Create (File.OpenRead (indexFilePath))) {
31                                 reader.ReadToFollowing ("Types");
32                                 var types = XElement.Load (reader.ReadSubtree ());
33
34                                 foreach (var ns in types.Elements ("Namespace")) {
35                                         var nsName = (string)ns.Attribute ("Name");
36                                         nsName = !string.IsNullOrEmpty (nsName) ? nsName : "global";
37                                         var nsNode = root.GetOrCreateNode (nsName, "N:" + nsName);
38
39                                         XElement nsElements;
40                                         if (!nsSummaries.TryGetValue (nsName, out nsElements))
41                                                 nsSummaries[nsName] = nsElements = new XElement ("elements",
42                                                                                                  new XElement ("summary"),
43                                                                                                  new XElement ("remarks"));
44
45                                         foreach (var type in ns.Elements ("Type")) {
46                                                 // Add the XML file corresponding to the type to our storage
47                                                 var id = indexGenerator (type);
48                                                 string typeFilePath;
49                                                 var typeDocument = EcmaDoc.LoadTypeDocument (asm, nsName, type.Attribute ("Name").Value, out typeFilePath);
50                                                 if (typeDocument == null)
51                                                         continue;
52                                                 using (var file = File.OpenRead (typeFilePath))
53                                                         storage.Store (id, file);
54                                                 nsElements.Add (ExtractClassSummary (typeFilePath));
55
56                                                 var typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type);
57                                                 var url = "ecma:" + id + '#' + typeCaption + '/';
58                                                 typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type, true);
59                                                 var typeNode = nsNode.CreateNode (typeCaption, url);
60
61                                                 // Add meta "Members" node
62                                                 typeNode.CreateNode ("Members", "*");
63                                                 var membersNode = typeDocument.Root.Element ("Members");
64                                                 if (membersNode == null || !membersNode.Elements ().Any ())
65                                                         continue;
66                                                 var members = membersNode
67                                                         .Elements ("Member")
68                                                         .ToLookup (EcmaDoc.GetMemberType);
69
70                                                 foreach (var memberType in members) {
71                                                         // We pluralize the member type to get the caption and take the first letter as URL
72                                                         var node = typeNode.CreateNode (EcmaDoc.PluralizeMemberType (memberType.Key), memberType.Key[0].ToString ());
73                                                         var memberIndex = 0;
74
75                                                         var isCtors = memberType.Key[0] == 'C';
76
77                                                         // We do not escape much member name here
78                                                         foreach (var memberGroup in memberType.GroupBy (m => MakeMemberCaption (m, isCtors))) {
79                                                                 if (memberGroup.Count () > 1) {
80                                                                         // Generate overload
81                                                                         var overloadCaption = MakeMemberCaption (memberGroup.First (), false);
82                                                                         var overloadNode = node.CreateNode (overloadCaption, overloadCaption);
83                                                                         foreach (var member in memberGroup)
84                                                                                 overloadNode.CreateNode (MakeMemberCaption (member, true), (memberIndex++).ToString ());
85                                                                         overloadNode.Sort ();
86                                                                 } else {
87                                                                         // We treat constructor differently by showing their argument list in all cases
88                                                                         node.CreateNode (MakeMemberCaption (memberGroup.First (), isCtors), (memberIndex++).ToString ());
89                                                                 }
90                                                         }
91                                                         node.Sort ();
92                                                 }
93                                         }
94
95                                         nsNode.Sort ();
96                                 }
97                                 root.Sort ();
98                         }
99                 }
100
101                 // Utility methods
102
103                 public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName)
104                 {
105                         string dummy;
106                         return LoadTypeDocument (basePath, nsName, typeName, out dummy);
107                 }
108
109                 public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName, out string finalPath)
110                 {
111                         finalPath = Path.Combine (basePath, nsName, Path.ChangeExtension (typeName, ".xml"));
112                         if (!File.Exists (finalPath)) {
113                                 Console.Error.WriteLine ("Warning: couldn't process type file `{0}' as it doesn't exist", finalPath);
114                                 return null;
115                         }
116                         return XDocument.Load (finalPath);
117                 }
118
119                 public static string GetTypeCaptionFromIndex (XElement typeNodeFromIndex, bool full = false)
120                 {
121                         var t = typeNodeFromIndex;
122                         var c = ((string)(t.Attribute ("DisplayName") ?? t.Attribute ("Name"))).Replace ('+', '.');
123                         if (full)
124                                 c += " " + (string)t.Attribute ("Kind");
125                         return c;
126                 }
127
128                 public static string PluralizeMemberType (string memberType)
129                 {
130                         switch (memberType) {
131                         case "Property":
132                                 return "Properties";
133                         default:
134                                 return memberType + "s";
135                         }
136                 }
137
138                 public static string GetMemberType (XElement m)
139                 {
140                         return m.Attribute ("MemberName").Value.StartsWith ("op_") ? "Operator" : m.Element ("MemberType").Value;
141                 }
142
143                 public static string MakeMemberCaption (XElement member, bool withArguments)
144                 {
145                         var caption = (string)member.Attribute ("MemberName");
146                         // Use type name instead of .ctor for cosmetic sake
147                         if (caption == ".ctor") {
148                                 caption = (string)member.Ancestors ("Type").First ().Attribute ("Name");
149                                 // If this is an inner type ctor, strip the parent type reference
150                                 var plusIndex = caption.LastIndexOf ('+');
151                                 if (plusIndex != -1)
152                                         caption = caption.Substring (plusIndex + 1);
153                         }
154                         if (caption.StartsWith ("op_")) {
155                                 string sig;
156                                 caption = MakeOperatorSignature (member, out sig);
157                                 caption = withArguments ? sig : caption;
158                                 return caption;
159                         }
160                         if (withArguments) {
161                                 var args = member.Element ("Parameters");
162                                 caption += '(';
163                                 if (args != null && args.Elements ("Parameter").Any ()) {
164                                         caption += args.Elements ("Parameter")
165                                                 .Select (p => (string)p.Attribute ("Type"))
166                                                 .Aggregate ((p1, p2) => p1 + "," + p2);
167                                 }
168                                 caption += ')';
169                         }
170                         
171                         return caption;
172                 }
173
174                 internal static string MakeOperatorSignature (XElement member, out string memberSignature)
175                 {
176                         string name = (string)member.Attribute ("MemberName");
177                         var nicename = name.Substring(3);
178                         memberSignature = null;
179
180                         switch (name) {
181                         // unary operators: no overloading possible     [ECMA-335 §10.3.1]
182                         case "op_UnaryPlus":                    // static     R operator+       (T)
183                         case "op_UnaryNegation":                // static     R operator-       (T)
184                         case "op_LogicalNot":                   // static     R operator!       (T)
185                         case "op_OnesComplement":               // static     R operator~       (T)
186                         case "op_Increment":                    // static     R operator++      (T)
187                         case "op_Decrement":                    // static     R operator--      (T)
188                         case "op_True":                         // static  bool operator true   (T)
189                         case "op_False":                        // static  bool operator false  (T)
190                         case "op_AddressOf":                    // static     R operator&       (T)
191                         case "op_PointerDereference":           // static     R operator*       (T)
192                                 memberSignature = nicename;
193                                 break;
194                         // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3]
195                         case "op_Implicit":                    // static implicit operator R (T)
196                         case "op_Explicit":                    // static explicit operator R (T)
197                                 nicename = name.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion";
198                                 string arg = (string)member.Element ("Parameters").Element ("Parameter").Attribute ("Type");
199                                 string ret = (string)member.Element ("ReturnValue").Element ("ReturnType");
200                                 memberSignature = arg + " to " + ret;
201                                 break;
202                         // binary operators: overloading is possible [ECMA-335 §10.3.2]
203                         default:
204                                 memberSignature =
205                                         nicename + "("
206                                         + string.Join (",", member.Element ("Parameters").Elements ("Parameter").Select (p => (string)p.Attribute ("Type")))
207                                         + ")";
208                                 break;
209                         }
210
211                         return nicename;
212                 }
213
214                 static XElement ExtractClassSummary (string typeFilePath)
215                 {
216                         using (var reader = XmlReader.Create (typeFilePath)) {
217                                 reader.ReadToFollowing ("Type");
218                                 var name = reader.GetAttribute ("Name");
219                                 var fullName = reader.GetAttribute ("FullName");
220                                 reader.ReadToFollowing ("AssemblyName");
221                                 var assemblyName = reader.ReadElementString ();
222                                 reader.ReadToFollowing ("summary");
223                                 var summary = reader.ReadInnerXml ();
224                                 reader.ReadToFollowing ("remarks");
225                                 var remarks = reader.ReadInnerXml ();
226
227                                 return new XElement ("class",
228                                                      new XAttribute ("name", name ?? string.Empty),
229                                                      new XAttribute ("fullname", fullName ?? string.Empty),
230                                                      new XAttribute ("assembly", assemblyName ?? string.Empty),
231                                                      new XElement ("summary", new XCData (summary)),
232                                                      new XElement ("remarks", new XCData (remarks)));
233                         }
234                 }
235         }
236 }