7 using System.Collections.Generic;
9 namespace MonkeyDoc.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);
116 return XDocument.Load (finalPath);
119 public static string GetTypeCaptionFromIndex (XElement typeNodeFromIndex, bool full = false)
121 var t = typeNodeFromIndex;
122 var c = ((string)(t.Attribute ("DisplayName") ?? t.Attribute ("Name"))).Replace ('+', '.');
124 c += " " + (string)t.Attribute ("Kind");
128 public static string PluralizeMemberType (string memberType)
130 switch (memberType) {
134 return memberType + "s";
138 public static string GetMemberType (XElement m)
140 return m.Attribute ("MemberName").Value.StartsWith ("op_") ? "Operator" : m.Element ("MemberType").Value;
143 public static string MakeMemberCaption (XElement member, bool withArguments)
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 ('+');
152 caption = caption.Substring (plusIndex + 1);
154 if (caption.StartsWith ("op_")) {
156 caption = MakeOperatorSignature (member, out sig);
157 caption = withArguments ? sig : caption;
161 var args = member.Element ("Parameters");
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);
174 internal static string MakeOperatorSignature (XElement member, out string memberSignature)
176 string name = (string)member.Attribute ("MemberName");
177 var nicename = name.Substring(3);
178 memberSignature = null;
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;
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;
202 // binary operators: overloading is possible [ECMA-335 §10.3.2]
206 + string.Join (",", member.Element ("Parameters").Elements ("Parameter").Select (p => (string)p.Attribute ("Type")))
214 static XElement ExtractClassSummary (string typeFilePath)
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 ();
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)));