Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

410 lines
11 KiB
C#

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Collections.Generic;
using Mono.Documentation;
using BF = System.Reflection.BindingFlags;
namespace Monodoc.Generators.Html
{
public class Ecma2Html : IHtmlExporter
{
static string css_ecma;
static string js;
static XslCompiledTransform ecma_transform;
readonly ExtensionObject ExtObject = new ExtensionObject ();
public Ecma2Html ()
{
}
public string CssCode {
get {
if (css_ecma != null)
return css_ecma;
var assembly = typeof(Ecma2Html).Assembly;
Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
css_ecma = (new StreamReader (str_css)).ReadToEnd();
return css_ecma;
}
}
public string JsCode {
get {
if (js != null)
return js;
var assembly = typeof(Ecma2Html).Assembly;
Stream str_js = assembly.GetManifestResourceStream ("helper.js");
js = (new StreamReader (str_js)).ReadToEnd();
return js;
}
}
public string Htmlize (XmlReader ecma_xml, Dictionary<string, string> extraArgs)
{
var args = new XsltArgumentList ();
args.AddExtensionObject("monodoc:///extensions", ExtObject);
string specialPage;
if (extraArgs.TryGetValue ("specialpage", out specialPage) && specialPage == "root") {
extraArgs.Remove ("specialpage");
extraArgs["show"] = "masteroverview";
}
foreach (var kvp in extraArgs)
args.AddParam (kvp.Key, string.Empty, kvp.Value);
return Htmlize (ecma_xml, args);
}
public string Htmlize (XmlReader ecma_xml, XsltArgumentList args)
{
EnsureTransform ();
var output = new StringBuilder ();
ecma_transform.Transform (ecma_xml,
args,
XmlWriter.Create (output, ecma_transform.OutputSettings),
CreateDocumentResolver ());
return output.ToString ();
}
protected virtual XmlResolver CreateDocumentResolver ()
{
// results in using XmlUrlResolver
return null;
}
public string Export (Stream stream, Dictionary<string, string> extraArgs)
{
return Htmlize (XmlReader.Create (WrapStream (new StreamReader (stream), extraArgs)), extraArgs);
}
public string Export (string input, Dictionary<string, string> extraArgs)
{
return Htmlize (XmlReader.Create (WrapStream (new StringReader (input), extraArgs)), extraArgs);
}
TextReader WrapStream (TextReader initialReader, Dictionary<string, string> renderArgs)
{
string show;
if (renderArgs.TryGetValue ("show", out show) && show == "namespace")
return new AvoidCDataTextReader (initialReader);
return initialReader;
}
static void EnsureTransform ()
{
if (ecma_transform == null) {
ecma_transform = new XslCompiledTransform ();
var assembly = System.Reflection.Assembly.GetCallingAssembly ();
Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
XmlReader xml_reader = new XmlTextReader (stream);
XmlResolver r = new ManifestResourceResolver (".");
ecma_transform.Load (xml_reader, XsltSettings.TrustedXslt, r);
}
}
public class ExtensionObject
{
bool quiet = true;
Dictionary<string, System.Reflection.Assembly> assemblyCache = new Dictionary<string, System.Reflection.Assembly> ();
public string Colorize(string code, string lang)
{
return Mono.Utilities.Colorizer.Colorize(code,lang);
}
// Used by stylesheet to nicely reformat the <see cref=> tags.
public string MakeNiceSignature(string sig, string contexttype)
{
if (sig.Length < 3)
return sig;
if (sig[1] != ':')
return sig;
char s = sig[0];
sig = sig.Substring(2);
switch (s) {
case 'N': return sig;
case 'T': return ShortTypeName (sig, contexttype);
case 'C': case 'M': case 'P': case 'F': case 'E':
string type, mem, arg;
// Get arguments
int paren;
if (s == 'C' || s == 'M')
paren = sig.IndexOf("(");
else if (s == 'P')
paren = sig.IndexOf("[");
else
paren = 0;
if (paren > 0 && paren < sig.Length-1) {
string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
for (int i = 0; i < args.Length; i++)
args[i] = ShortTypeName(args[i], contexttype);
arg = "(" + String.Join(", ", args) + ")";
sig = sig.Substring(0, paren);
} else {
arg = string.Empty;
}
// Get type and member names
int dot = sig.LastIndexOf(".");
if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
mem = string.Empty;
type = sig;
} else {
type = sig.Substring(0, dot);
mem = sig.Substring(dot);
}
type = ShortTypeName(type, contexttype);
return type + mem + arg;
default:
return sig;
}
}
static string ShortTypeName(string name, string contexttype)
{
int dot = contexttype.LastIndexOf(".");
if (dot < 0) return name;
string contextns = contexttype.Substring(0, dot+1);
if (name == contexttype)
return name.Substring(dot+1);
if (name.StartsWith(contextns))
return name.Substring(contextns.Length);
return name.Replace("+", ".");
}
string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
{
if (quiet)
return string.Empty;
var a = new List<string> ();
if (!string.IsNullOrEmpty (arglist)) a.Add (arglist);
return MonoImpInfo(assemblyname, typename, membername, a, strlong);
}
string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
{
if (quiet)
return string.Empty;
var rgs = itr.Cast<XPathNavigator> ().Select (nav => nav.Value).ToList ();
return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
}
string MonoImpInfo(string assemblyname, string typename, string membername, List<string> arglist, bool strlong)
{
try {
System.Reflection.Assembly assembly = null;
try {
if (!assemblyCache.TryGetValue (assemblyname, out assembly)) {
assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
if (assembly != null)
assemblyCache[assemblyname] = assembly;
}
} catch (Exception) {
// nothing.
}
if (assembly == null) {
/*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
else return string.Empty;*/
return string.Empty; // silently ignore
}
Type t = assembly.GetType(typename, false);
if (t == null) {
if (strlong)
return typename + " has not been implemented.";
else
return "Not implemented.";
}
// The following code is flakey and fails to find existing members
return string.Empty;
} catch (Exception) {
return string.Empty;
}
}
string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
{
if (quiet)
return string.Empty;
string s = string.Empty;
object[] atts = mi.GetCustomAttributes(true);
int todoctr = 0;
foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
if (todoctr > 0) {
if (strlong)
s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
else
s = "Unfinished.";
}
return s;
}
public string MonoImpInfo(string assemblyname, string typename, bool strlong)
{
if (quiet)
return string.Empty;
try {
if (assemblyname == string.Empty)
return string.Empty;
System.Reflection.Assembly assembly;
if (!assemblyCache.TryGetValue (assemblyname, out assembly)) {
assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
if (assembly != null)
assemblyCache[assemblyname] = assembly;
}
if (assembly == null)
return string.Empty;
Type t = assembly.GetType(typename, false);
if (t == null) {
if (strlong)
return typename + " has not been implemented.";
else
return "Not implemented.";
}
string s = MonoImpInfo(t, "type", strlong);
if (strlong) {
var mis = t.GetMembers (BF.Static | BF.Instance | BF.Public | BF.NonPublic);
// Scan members for MonoTODO attributes
int mctr = 0;
foreach (var mi in mis) {
string mii = MonoImpInfo(mi, null, false);
if (mii != string.Empty) mctr++;
}
if (mctr > 0) {
s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
}
}
return s;
} catch (Exception) {
return string.Empty;
}
}
public bool MonoEditing ()
{
return false;
}
public bool IsToBeAdded(string text)
{
return text.StartsWith ("To be added");
}
}
}
public class AvoidCDataTextReader : TextReader
{
static readonly char[] CDataPattern = new[] {
'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['
};
static readonly char[] CDataClosingPattern = new[] {
']', ']', '>'
};
TextReader wrappedReader;
char[] backingArray = new char[9]; // "<![CDATA[".Length
int currentIndex = -1;
int eofIndex = -1;
bool inCData;
public AvoidCDataTextReader (TextReader wrappedReader)
{
this.wrappedReader = wrappedReader;
}
public override int Peek ()
{
if (!EnsureBuffer ())
return -1;
return (int)backingArray[currentIndex];
}
public override int Read ()
{
if (!EnsureBuffer ())
return -1;
var result = (int)backingArray[currentIndex];
var next = wrappedReader.Read ();
if (next == -1 && eofIndex == -1)
eofIndex = currentIndex;
else
backingArray[currentIndex] = (char)next;
currentIndex = (currentIndex + 1) % backingArray.Length;
return result;
}
void ReadLength (int length)
{
for (int i = 0; i < length; i++)
Read ();
}
bool EnsureBuffer ()
{
if (currentIndex == -1) {
currentIndex = 0;
var read = wrappedReader.ReadBlock (backingArray, 0, backingArray.Length);
if (read < backingArray.Length)
eofIndex = read;
return read > 0;
} else if (currentIndex == eofIndex) {
return false;
}
if (!inCData && PatternDetect (CDataPattern)) {
inCData = true;
ReadLength (CDataPattern.Length);
return EnsureBuffer ();
}
if (inCData && PatternDetect (CDataClosingPattern)) {
inCData = false;
ReadLength (CDataClosingPattern.Length);
return EnsureBuffer ();
}
return true;
}
bool PatternDetect (char[] pattern)
{
return backingArray[currentIndex] == pattern[0] && Enumerable.Range (1, pattern.Length - 1).All (i => backingArray[(currentIndex + i) % backingArray.Length] == pattern[i]);
}
}
}