c73ed1907e25691a2dad7604e5fa4b77d891a25d
[mono.git] / mcs / class / monodoc / Monodoc / generators / html / Ecma2Html.cs
1 using System;
2 using System.IO;
3 using System.Text;
4 using System.Linq;
5 using System.Xml;
6 using System.Xml.Xsl;
7 using System.Xml.XPath;
8 using System.Collections.Generic;
9
10 using Mono.Documentation;
11 using BF = System.Reflection.BindingFlags;
12
13 namespace Monodoc.Generators.Html
14 {
15         public class Ecma2Html : IHtmlExporter
16         {
17                 static string css_ecma;
18                 static string js;
19                 static XslCompiledTransform ecma_transform;
20                 readonly ExtensionObject ExtObject = new ExtensionObject ();
21
22                 public Ecma2Html ()
23                 {
24                 }
25
26                 public string CssCode {
27                         get {
28                                 if (css_ecma != null)
29                                         return css_ecma;
30                                 var assembly = typeof(Ecma2Html).Assembly;
31                                 Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
32                                 css_ecma = (new StreamReader (str_css)).ReadToEnd();
33                                 return css_ecma;
34                         }
35                 }
36
37                 public string JsCode {
38                         get {
39                                 if (js != null)
40                                         return js;
41                                 var assembly = typeof(Ecma2Html).Assembly;
42                                 Stream str_js = assembly.GetManifestResourceStream ("helper.js");
43                                 js = (new StreamReader (str_js)).ReadToEnd();
44                                 return js;
45                         }
46                 }
47                 
48                 public string Htmlize (XmlReader ecma_xml, Dictionary<string, string> extraArgs)
49                 {
50                         var args = new XsltArgumentList ();
51                         args.AddExtensionObject("monodoc:///extensions", ExtObject);
52                         string specialPage;
53                         if (extraArgs.TryGetValue ("specialpage", out specialPage) && specialPage == "root") {
54                                 extraArgs.Remove ("specialpage");
55                                 extraArgs["show"] = "masteroverview";
56                         }
57
58                         foreach (var kvp in extraArgs)
59                                 args.AddParam (kvp.Key, string.Empty, kvp.Value);
60
61                         return Htmlize (ecma_xml, args);
62                 }
63
64                 public string Htmlize (XmlReader ecma_xml, XsltArgumentList args)
65                 {
66                         EnsureTransform ();
67                 
68                         var output = new StringBuilder ();
69                         ecma_transform.Transform (ecma_xml, 
70                                                   args, 
71                                                   XmlWriter.Create (output, ecma_transform.OutputSettings),
72                                                   CreateDocumentResolver ());
73                         return output.ToString ();
74                 }
75                 
76                 protected virtual XmlResolver CreateDocumentResolver ()
77                 {
78                         // results in using XmlUrlResolver
79                         return null;
80                 }
81
82                 public string Export (Stream stream, Dictionary<string, string> extraArgs)
83                 {
84                         return Htmlize (XmlReader.Create (WrapStream (new StreamReader (stream), extraArgs)), extraArgs);
85                 }
86
87                 public string Export (string input, Dictionary<string, string> extraArgs)
88                 {
89                         return Htmlize (XmlReader.Create (WrapStream (new StringReader (input), extraArgs)), extraArgs);
90                 }
91
92                 TextReader WrapStream (TextReader initialReader, Dictionary<string, string> renderArgs)
93                 {
94                         string show;
95                         if (renderArgs.TryGetValue ("show", out show) && show == "namespace")
96                                 return new AvoidCDataTextReader (initialReader);
97                         return initialReader;
98                 }
99                 
100                 static void EnsureTransform ()
101                 {
102                         if (ecma_transform == null) {
103                                 ecma_transform = new XslCompiledTransform ();
104                                 var assembly = System.Reflection.Assembly.GetCallingAssembly ();
105                         
106                                 Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
107                                 XmlReader xml_reader = new XmlTextReader (stream);
108                                 XmlResolver r = new ManifestResourceResolver (".");
109                                 ecma_transform.Load (xml_reader, XsltSettings.TrustedXslt, r);                  
110                         }
111                 }
112
113                 public class ExtensionObject
114                 {
115                         bool quiet = true;
116
117                         public string Colorize(string code, string lang)
118                         {
119                                 return Mono.Utilities.Colorizer.Colorize(code,lang);
120                         }
121
122                         // Used by stylesheet to nicely reformat the <see cref=> tags. 
123                         public string MakeNiceSignature(string sig, string contexttype)
124                         {
125                                 if (sig.Length < 3)
126                                         return sig;
127                                 if (sig[1] != ':')
128                                         return sig;
129
130                                 char s = sig[0];
131                                 sig = sig.Substring(2);
132                         
133                                 switch (s) {
134                                 case 'N': return sig;
135                                 case 'T': return ShortTypeName (sig, contexttype);
136
137                                 case 'C': case 'M': case 'P': case 'F': case 'E':
138                                         string type, mem, arg;
139                                         
140                                         // Get arguments
141                                         int paren;
142                                         if (s == 'C' || s == 'M')
143                                                 paren = sig.IndexOf("(");
144                                         else if (s == 'P')
145                                                 paren = sig.IndexOf("[");
146                                         else
147                                                 paren = 0;
148                                         
149                                         if (paren > 0 && paren < sig.Length-1) {
150                                                 string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');                                          
151                                                 for (int i = 0; i < args.Length; i++)
152                                                         args[i] = ShortTypeName(args[i], contexttype);
153                                                 arg = "(" + String.Join(", ", args) + ")";
154                                                 sig = sig.Substring(0, paren); 
155                                         } else {
156                                                 arg = string.Empty;
157                                         }
158
159                                         // Get type and member names
160                                         int dot = sig.LastIndexOf(".");
161                                         if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
162                                                 mem = string.Empty;
163                                                 type = sig;
164                                         } else {
165                                                 type = sig.Substring(0, dot);
166                                                 mem = sig.Substring(dot);
167                                         }
168                                                 
169                                         type = ShortTypeName(type, contexttype);
170                                         
171                                         return type + mem + arg;
172
173                                 default:
174                                         return sig;
175                                 }
176                         }
177
178                         static string ShortTypeName(string name, string contexttype)
179                         {
180                                 int dot = contexttype.LastIndexOf(".");
181                                 if (dot < 0) return name;
182                                 string contextns = contexttype.Substring(0, dot+1);
183
184                                 if (name == contexttype)
185                                         return name.Substring(dot+1);
186                         
187                                 if (name.StartsWith(contextns))
188                                         return name.Substring(contextns.Length);
189                         
190                                 return name.Replace("+", ".");
191                         }
192
193                         string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
194                         {
195                                 if (quiet)
196                                         return string.Empty;
197                                 
198                                 var a = new List<string> ();
199                                 if (!string.IsNullOrEmpty (arglist)) a.Add (arglist);
200                                 return MonoImpInfo(assemblyname, typename, membername, a, strlong);
201                         }
202
203                         string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
204                         {
205                                 if (quiet)
206                                         return string.Empty;
207                                 
208                                 var rgs = itr.Cast<XPathNavigator> ().Select (nav => nav.Value).ToList ();
209                         
210                                 return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
211                         }
212                 
213                         string MonoImpInfo(string assemblyname, string typename, string membername, List<string> arglist, bool strlong)
214                         {
215                                 try {
216                                         System.Reflection.Assembly assembly = null;
217                                 
218                                         try {
219                                                 assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
220                                         } catch (Exception) {
221                                                 // nothing.
222                                         }
223                                 
224                                         if (assembly == null) {
225                                                 /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
226                                                   else return string.Empty;*/
227                                                 return string.Empty; // silently ignore
228                                         }
229
230                                         Type t = assembly.GetType(typename, false);
231                                         if (t == null) {
232                                                 if (strlong)
233                                                         return typename + " has not been implemented.";
234                                                 else
235                                                         return "Not implemented.";
236                                         }
237
238                                         // The following code is flakey and fails to find existing members
239                                         return string.Empty;
240                                 } catch (Exception) {
241                                         return string.Empty;
242                                 }
243                         }
244                 
245                         string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
246                         {
247                                 if (quiet)
248                                         return string.Empty;
249                                 
250                                 string s = string.Empty;
251
252                                 object[] atts = mi.GetCustomAttributes(true);
253                                 int todoctr = 0;
254                                 foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
255
256                                 if (todoctr > 0) {
257                                         if (strlong)
258                                                 s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
259                                         else 
260                                                 s = "Unfinished.";
261                                 }
262
263                                 return s;
264                         }
265
266                         public string MonoImpInfo(string assemblyname, string typename, bool strlong)
267                         {
268                                 if (quiet)
269                                         return string.Empty;
270                                 
271                                 try {
272                                         if (assemblyname == string.Empty)
273                                                 return string.Empty;
274
275                                         var assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
276                                         if (assembly == null)
277                                                 return string.Empty;
278
279                                         Type t = assembly.GetType(typename, false);
280                                         if (t == null) {
281                                                 if (strlong)
282                                                         return typename + " has not been implemented.";
283                                                 else
284                                                         return "Not implemented.";
285                                         }
286
287                                         string s = MonoImpInfo(t, "type", strlong);
288
289                                         if (strlong) {
290                                                 var mis = t.GetMembers (BF.Static | BF.Instance | BF.Public | BF.NonPublic);
291
292                                                 // Scan members for MonoTODO attributes
293                                                 int mctr = 0;
294                                                 foreach (var mi in mis) {
295                                                         string mii = MonoImpInfo(mi, null, false);
296                                                         if (mii != string.Empty) mctr++; 
297                                                 }
298                                                 if (mctr > 0) {
299                                                         s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
300                                                 }
301                                         }
302
303                                         return s;
304
305                                 } catch (Exception) {
306                                         return string.Empty;
307                                 }                       
308                         }
309
310                         public bool MonoEditing ()
311                         {
312                                 return false;
313                         }
314                 
315                         public bool IsToBeAdded(string text)
316                         {
317                                 return text.StartsWith ("To be added");
318                         }
319                 }
320         }
321
322         public class AvoidCDataTextReader : TextReader
323         {
324                 static readonly char[] CDataPattern = new[] {
325                         '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['
326                 };
327                 static readonly char[] CDataClosingPattern = new[] {
328                         ']', ']', '>'
329                 };
330                 TextReader wrappedReader;
331                 char[] backingArray = new char[9]; // "<![CDATA[".Length
332                 int currentIndex = -1;
333                 int eofIndex = -1;
334                 bool inCData;
335
336                 public AvoidCDataTextReader (TextReader wrappedReader)
337                 {
338                         this.wrappedReader = wrappedReader;
339                 }
340
341                 public override int Peek ()
342                 {
343                         if (!EnsureBuffer ())
344                                 return -1;
345                         return (int)backingArray[currentIndex];
346                 }
347
348                 public override int Read ()
349                 {
350                         if (!EnsureBuffer ())
351                                 return -1;
352                         var result = (int)backingArray[currentIndex];
353                         var next = wrappedReader.Read ();
354                         if (next == -1 && eofIndex == -1)
355                                 eofIndex = currentIndex;
356                         else
357                                 backingArray[currentIndex] = (char)next;
358                         currentIndex = (currentIndex + 1) % backingArray.Length;
359                         return result;
360                 }
361
362                 void ReadLength (int length)
363                 {
364                         for (int i = 0; i < length; i++)
365                                 Read ();
366                 }
367
368                 bool EnsureBuffer ()
369                 {
370                         if (currentIndex == -1) {
371                                 currentIndex = 0;
372                                 var read = wrappedReader.ReadBlock (backingArray, 0, backingArray.Length);
373                                 if (read < backingArray.Length)
374                                         eofIndex = read;
375                                 return read > 0;
376                         } else if (currentIndex == eofIndex) {
377                                 return false;
378                         }
379                         if (!inCData && PatternDetect (CDataPattern)) {
380                                 inCData = true;
381                                 ReadLength (CDataPattern.Length);
382                                 return EnsureBuffer ();
383                         }
384                         if (inCData && PatternDetect (CDataClosingPattern)) {
385                                 inCData = false;
386                                 ReadLength (CDataClosingPattern.Length);
387                                 return EnsureBuffer ();
388                         }
389
390                         return true;
391                 }
392
393                 bool PatternDetect (char[] pattern)
394                 {
395                         return backingArray[currentIndex] == pattern[0] && Enumerable.Range (1, pattern.Length - 1).All (i => backingArray[(currentIndex + i) % backingArray.Length] == pattern[i]);
396                 }
397         }
398 }