5 // Jonathan Pryor <jpryor@novell.com>
7 // Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections.Generic;
31 using System.Diagnostics;
36 using System.Xml.Linq;
39 using Monodoc.Generators;
41 using Mono.Documentation;
46 using ICSharpCode.SharpZipLib.Zip;
48 namespace Mono.Documentation
50 public class MDocExportWebdocHtml : MDocCommand
53 public Dictionary<string, List<string>> Formats = new Dictionary<string, List<string>>();
54 public List<string> Sources = new List<string>();
55 public bool UseSystemSources = true;
56 public bool ForceUpdate = false;
57 public string OutputDirectory = null;
60 public override void Run (IEnumerable<string> args)
62 var opts = new Options ();
63 var formatOptions = MDocAssembler.CreateFormatOptions (this, opts.Formats);
64 var options = new OptionSet () {
66 "Always generate new files. If not specified, will only generate " +
67 "files if the write time of the output directory is older than the " +
68 "write time of the source .tree/.zip files.",
69 v => opts.ForceUpdate = v != null },
73 "The {PREFIX} to place the generated files and directories. " +
74 "Default: \"`dirname FILE`/cache/\".\n" +
75 "Underneath {PREFIX}, `basename FILE .tree` directories will be " +
76 "created which will contain the pre-generated HTML content.",
77 v => opts.OutputDirectory = v },
79 "A {SOURCE} file to use for reference purposes.\n" +
80 "Extension methods are searched for among all {SOURCE}s which are referenced.\n" +
81 "This option may be specified multiple times.",
82 v => opts.Sources.Add (v) },
83 { "use-system-sources",
84 "Use the system-wide .source files for reference purposes. " +
85 "Default is " + (opts.UseSystemSources ? "enabled" : "disabled") + ".",
86 v => opts.UseSystemSources = v != null },
88 Parse (options, args, "export-html-webdoc",
90 "Export mdoc documentation within FILES to HTML for use by ASP.NET webdoc.\n\n" +
91 "FILES are .tree or .zip files as produced by 'mdoc assemble', or .source files\n" +
92 "which reference .tree and .zip files produced by 'mdoc assemble'.\n\n" +
93 "See mdoc(5) or mdoc-assemble(1) for information about the .source file format.");
94 if (opts.Formats.Values.All (files => files.Count == 0))
95 Error ("No files specified.");
96 ProcessSources (opts);
97 foreach (var p in opts.Formats)
98 ProcessFiles (opts, p.Key, p.Value);
101 void ProcessSources (Options opts)
103 foreach (var p in opts.Formats) {
105 foreach (var f in files.Where (f => f.EndsWith (".source")).ToList ()) {
107 foreach (var tfi in GetTreeFilesFromSource (f)) {
108 List<string> treeFiles;
109 if (!opts.Formats.TryGetValue (tfi.Key, out treeFiles))
110 opts.Formats.Add (tfi.Key, treeFiles = new List<string> ());
111 treeFiles.Add (tfi.Value);
117 IEnumerable<KeyValuePair<string, string>> GetTreeFilesFromSource (string sourceFile)
120 var source = XElement.Load (sourceFile);
121 return source.Descendants ("source")
122 .Select (e => new KeyValuePair<string, string>(e.Attribute ("provider").Value,
123 Path.Combine (Path.GetDirectoryName (sourceFile), e.Attribute ("basefile").Value + ".tree")));
125 catch (Exception e) {
126 Message (TraceLevel.Error, "mdoc: error parsing file {0}: {1}", sourceFile, e.Message);
127 return new KeyValuePair<string, string>[0];
131 void ProcessFiles (Options opts, string format, List<string> files)
133 foreach (var basePath in
135 Path.Combine (Path.GetDirectoryName (f), Path.GetFileNameWithoutExtension (f)))
137 string treeFile = basePath + ".tree";
138 string zipFile = basePath + ".zip";
139 if (!Exists (treeFile) || !Exists (zipFile))
141 string outDir = opts.OutputDirectory != null
142 ? Path.Combine (opts.OutputDirectory, Path.GetFileName (basePath))
143 : XmlDocUtils.GetCacheDirectory (basePath);
144 if (!opts.ForceUpdate && Directory.Exists (outDir) &&
145 MaxWriteTime (treeFile, zipFile) < Directory.GetLastWriteTime (outDir))
147 Message (TraceLevel.Warning, "Processing files: {0}, {1}", treeFile, zipFile);
148 Directory.CreateDirectory (outDir);
149 ExtractZipFile (zipFile, outDir);
150 GenerateCache (opts, basePath, format, outDir);
154 bool Exists (string file)
156 if (!File.Exists (file)) {
157 Message (TraceLevel.Error,
158 "mdoc: Could not find file: {0}", file);
164 DateTime MaxWriteTime (params string[] files)
166 return files.Select (f => File.GetLastWriteTime (f)).Max ();
169 void ExtractZipFile (string zipFile, string outDir)
171 ZipInputStream zip = new ZipInputStream (File.OpenRead (zipFile));
174 while ((entry = zip.GetNextEntry ()) != null) {
175 string file = Path.Combine (outDir, entry.Name);
176 Directory.CreateDirectory (Path.GetDirectoryName (file));
177 using (var output = File.OpenWrite (file))
178 zip.WriteTo (output);
182 void GenerateCache (Options opts, string basePath, string format, string outDir)
184 var hs = RootTree.GetHelpSource (format, basePath);
186 Error ("Unable to find a HelpSource for provider '{0}' and file '{1}.tree'.", format, basePath);
189 RootTree docRoot = null;
190 if (!opts.UseSystemSources)
191 docRoot = RootTree.LoadTree (null, null, opts.Sources);
193 docRoot = RootTree.LoadTree ();
194 foreach (var source in opts.Sources)
195 docRoot.AddSourceFile (source);
197 hs.RootTree = docRoot;
198 var generator = new HtmlGenerator (new NullCache ());
199 foreach (Node node in tree.RootNode.TraverseDepthFirst<Node, Node> (t => t, t => t.ChildNodes)) {
200 var url = node.PublicUrl;
201 Message (TraceLevel.Info, "\tProcessing URL: {0}", url);
202 if (string.IsNullOrEmpty (url))
204 var file = XmlDocUtils.GetCachedFileName (outDir, url);
205 using (var o = File.AppendText (file)) {
206 string contents = docRoot.RenderUrl (url, generator, hs);