[Mono-docs-list] XHtml provider, mono handbook provider

Lee Mallabone mono-docs@fonicmonkey.net
27 May 2003 22:55:37 +0100


--=-LB4DqlCYCdJtHpzq4mBL
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Hi all,

Please find attached code that adds 2 providers to the monodoc browser -
one is a generic XHtml provider that works by looking at a table of
contents in an XML file, (which I gather from Johannes is standard
Microsoft "HTML Help" TOC format). It then adds the files that are
referenced in that TOC to the .zip, as well as building the .tree.

The other is the mono handbook provider, which is a slight
specialization of the xhtml provider needed to dynamically remove bits
of the xhtml docs for the handbook on the fly. This demonstrates fairly
nicely how to extend the xhtml provider for specific needs.

Open issues with this code:

1) Clicking of URLs isn't really handled yet.
2) Loading of images/css isn't handled yet.
3) Detection of the provider(s) is hard-coded into the
browser/assembler, rather than dynamically registered.
4) I'm not sure if I've coded the "xhtml:" URL prefixes correctly. 
5) I've commented out the line in assembler.cs that sorts the nodes.
6) There are some weird entities in the mono handbook source, (like
 ) that I'm not convinced should be there.

I plan to (eventually) address the first 3 of those issues. As I'm not
sure when I'll get chance, I thought it best to offer up the code in
case others have more time to improve it.

Miguel/browser-hackers, I'd appreciate if you could comment on 4 & 5 -
it works for the mono handbook, but I don't know if everything will
still Just Work if people create lots of providers based on the xhtml
provider.

Let me know if I need to change anything before committing...

There's a transcript of roughly how to use the patched assembler/browser
below. I know it's something of a pain to get going atm, but please
shout if you have any problems...

Regards,

Lee.


[lee@slayer browser]$ mono assembler.exe --hb Documentation.xml --out
monohb
.. snip lots of output processing the monkeyguide TOC ..
[root@slayer browser]# cp monohb.tree /usr/local/lib/monodoc/sources/
[root@slayer browser]# cp monohb.zip /usr/local/lib/monodoc/sources/
[lee@slayer browser]$ cat /usr/local/lib/monodoc/monodoc.xml
<?xml version="1.0"?>
<node label="Mono Documentation" name="root:">
  <node label="Class Library" name="classlib"/>
  <node label="Gnome Libraries" name="classlib-gnome"/>
  <node label="Mono Handbook" name="monohb"/>
  <node label="XHTML Testing" name="xhtml"/>
</node>
[lee@slayer browser]$ cat /usr/local/lib/monodoc/sources/monohb.source
<?xml version="1.0"?>
<monodoc>
  <source provider="monohb" basefile="monohb" path="monohb"/>
</monodoc>

PS. The code is a tiny bit icky atm - please forgive.



--=-LB4DqlCYCdJtHpzq4mBL
Content-Disposition: attachment; filename=newprovider.diff
Content-Type: text/plain; name=newprovider.diff; charset=ANSI_X3.4-1968
Content-Transfer-Encoding: 7bit

? Makefile
? Makefile.in
? System.XML.zip
? System.zip
? corlib.zip
? hb.source
? lee1.zip
? lee2.zip
? monohb.zip
? newprovider.diff
? tmp
? tree.zip
? xhtml.zip
? ziplib.dll
? ziplib.dll2
Index: Makefile.am
===================================================================
RCS file: /cvs/public/monodoc/browser/Makefile.am,v
retrieving revision 1.10
diff -u -r1.10 Makefile.am
--- Makefile.am	27 May 2003 03:44:24 -0000	1.10
+++ Makefile.am	27 May 2003 21:00:45 -0000
@@ -2,7 +2,7 @@
 monodoc_DATA = browser.exe assembler.exe monodoc.xml
 CSC=mcs
 
-shared_sources = $(srcdir)/ecma-provider.cs $(srcdir)/simple-provider.cs $(srcdir)/html-helper.cs $(srcdir)/provider.cs $(srcdir)/index.cs
+shared_sources = $(srcdir)/monohb-provider.cs $(srcdir)/xhtml-provider.cs $(srcdir)/ecma-provider.cs $(srcdir)/simple-provider.cs $(srcdir)/html-helper.cs $(srcdir)/provider.cs $(srcdir)/index.cs
 assembler_sources = $(srcdir)/assembler.cs $(shared_sources)
 dump_sources      = $(srcdir)/dump.cs $(shared_sources)
 browser_sources   = $(srcdir)/browser.cs $(srcdir)/list.cs $(srcdir)/history.cs $(shared_sources)
Index: assembler.cs
===================================================================
RCS file: /cvs/public/monodoc/browser/assembler.cs,v
retrieving revision 1.10
diff -u -r1.10 assembler.cs
--- assembler.cs	17 Apr 2003 19:48:28 -0000	1.10
+++ assembler.cs	27 May 2003 21:00:45 -0000
@@ -42,7 +42,19 @@
 				}
 				break;
 
-			case "--simple":
+			case "--xhtml":
+			case "--hb":
+				if (i < argc){
+					Provider populator = new XhtmlProvider (args [++i]);
+
+					list.Add (populator);
+				} else {
+					Usage ();
+					return 1;
+				}
+				break;
+
+		case "--simple":
 				if (i < argc){
 					Provider populator = new SimpleProvider (args [++i]);
 
@@ -53,7 +65,7 @@
 				}
 				break;
 
-				
+			
 			default:
 				Usage ();
 				break;
@@ -70,7 +82,7 @@
 		// Sort the toplevel tree
 		//
 
-		hs.Tree.Sort ();
+//		hs.Tree.Sort ();
 			      
 		//
 		// Flushes the EcmaProvider
Index: provider.cs
===================================================================
RCS file: /cvs/public/monodoc/browser/provider.cs,v
retrieving revision 1.25
diff -u -r1.25 provider.cs
--- provider.cs	27 May 2003 03:30:12 -0000	1.25
+++ provider.cs	27 May 2003 21:00:45 -0000
@@ -597,6 +597,22 @@
 						break;
 					}
 					break;
+				case "monohb":
+					try {
+						hs = new MonoHBHelpSource("sources/" + basefile, false);
+					} catch (FileNotFoundException) {
+						Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefile);
+						break;
+					}
+					break;
+				case "xhtml":
+					try {
+						hs = new XhtmlHelpSource ("sources/" + basefile, false);
+					} catch (FileNotFoundException) {
+						Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefile);
+						break;
+					}
+					break;
 				case "simple":
 					try {
 						hs = new SimpleHelpSource ("sources/" + basefile, false);

--=-LB4DqlCYCdJtHpzq4mBL
Content-Disposition: attachment; filename=monohb-provider.cs
Content-Type: text/plain; name=monohb-provider.cs; charset=ANSI_X3.4-1968
Content-Transfer-Encoding: 7bit


using System.Xml;

/**
 * Processes the mono handbook to remove extra web-specific div sections.
 */
public class MonoHBHelpSource: XhtmlHelpSource
{
	public MonoHBHelpSource (string base_file, bool create) : base (base_file, create)
	{
	}
	
	public override XmlDocument ProcessContent(XmlDocument docToProcess)
	{
		XmlNodeList nodeList = docToProcess.GetElementsByTagName("div");
		
		/* Remove the mono handbook specific decorations */
        foreach(XmlNode node in nodeList)
		{
			string cssClass = ((XmlElement)node).GetAttribute("class");
			if (cssClass != null && (cssClass == "topframe" || cssClass == "navbar" || cssClass == "copyright"))
			{
				node.RemoveAll();
			}
		}
		return docToProcess;
	}
}

--=-LB4DqlCYCdJtHpzq4mBL
Content-Disposition: attachment; filename=xhtml-provider.cs
Content-Type: text/plain; name=xhtml-provider.cs; charset=ANSI_X3.4-1968
Content-Transfer-Encoding: 7bit

//
// A provider that uses Windows help file xhtml TOC files and looks for the
// referenced documents to create the help source. 
//
// TODO: Spider the html files in the TOC for other pages .
//
// Authors:
// Copyright 2003 Lee Mallabone <gnome@fonicmonkey.net>
//				  Johannes Roith <johannes@roith.de>
//				  Miguel de Icaza <miguel@ximian.com>

using System;
using System.IO;
using System.Text;
using System.Xml;

//
// The simple provider generates the information source
//
public class XhtmlProvider : Provider {
	string tocFile;
	SimpleHandbookTOCParser tocParser = null;
	
	public XhtmlProvider (string handbookTocFile)
	{
		tocFile = handbookTocFile;
		if (!File.Exists (tocFile))
			throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFile));
		
	}

	public override void PopulateTree (Tree tree)
	{
		new SimpleHandbookTOCParser(tree);
	}


	public override void CloseTree (HelpSource hs, Tree tree)
	{
	}
}

//
// The HelpSource is used during the rendering phase.
//

public class XhtmlHelpSource : HelpSource {
	Encoding enc;
	
	public XhtmlHelpSource (string base_file, bool create) : base (base_file, create)
	{
		enc = new UTF8Encoding (false, false);
	}

	public override string GetText (string url, out Node match_node)
	{
		match_node = null;
		if (url.StartsWith ("xhtml:"))
			return GetTextFromUrl (url);

		return null;
	}
	
	public virtual XmlDocument ProcessContent(XmlDocument docToProcess)
	{
		return docToProcess;
	}

	private XmlDocument RewriteLinks(XmlDocument docToProcess)
	{
		// FIXME: Rewrite any links here that need extra necessary info...
		return docToProcess;
	}

	string GetTextFromUrl (string url)
	{
		// Remove "simple:" prefix
		url = url.Substring (6);

		// Otherwise the last element of the url is the file code we got.
		int pound = url.LastIndexOf ("#");
		string code;
		if (pound == -1)
			code = url;
		else
			code = url.Substring (pound+1);

		if (code == null)
		{
			Console.WriteLine("Warning, NULL url!");
			return "<html>url was null</html>";
		}

		Stream s = GetHelpStream (code);
		if (s == null)
			return String.Format ("<html>No stream for this node: {0} with code ({1})</html>", url, code);

		//
		// Now, get the file type
		//
		int slash = url.LastIndexOf ("/");
		string fname = url; //url.Substring (slash + 1, pound - slash - 1).ToLower ();

		if (s != null && (fname.EndsWith (".html") || fname.EndsWith (".htm") || fname.EndsWith(".xhtml")))
		{
			XmlDocument newdoc = new XmlDocument();
			TextReader reader = new StreamReader (s, enc);
			try
			{
				newdoc.Load(reader);
				reader.Close();
			} 
			catch (XmlException e)
			{
				return "<html>XML Error when loading <b>" + url + "</b>:<br>" + e.Message;
			}
			
			XmlDocument processedDoc = ProcessContent(newdoc);
			XmlDocument docForMonodoc = RewriteLinks(processedDoc);
			return docForMonodoc.InnerXml;
		}
		else
		{
			return String.Format("<html>Unsupported file name: {0}</html>", fname);
		}
	}
}




// Simple Parser for the Handbook TOC format
public class SimpleHandbookTOCParser
{

	public XmlDocument newdoc;
//	Tree monodocTree;

	public static string spaces = "";

  	public SimpleHandbookTOCParser(Tree monodocTree)
  	{
		XmlDocument doc = new XmlDocument();
		doc.Load("Documentation.xml");

		XmlNodeList nodeList = doc.GetElementsByTagName("body");
		XmlNodeList bodylist = nodeList[0].ChildNodes[1].ChildNodes;
		//Node top = monodocTree.LookupNode ("Mono handbook root", "hb:");

		ParseUl(bodylist[1].ChildNodes,monodocTree);
   	}


   public void ParseUl(XmlNodeList items, Node monoTreeNode)
   {

	 Node latestNodeAddition = monoTreeNode;
	 Node nodeToAddChildrenTo = monoTreeNode;
     for (int i = 0;i < items.Count;i++)
	 {    
		if (items[i].LocalName == "li")
		{

			string[] attribs = ParseLi(items[i]);
			
			string filename = attribs[1];

			if (i+1 == items.Count || items[i+1].LocalName == "ul")
			{
				Console.WriteLine(spaces + "+" + attribs[0] + ": " + filename);
				// Put the node in the monodoc toc.
				nodeToAddChildrenTo = latestNodeAddition.CreateNode (attribs[0], "xhtml:" + filename);

			}
			else {
				Console.WriteLine( spaces + attribs[0] + ": " + filename);
				// Put the node in the monodoc toc.
				latestNodeAddition.CreateNode (attribs[0], "xhtml:" + filename);
			}
			// Put the file in the archive.
			if (File.Exists(filename))
				nodeToAddChildrenTo.tree.HelpSource.PackFile (filename, filename);

		/*	if(File.Exists(Environment.CurrentDirectory + "\\" + attribs[1])) {

				try {
					newdoc = new XmlDocument();
					newdoc.Load(Environment.CurrentDirectory + "\\" + attribs[1]);
		
					XmlNodeList nodeList = newdoc.GetElementsByTagName("a");
					foreach(XmlNode node in nodeList) {
	
						try {
							Console.WriteLine(spaces + "   " + node.Attributes.GetNamedItem("href").Value);
						}
						catch
						{
						}
					}
	
				}

				catch
				{
					Console.WriteLine(spaces + "-- PARSE ERROR --");
				}

*/			

		}

		if (items[i].LocalName == "ul")
		{
			spaces += "      ";
			ParseUl(items[i].ChildNodes, nodeToAddChildrenTo);
			nodeToAddChildrenTo = latestNodeAddition;
			spaces = spaces.Substring(6);
		}

	}

	
   }


   public string[] ParseLi(XmlNode me)
   {
		string[] values = {null, null};

		foreach (XmlNode param in me.ChildNodes[0].ChildNodes)
		{    
			if (param.Attributes.GetNamedItem("name").Value == "Name")
					values[0] =  param.Attributes.GetNamedItem("value").Value;		

			if (param.Attributes.GetNamedItem("name").Value == "Local")
					values[1] =  param.Attributes.GetNamedItem("value").Value;
		}

		return values;
	
   }
}

--=-LB4DqlCYCdJtHpzq4mBL--