* Mono.Documentation/webdoc.cs: Remove console debug spew. If you run
[mono.git] / mcs / tools / mdoc / Mono.Documentation / webdoc.cs
1 //
2 // webdoc.cs
3 //
4 // Author:
5 //   Jonathan Pryor  <jpryor@novell.com>
6 //
7 // Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.Diagnostics;
32 using System.IO;
33 using System.Linq;
34 using System.Text;
35 using System.Web;
36
37 using Monodoc;
38 using Mono.Documentation;
39
40 using Mono.Options;
41 using Mono.Rocks;
42
43 using ICSharpCode.SharpZipLib.Zip;
44
45 namespace Mono.Documentation
46 {
47         public class MDocExportWebdocHtml : MDocCommand
48         {
49                 public override void Run (IEnumerable<string> args)
50                 {
51                         string dir = null;
52                         bool forceUpdate = false;
53                         var options = new OptionSet () {
54                                 { "force-update",
55                                         "Always generate new files.  If not specified, will only generate " +
56                                         "files if the write time of the output directory is older than the " +
57                                         "write time of the source .tree/.zip files.",
58                                         v => forceUpdate = v != null },
59                                 { "o|out=",
60                                         "The {DIRECTORY} to place the generated files and directories.\n\n" +
61                                         "If not specified, defaults to\n`dirname FILE`/cache/`basename FILE .tree`.",
62                                         v => dir = v },
63                         };
64                         List<string> files = Parse (options, args, "export-html-webdoc", 
65                                         "[OPTIONS]+ FILES",
66                                         "Export mdoc documentation within FILES to HTML for use by ASP.NET webdoc.\n\n" +
67                                         "FILES are .tree or .zip files as produced by 'mdoc assemble'.");
68                         if (files == null)
69                                 return;
70                         if (files.Count == 0)
71                                 Error ("No files specified.");
72                         HelpSource.use_css = true;
73                         HelpSource.FullHtml = false;
74                         SettingsHandler.Settings.EnableEditing = false;
75                         foreach (var basePath in 
76                                         files.Select (f => 
77                                                         Path.Combine (Path.GetDirectoryName (f), Path.GetFileNameWithoutExtension (f)))
78                                         .Distinct ()) {
79                                 string treeFile = basePath + ".tree";
80                                 string zipFile  = basePath + ".zip";
81                                 if (!Exists (treeFile) || !Exists (zipFile))
82                                         continue;
83                                 string outDir = dir ?? XmlDocUtils.GetCacheDirectory (basePath);
84                                 if (!forceUpdate && Directory.Exists (outDir) &&
85                                                         MaxWriteTime (treeFile, zipFile) < Directory.GetLastWriteTime (outDir))
86                                         continue;
87                                 Message (TraceLevel.Warning, "Processing files: {0}, {1}", treeFile, zipFile);
88                                 Directory.CreateDirectory (outDir);
89                                 ExtractZipFile (zipFile, outDir);
90                                 GenerateCache (basePath, treeFile, outDir);
91                         }
92                 }
93
94                 bool Exists (string file)
95                 {
96                         if (!File.Exists (file)) {
97                                         Message (TraceLevel.Error,
98                                                         "mdoc: Could not find file: {0}", file);
99                                         return false;
100                         }
101                         return true;
102                 }
103
104                 DateTime MaxWriteTime (params string[] files)
105                 {
106                         return files.Select (f => File.GetLastWriteTime (f)).Max ();
107                 }
108
109                 void ExtractZipFile (string zipFile, string outDir)
110                 {
111                         ZipInputStream zip = new ZipInputStream (File.OpenRead (zipFile));
112
113                         ZipEntry entry;
114                         while ((entry = zip.GetNextEntry ()) != null) {
115                                 string file = Path.Combine (outDir, entry.Name);
116                                 Directory.CreateDirectory (Path.GetDirectoryName (file));
117                                 using (var output = File.OpenWrite (file))
118                                         zip.WriteTo (output);
119                         }
120                 }
121
122                 void GenerateCache (string basePath, string treeFile, string outDir)
123                 {
124                         Tree tree = new Tree (null, treeFile);
125                         RootTree docRoot = RootTree.LoadTree ();
126                         string helpSourceName = Path.GetFileName (basePath);
127                         HelpSource hs = docRoot.HelpSources.Cast<HelpSource> ()
128                                 .FirstOrDefault (h => h.Name == helpSourceName);
129                         if (hs == null) {
130                                 throw new Exception ("Only installed .tree and .zip files are supported.");
131                         }
132                         foreach (Node node in tree.TraverseDepthFirst<Node, Node> (t => t, t => t.Nodes.Cast<Node> ())) {
133                                 var url = node.URL;
134                                 Message (TraceLevel.Info, "\tProcessing URL: {0}", url);
135                                 if (string.IsNullOrEmpty (url))
136                                         continue;
137                                 var file = XmlDocUtils.GetCachedFileName (outDir, url);
138                                 using (var o = File.AppendText (file)) {
139                                         Node _;
140                                         // Sometimes the HelpSource won't directly support a url.
141                                         // Case in point: the Tree will contain N:Enter.Namespace.Here nodes
142                                         // which aren't supported by HelpSource.GetText.
143                                         // If this happens, docRoot.RenderUrl() works.
144                                         // (And no, we can't always use docRoot.RenderUrl() for URLs like
145                                         // "ecma:0#Foo/", as that'll just grab the 0th stream contents from
146                                         // the first EcmaHelpSource found...
147                                         string contents = hs.GetText (url, out _) ?? docRoot.RenderUrl (url, out _);
148                                         o.Write (contents);
149                                 }
150                         }
151                 }
152         }
153 }