7 using System.Collections.Generic;
9 namespace Monodoc.Providers
11 // Common functionality between ecma-provider and ecmauncompiled-provider
12 internal class EcmaDoc
14 public static void PopulateTreeFromIndexFile (string indexFilePath,
17 Dictionary<string, XElement> nsSummaries,
18 Func<XElement, string> indexGenerator = null)
20 var root = tree.RootNode;
22 var asm = Path.GetDirectoryName (indexFilePath);
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 ());
30 using (var reader = XmlReader.Create (File.OpenRead (indexFilePath))) {
31 reader.ReadToFollowing ("Types");
32 var types = XElement.Load (reader.ReadSubtree ());
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);
40 if (!nsSummaries.TryGetValue (nsName, out nsElements))
41 nsSummaries[nsName] = nsElements = new XElement ("elements",
42 new XElement ("summary"),
43 new XElement ("remarks"));
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);
49 var typeDocument = EcmaDoc.LoadTypeDocument (asm, nsName, type.Attribute ("Name").Value, out typeFilePath);
50 if (typeDocument == null)
52 using (var file = File.OpenRead (typeFilePath))
53 storage.Store (id, file);
54 nsElements.Add (ExtractClassSummary (typeFilePath));
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);
61 // Add meta "Members" node
62 typeNode.CreateNode ("Members", "*");
63 var membersNode = typeDocument.Root.Element ("Members");
64 if (membersNode == null || !membersNode.Elements ().Any ())
66 var members = membersNode
68 .ToLookup (EcmaDoc.GetMemberType);
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 ());
75 var isCtors = memberType.Key[0] == 'C';
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) {
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 ());
87 // We treat constructor differently by showing their argument list in all cases
88 node.CreateNode (MakeMemberCaption (memberGroup.First (), isCtors), (memberIndex++).ToString ());
103 public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName)
106 return LoadTypeDocument (basePath, nsName, typeName, out dummy);
109 public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName, out string finalPath)
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);
117 XDocument doc = null;
119 doc = XDocument.Load (finalPath);
120 } catch (Exception e) {
121 Console.WriteLine ("Document `{0}' is unparsable, {1}", finalPath, e.ToString ());
127 public static string GetTypeCaptionFromIndex (XElement typeNodeFromIndex, bool full = false)
129 var t = typeNodeFromIndex;
130 var c = ((string)(t.Attribute ("DisplayName") ?? t.Attribute ("Name"))).Replace ('+', '.');
132 c += " " + (string)t.Attribute ("Kind");
136 public static string PluralizeMemberType (string memberType)
138 switch (memberType) {
142 return memberType + "s";
146 public static string GetMemberType (XElement m)
148 return m.Attribute ("MemberName").Value.StartsWith ("op_") ? "Operator" : m.Element ("MemberType").Value;
151 public static string MakeMemberCaption (XElement member, bool withArguments)
153 var caption = (string)member.Attribute ("MemberName");
154 // Use type name instead of .ctor for cosmetic sake
155 if (caption == ".ctor") {
156 caption = (string)member.Ancestors ("Type").First ().Attribute ("Name");
157 // If this is an inner type ctor, strip the parent type reference
158 var plusIndex = caption.LastIndexOf ('+');
160 caption = caption.Substring (plusIndex + 1);
162 if (caption.StartsWith ("op_")) {
164 caption = MakeOperatorSignature (member, out sig);
165 caption = withArguments ? sig : caption;
169 var args = member.Element ("Parameters");
171 if (args != null && args.Elements ("Parameter").Any ()) {
172 caption += args.Elements ("Parameter")
173 .Select (p => (string)p.Attribute ("Type"))
174 .Aggregate ((p1, p2) => p1 + "," + p2);
182 internal static string MakeOperatorSignature (XElement member, out string memberSignature)
184 string name = (string)member.Attribute ("MemberName");
185 var nicename = name.Substring(3);
186 memberSignature = null;
189 // unary operators: no overloading possible [ECMA-335 §10.3.1]
190 case "op_UnaryPlus": // static R operator+ (T)
191 case "op_UnaryNegation": // static R operator- (T)
192 case "op_LogicalNot": // static R operator! (T)
193 case "op_OnesComplement": // static R operator~ (T)
194 case "op_Increment": // static R operator++ (T)
195 case "op_Decrement": // static R operator-- (T)
196 case "op_True": // static bool operator true (T)
197 case "op_False": // static bool operator false (T)
198 case "op_AddressOf": // static R operator& (T)
199 case "op_PointerDereference": // static R operator* (T)
200 memberSignature = nicename;
202 // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3]
203 case "op_Implicit": // static implicit operator R (T)
204 case "op_Explicit": // static explicit operator R (T)
205 nicename = name.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion";
206 string arg = (string)member.Element ("Parameters").Element ("Parameter").Attribute ("Type");
207 string ret = (string)member.Element ("ReturnValue").Element ("ReturnType");
208 memberSignature = arg + " to " + ret;
210 // binary operators: overloading is possible [ECMA-335 §10.3.2]
214 + string.Join (",", member.Element ("Parameters").Elements ("Parameter").Select (p => (string)p.Attribute ("Type")))
222 static XElement ExtractClassSummary (string typeFilePath)
224 using (var reader = XmlReader.Create (typeFilePath)) {
225 reader.ReadToFollowing ("Type");
226 var name = reader.GetAttribute ("Name");
227 var fullName = reader.GetAttribute ("FullName");
228 reader.ReadToFollowing ("AssemblyName");
229 var assemblyName = reader.ReadElementString ();
230 reader.ReadToFollowing ("summary");
231 var summary = reader.ReadInnerXml ();
232 reader.ReadToFollowing ("remarks");
233 var remarks = reader.ReadInnerXml ();
235 return new XElement ("class",
236 new XAttribute ("name", name ?? string.Empty),
237 new XAttribute ("fullname", fullName ?? string.Empty),
238 new XAttribute ("assembly", assemblyName ?? string.Empty),
239 new XElement ("summary", new XCData (summary)),
240 new XElement ("remarks", new XCData (remarks)));