PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/tools/monodoc/Monodoc/provider.cs

https://bitbucket.org/steenlund/mono-2.6.7-for-amiga
C# | 1642 lines | 1245 code | 249 blank | 148 comment | 206 complexity | cc2ee0b472b073b8cb8bb2cabf89ce77 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, LGPL-2.1
  1. //
  2. // Provider: shared code and interfaces for providers
  3. //
  4. // Author:
  5. // Miguel de Icaza (miguel@ximian.com)
  6. //
  7. // (C) 2002, Ximian, Inc.
  8. //
  9. // TODO:
  10. // Each node should have a provider link
  11. //
  12. // Should encode numbers using a runlength encoding to save space
  13. //
  14. namespace Monodoc {
  15. using System;
  16. using System.Collections.Generic;
  17. using System.IO;
  18. using System.Linq;
  19. using System.Text;
  20. using System.Text.RegularExpressions;
  21. using System.Collections;
  22. using System.Diagnostics;
  23. using System.Configuration;
  24. using System.Reflection;
  25. using System.Text.RegularExpressions;
  26. using System.Xml;
  27. using System.Xml.XPath;
  28. using ICSharpCode.SharpZipLib.Zip;
  29. using Monodoc.Lucene.Net.Index;
  30. using Monodoc.Lucene.Net.Analysis.Standard;
  31. using Mono.Documentation;
  32. /// <summary>
  33. /// This tree is populated by the documentation providers, or populated
  34. /// from a binary encoding of the tree. The format of the tree is designed
  35. /// to minimize the need to load it in full.
  36. /// </summary>
  37. public class Tree : Node {
  38. #region Loading the tree from a file
  39. /// <summary>
  40. /// Our HelpSource container
  41. /// </summary>
  42. public readonly HelpSource HelpSource;
  43. internal FileStream InputStream;
  44. internal BinaryReader InputReader;
  45. /// <summary>
  46. /// Load from file constructor
  47. /// </summary>
  48. public Tree (HelpSource hs, string filename) : base (null, null)
  49. {
  50. Encoding utf8 = new UTF8Encoding (false, true);
  51. if (!File.Exists (filename)){
  52. throw new FileNotFoundException ();
  53. }
  54. InputStream = File.OpenRead (filename);
  55. InputReader = new BinaryReader (InputStream, utf8);
  56. byte [] sig = InputReader.ReadBytes (4);
  57. if (!GoodSig (sig))
  58. throw new Exception ("Invalid file format");
  59. InputStream.Position = 4;
  60. position = InputReader.ReadInt32 ();
  61. LoadNode ();
  62. HelpSource = hs;
  63. }
  64. /// <summary>
  65. /// Tree creation and merged tree constructor
  66. /// </summary>
  67. public Tree (HelpSource hs, string caption, string url) : base (caption, url)
  68. {
  69. HelpSource = hs;
  70. }
  71. public Tree (HelpSource hs, Node parent, string caption, string element) : base (parent, caption, element)
  72. {
  73. HelpSource = hs;
  74. }
  75. #endregion
  76. /// <summary>
  77. /// Saves the tree into the specified file using the help file format.
  78. /// </summary>
  79. public void Save (string file)
  80. {
  81. Encoding utf8 = new UTF8Encoding (false, true);
  82. using (FileStream output = File.OpenWrite (file)){
  83. // Skip over the pointer to the first node.
  84. output.Position = 8;
  85. using (BinaryWriter writer = new BinaryWriter (output, utf8)){
  86. // Recursively dump
  87. Dump (output, writer);
  88. output.Position = 0;
  89. writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
  90. writer.Write (position);
  91. }
  92. }
  93. }
  94. static bool GoodSig (byte [] sig)
  95. {
  96. if (sig.Length != 4)
  97. return false;
  98. if (sig [0] != (byte) 'M' ||
  99. sig [1] != (byte) 'o' ||
  100. sig [2] != (byte) 'H' ||
  101. sig [3] != (byte) 'P')
  102. return false;
  103. return true;
  104. }
  105. }
  106. public class Node : IComparable {
  107. string caption, element;
  108. public bool Documented;
  109. public readonly Tree tree;
  110. Node parent;
  111. protected ArrayList nodes;
  112. protected internal int position;
  113. static ArrayList empty = ArrayList.ReadOnly(new ArrayList(0));
  114. /// <summary>
  115. /// Creates a node, called by the Tree.
  116. /// </summary>
  117. public Node (string caption, string element)
  118. {
  119. this.tree = (Tree) this;
  120. this.caption = caption;
  121. this.element = element;
  122. parent = null;
  123. }
  124. public Node (Node parent, string caption, string element)
  125. {
  126. this.parent = parent;
  127. this.tree = parent.tree;
  128. this.caption = caption;
  129. this.element = element;
  130. }
  131. /// <summary>
  132. /// Creates a node from an on-disk representation
  133. /// </summary>
  134. Node (Node parent, int address)
  135. {
  136. this.parent = parent;
  137. position = address;
  138. this.tree = parent.tree;
  139. if (address > 0)
  140. LoadNode ();
  141. }
  142. public void AddNode (Node n)
  143. {
  144. Nodes.Add (n);
  145. n.parent = this;
  146. n.Documented = true;
  147. }
  148. public void DelNode (Node n)
  149. {
  150. Nodes.Remove (n);
  151. }
  152. public ArrayList Nodes {
  153. get {
  154. if (position < 0)
  155. LoadNode ();
  156. return nodes != null ? nodes : empty;
  157. }
  158. }
  159. public string Element {
  160. get {
  161. if (position < 0)
  162. LoadNode ();
  163. return element;
  164. }
  165. set {
  166. element = value;
  167. }
  168. }
  169. public string Caption {
  170. get {
  171. if (position < 0)
  172. LoadNode ();
  173. return caption;
  174. }
  175. }
  176. public Node Parent {
  177. get {
  178. return parent;
  179. }
  180. }
  181. public void LoadNode ()
  182. {
  183. if (position < 0)
  184. position = -position;
  185. tree.InputStream.Position = position;
  186. BinaryReader reader = tree.InputReader;
  187. int count = DecodeInt (reader);
  188. element = reader.ReadString ();
  189. caption = reader.ReadString ();
  190. if (count == 0)
  191. return;
  192. nodes = new ArrayList (count);
  193. for (int i = 0; i < count; i++){
  194. int child_address = DecodeInt (reader);
  195. Node t = new Node (this, -child_address);
  196. nodes.Add (t);
  197. }
  198. }
  199. /// <summary>
  200. /// Creates a new node, in the locator entry point, and with
  201. /// a user visible caption of @caption
  202. /// </summary>
  203. public Node CreateNode (string c_caption, string c_element)
  204. {
  205. if (nodes == null)
  206. nodes = new ArrayList ();
  207. Node t = new Node (this, c_caption, c_element);
  208. nodes.Add (t);
  209. return t;
  210. }
  211. /// <summary>
  212. /// Looks up or creates a new node, in the locator entry point, and with
  213. /// a user visible caption of @caption. This is different from
  214. /// CreateNode in that it will look up an existing node for the given @locator.
  215. /// </summary>
  216. public Node LookupNode (string c_caption, string c_element)
  217. {
  218. if (nodes == null)
  219. return CreateNode (c_caption, c_element);
  220. foreach (Node n in nodes){
  221. if (n.element == c_element)
  222. return n;
  223. }
  224. return CreateNode (c_caption, c_element);
  225. }
  226. public void EnsureNodes ()
  227. {
  228. if (nodes == null)
  229. nodes = new ArrayList ();
  230. }
  231. public bool IsLeaf {
  232. get {
  233. return nodes == null;
  234. }
  235. }
  236. void EncodeInt (BinaryWriter writer, int value)
  237. {
  238. do {
  239. int high = (value >> 7) & 0x01ffffff;
  240. byte b = (byte)(value & 0x7f);
  241. if (high != 0) {
  242. b = (byte)(b | 0x80);
  243. }
  244. writer.Write(b);
  245. value = high;
  246. } while(value != 0);
  247. }
  248. int DecodeInt (BinaryReader reader)
  249. {
  250. int ret = 0;
  251. int shift = 0;
  252. byte b;
  253. do {
  254. b = reader.ReadByte();
  255. ret = ret | ((b & 0x7f) << shift);
  256. shift += 7;
  257. } while ((b & 0x80) == 0x80);
  258. return ret;
  259. }
  260. internal void Dump (FileStream output, BinaryWriter writer)
  261. {
  262. if (nodes != null){
  263. foreach (Node child in nodes){
  264. child.Dump (output, writer);
  265. }
  266. }
  267. position = (int) output.Position;
  268. EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
  269. writer.Write (element);
  270. writer.Write (caption);
  271. if (nodes != null){
  272. foreach (Node child in nodes){
  273. EncodeInt (writer, child.position);
  274. }
  275. }
  276. }
  277. static int indent;
  278. static void Indent ()
  279. {
  280. for (int i = 0; i < indent; i++)
  281. Console.Write (" ");
  282. }
  283. public static void PrintTree (Node node)
  284. {
  285. Indent ();
  286. Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
  287. if (node.Nodes.Count == 0)
  288. return;
  289. indent++;
  290. foreach (Node n in node.Nodes)
  291. PrintTree (n);
  292. indent--;
  293. }
  294. public void Sort ()
  295. {
  296. if (nodes != null)
  297. nodes.Sort ();
  298. }
  299. public string URL {
  300. get {
  301. if (position < 0)
  302. LoadNode ();
  303. if (element.IndexOf (":") >= 0)
  304. return element;
  305. if (parent != null){
  306. string url = parent.URL;
  307. if (url.EndsWith ("/"))
  308. return url + element;
  309. else
  310. return parent.URL + "/" + element;
  311. } else
  312. return element;
  313. }
  314. }
  315. public string PublicUrl {
  316. get {
  317. return tree.HelpSource != null
  318. ? tree.HelpSource.GetPublicUrl (URL)
  319. : URL;
  320. }
  321. }
  322. int IComparable.CompareTo (object obj)
  323. {
  324. Node other = obj as Node;
  325. if (other == null)
  326. return -1;
  327. if (position < 0)
  328. LoadNode ();
  329. if (other.position < 0)
  330. other.LoadNode ();
  331. Regex digits = new Regex (@"([\d]+)|([^\d]+)");
  332. MatchEvaluator eval = delegate (Match m) {
  333. return (m.Value.Length > 0 && char.IsDigit (m.Value [0]))
  334. ? m.Value.PadLeft (System.Math.Max (caption.Length, other.caption.Length))
  335. : m.Value;
  336. };
  337. return digits.Replace (caption, eval).CompareTo (digits.Replace (other.caption, eval));
  338. }
  339. }
  340. //
  341. // The HelpSource class keeps track of the archived data, and its
  342. // tree
  343. //
  344. public class HelpSource {
  345. static int id;
  346. public static bool use_css = false;
  347. public static string css_code;
  348. public static string CssCode {
  349. get {
  350. if (css_code != null)
  351. return css_code;
  352. System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
  353. Stream str_css = assembly.GetManifestResourceStream ("base.css");
  354. StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
  355. sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
  356. sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
  357. css_code = sb.ToString ();
  358. return css_code;
  359. }
  360. set { css_code = value; }
  361. }
  362. public virtual string InlineCss {
  363. get { return CssCode; }
  364. }
  365. public virtual string InlineJavaScript {
  366. get { return null; }
  367. }
  368. public static bool FullHtml = true;
  369. // should only be enabled by ASP.NET webdoc
  370. public static bool UseWebdocCache;
  371. //
  372. // The unique ID for this HelpSource.
  373. //
  374. int source_id;
  375. DateTime zipFileWriteTime;
  376. string name;
  377. TraceLevel trace_level = TraceLevel.Warning;
  378. protected bool nozip;
  379. protected string base_dir;
  380. public HelpSource (string base_filename, bool create)
  381. {
  382. this.name = Path.GetFileName (base_filename);
  383. tree_filename = base_filename + ".tree";
  384. zip_filename = base_filename + ".zip";
  385. base_dir = XmlDocUtils.GetCacheDirectory (base_filename);
  386. if (UseWebdocCache && !create && Directory.Exists (base_dir)) {
  387. nozip = true;
  388. }
  389. if (create)
  390. SetupForOutput ();
  391. else
  392. Tree = new Tree (this, tree_filename);
  393. source_id = id++;
  394. try {
  395. FileInfo fi = new FileInfo (zip_filename);
  396. zipFileWriteTime = fi.LastWriteTime;
  397. } catch {
  398. zipFileWriteTime = DateTime.Now;
  399. }
  400. }
  401. public HelpSource() {
  402. Tree = new Tree (this, "Blah", "Blah");
  403. source_id = id++;
  404. }
  405. public DateTime ZipFileWriteTime {
  406. get {
  407. return zipFileWriteTime;
  408. }
  409. }
  410. public int SourceID {
  411. get {
  412. return source_id;
  413. }
  414. }
  415. public string Name {
  416. get {
  417. return name;
  418. }
  419. }
  420. public TraceLevel TraceLevel {
  421. get { return trace_level; }
  422. set { trace_level = value; }
  423. }
  424. ZipFile zip_file;
  425. /// <summary>
  426. /// Returns a stream from the packaged help source archive
  427. /// </summary>
  428. public virtual Stream GetHelpStream (string id)
  429. {
  430. if (nozip) {
  431. string path = XmlDocUtils.GetCachedFileName (base_dir, id);
  432. if (File.Exists (path))
  433. return File.OpenRead (path);
  434. return null;
  435. }
  436. if (zip_file == null)
  437. zip_file = new ZipFile (zip_filename);
  438. ZipEntry entry = zip_file.GetEntry (id);
  439. if (entry != null)
  440. return zip_file.GetInputStream (entry);
  441. return null;
  442. }
  443. public string GetRealPath (string file)
  444. {
  445. if (zip_file == null)
  446. zip_file = new ZipFile (zip_filename);
  447. ZipEntry entry = zip_file.GetEntry (file);
  448. if (entry != null && entry.ExtraData != null)
  449. return ConvertToString (entry.ExtraData);
  450. return null;
  451. }
  452. public XmlReader GetHelpXml (string id)
  453. {
  454. if (nozip) {
  455. Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
  456. string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
  457. return new XmlTextReader (url, s);
  458. }
  459. if (zip_file == null)
  460. zip_file = new ZipFile (zip_filename);
  461. ZipEntry entry = zip_file.GetEntry (id);
  462. if (entry != null) {
  463. Stream s = zip_file.GetInputStream (entry);
  464. string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
  465. return new XmlTextReader (url, s);
  466. }
  467. return null;
  468. }
  469. public virtual XmlDocument GetHelpXmlWithChanges (string id)
  470. {
  471. if (nozip) {
  472. Stream s = File.OpenRead (XmlDocUtils.GetCachedFileName (base_dir, id));
  473. string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
  474. XmlReader r = new XmlTextReader (url, s);
  475. XmlDocument ret = new XmlDocument ();
  476. ret.Load (r);
  477. return ret;
  478. }
  479. if (zip_file == null)
  480. zip_file = new ZipFile (zip_filename);
  481. ZipEntry entry = zip_file.GetEntry (id);
  482. if (entry != null) {
  483. Stream s = zip_file.GetInputStream (entry);
  484. string url = "monodoc:///" + SourceID + "@" + System.Web.HttpUtility.UrlEncode (id) + "@";
  485. XmlReader r = new XmlTextReader (url, s);
  486. XmlDocument ret = new XmlDocument ();
  487. ret.Load (r);
  488. if (entry.ExtraData != null)
  489. EditingUtils.AccountForChanges (ret, Name, ConvertToString (entry.ExtraData));
  490. return ret;
  491. }
  492. return null;
  493. }
  494. /// <summary>
  495. /// Get a nice, unique expression for any XPath node that you get.
  496. /// This function is used by editing to get the expression to put
  497. /// on to the file. The idea is to create an expression that is resistant
  498. /// to changes in the structure of the XML.
  499. /// </summary>
  500. public virtual string GetNodeXPath (XPathNavigator n)
  501. {
  502. return EditingUtils.GetXPath (n.Clone ());
  503. }
  504. public string GetEditUri (XPathNavigator n)
  505. {
  506. return EditingUtils.FormatEditUri (n.BaseURI, GetNodeXPath (n));
  507. }
  508. static string ConvertToString (byte[] data)
  509. {
  510. return Encoding.UTF8.GetString(data);
  511. }
  512. static byte[] ConvertToArray (string str)
  513. {
  514. return Encoding.UTF8.GetBytes(str);
  515. }
  516. /// <summary>
  517. /// The tree that is being populated
  518. /// </summary>
  519. public Tree Tree;
  520. public RootTree RootTree;
  521. // Base filename used by this HelpSource.
  522. string tree_filename, zip_filename;
  523. // Used for ziping.
  524. const int buffer_size = 65536;
  525. ZipOutputStream zip_output;
  526. byte [] buffer;
  527. HelpSource (string base_filename)
  528. {
  529. }
  530. void SetupForOutput ()
  531. {
  532. Tree = new Tree (this, "", "");
  533. FileStream stream = File.Create (zip_filename);
  534. zip_output = new ZipOutputStream (stream);
  535. zip_output.SetLevel (9);
  536. buffer = new byte [buffer_size];
  537. }
  538. /// <summary>
  539. /// Saves the tree and the archive
  540. /// </summary>
  541. public void Save ()
  542. {
  543. Tree.Save (tree_filename);
  544. zip_output.Finish ();
  545. zip_output.Close ();
  546. }
  547. int code;
  548. string GetNewCode ()
  549. {
  550. return String.Format ("{0}", code++);
  551. }
  552. /// <summary>
  553. /// Providers call this to store a file they will need, and the return value
  554. /// is the name that was assigned to it
  555. /// </summary>
  556. public string PackFile (string file)
  557. {
  558. string entry_name = GetNewCode ();
  559. return PackFile (file, entry_name);
  560. }
  561. public string PackFile (string file, string entry_name)
  562. {
  563. using (FileStream input = File.OpenRead (file)) {
  564. PackStream (input, entry_name, file);
  565. }
  566. return entry_name;
  567. }
  568. public void PackStream (Stream s, string entry_name)
  569. {
  570. PackStream (s, entry_name, null);
  571. }
  572. void PackStream (Stream s, string entry_name, string realPath)
  573. {
  574. ZipEntry entry = new ZipEntry (entry_name);
  575. if (realPath != null)
  576. entry.ExtraData = ConvertToArray (realPath);
  577. zip_output.PutNextEntry (entry);
  578. int n;
  579. while ((n = s.Read (buffer, 0, buffer_size)) > 0){
  580. zip_output.Write (buffer, 0, n);
  581. }
  582. }
  583. public void PackXml (string fname, XmlDocument doc, string real_path)
  584. {
  585. ZipEntry entry = new ZipEntry (fname);
  586. if (real_path != null)
  587. entry.ExtraData = ConvertToArray(real_path);
  588. zip_output.PutNextEntry (entry);
  589. XmlTextWriter xmlWriter = new XmlTextWriter (zip_output, Encoding.UTF8);
  590. doc.WriteContentTo (xmlWriter);
  591. xmlWriter.Flush ();
  592. }
  593. public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
  594. {
  595. throw new NotImplementedException ();
  596. }
  597. public virtual string GetPublicUrl (string id)
  598. {
  599. return id;
  600. }
  601. public virtual string GetText (string url, out Node n)
  602. {
  603. n = null;
  604. return null;
  605. }
  606. protected string GetCachedText (string url)
  607. {
  608. if (!nozip)
  609. return null;
  610. string file = XmlDocUtils.GetCachedFileName (base_dir, url);
  611. if (!File.Exists (file))
  612. return null;
  613. return File.OpenText (file).ReadToEnd ();
  614. }
  615. public virtual Stream GetImage (string url)
  616. {
  617. return null;
  618. }
  619. //
  620. // Default method implementation does not satisfy the request
  621. //
  622. public virtual string RenderTypeLookup (string prefix, string ns, string type, string member, out Node n)
  623. {
  624. n = null;
  625. return null;
  626. }
  627. public virtual string RenderNamespaceLookup (string nsurl, out Node n)
  628. {
  629. n = null;
  630. return null;
  631. }
  632. //
  633. // Populates the index.
  634. //
  635. public virtual void PopulateIndex (IndexMaker index_maker)
  636. {
  637. }
  638. //
  639. // Build an html document
  640. //
  641. public static string BuildHtml (string css, string html_code)
  642. {
  643. return BuildHtml (css, null, html_code);
  644. }
  645. internal static string BuildHtml (string css, string js, string html_code) {
  646. if (!FullHtml) {
  647. return html_code;
  648. }
  649. StringWriter output = new StringWriter ();
  650. output.Write ("<html><head>");
  651. output.Write ("<style type=\"text/css\">");
  652. output.Write (CssCode);
  653. output.Write (css);
  654. output.Write ("</style>");
  655. System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
  656. Stream str_js = assembly.GetManifestResourceStream ("helper.js");
  657. StringBuilder sb = new StringBuilder ((new StreamReader (str_js)).ReadToEnd());
  658. output.Write ("<script type=\"text/JavaScript\">\n");
  659. output.Write (sb.ToString ());
  660. output.Write ("</script>\n");
  661. if (js != null) {
  662. output.Write ("<script type=\"text/JavaScript\">\n");
  663. output.Write (js);
  664. output.Write ("\n</script>");
  665. }
  666. output.Write ("</head><body>");
  667. output.Write (html_code);
  668. output.Write ("</body></html>");
  669. return output.ToString ();
  670. }
  671. //
  672. // Create different Documents for adding to Lucene search index
  673. // The default action is do nothing. Subclasses should add the docs
  674. //
  675. public virtual void PopulateSearchableIndex (IndexWriter writer) {
  676. return;
  677. }
  678. public void Message (TraceLevel level, string format, params object[] args)
  679. {
  680. if ((int) level <= (int) trace_level)
  681. Console.WriteLine (format, args);
  682. }
  683. public void Error (string format, params object[] args)
  684. {
  685. Console.Error.WriteLine (format, args);
  686. }
  687. }
  688. public abstract class Provider {
  689. //
  690. // This code is used to "tag" all the different sources
  691. //
  692. static short serial;
  693. public int code;
  694. public Provider ()
  695. {
  696. code = serial++;
  697. }
  698. public abstract void PopulateTree (Tree tree);
  699. //
  700. // Called at shutdown time after the tree has been populated to perform
  701. // any fixups or final tasks.
  702. //
  703. public abstract void CloseTree (HelpSource hs, Tree tree);
  704. }
  705. public class RootTree : Tree {
  706. string basedir;
  707. public static ArrayList UncompiledHelpSources = new ArrayList();
  708. public const int MonodocVersion = 1;
  709. public static RootTree LoadTree ()
  710. {
  711. return LoadTree (null);
  712. }
  713. //
  714. // Loads the tree layout
  715. //
  716. public static RootTree LoadTree (string basedir)
  717. {
  718. if (basedir == null) {
  719. string myPath = System.Reflection.Assembly.GetExecutingAssembly ().Location;
  720. string cfgFile = myPath + ".config";
  721. if (!File.Exists (cfgFile)) {
  722. basedir = ".";
  723. }
  724. else {
  725. XmlDocument d = new XmlDocument ();
  726. d.Load (cfgFile);
  727. basedir = d.SelectSingleNode ("config/path").Attributes ["docsPath"].Value;
  728. }
  729. }
  730. //
  731. // Load the layout
  732. //
  733. XmlDocument doc = new XmlDocument ();
  734. string layout = Path.Combine (basedir, "monodoc.xml");
  735. doc.Load (layout);
  736. return LoadTree (basedir, doc,
  737. Where (Directory.GetFiles (Path.Combine (basedir, "sources")),
  738. delegate (object file) {return file.ToString ().EndsWith (".source");}));
  739. }
  740. delegate bool WherePredicate (object o);
  741. static IEnumerable Where (IEnumerable source, WherePredicate predicate)
  742. {
  743. foreach (var e in source)
  744. if (predicate (e))
  745. yield return e;
  746. }
  747. public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable/*<string>*/ sourceFiles)
  748. {
  749. if (docTree == null) {
  750. docTree = new XmlDocument ();
  751. using (var defTree = typeof(RootTree).Assembly.GetManifestResourceStream ("monodoc.xml"))
  752. docTree.Load (defTree);
  753. }
  754. sourceFiles = sourceFiles ?? new string [0];
  755. //
  756. // Load the layout
  757. //
  758. RootTree root = new RootTree ();
  759. root.basedir = indexDir;
  760. XmlNodeList nodes = docTree.SelectNodes ("/node/node");
  761. root.name_to_node ["root"] = root;
  762. root.name_to_node ["libraries"] = root;
  763. root.Populate (root, nodes);
  764. Node third_party = root.LookupEntryPoint ("various");
  765. if (third_party == null) {
  766. Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
  767. third_party = root;
  768. }
  769. //
  770. // Load the sources
  771. //
  772. foreach (string sourceFile in sourceFiles)
  773. root.AddSourceFile (sourceFile);
  774. foreach (string path in UncompiledHelpSources) {
  775. EcmaUncompiledHelpSource hs = new EcmaUncompiledHelpSource(path);
  776. hs.RootTree = root;
  777. root.help_sources.Add (hs);
  778. string epath = "extra-help-source-" + hs.Name;
  779. Node hsn = root.CreateNode (hs.Name, "root:/" + epath);
  780. root.name_to_hs [epath] = hs;
  781. hsn.EnsureNodes ();
  782. foreach (Node n in hs.Tree.Nodes){
  783. hsn.AddNode (n);
  784. }
  785. }
  786. // Clean the tree
  787. PurgeNode(root);
  788. root.Sort ();
  789. return root;
  790. }
  791. public void AddSource (string sources_dir)
  792. {
  793. string [] files = Directory.GetFiles (sources_dir);
  794. foreach (string file in files){
  795. if (!file.EndsWith (".source"))
  796. continue;
  797. AddSourceFile (file);
  798. }
  799. }
  800. public void AddSourceFile (string sourceFile)
  801. {
  802. Node third_party = LookupEntryPoint ("various") ?? this;
  803. XmlDocument doc = new XmlDocument ();
  804. try {
  805. doc.Load (sourceFile);
  806. }
  807. catch {
  808. Console.Error.WriteLine ("Error: Could not load source file {0}", sourceFile);
  809. return;
  810. }
  811. XmlNodeList extra_nodes = doc.SelectNodes ("/monodoc/node");
  812. if (extra_nodes.Count > 0)
  813. Populate (third_party, extra_nodes);
  814. XmlNodeList sources = doc.SelectNodes ("/monodoc/source");
  815. if (sources == null){
  816. Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
  817. return;
  818. }
  819. foreach (XmlNode source in sources){
  820. XmlAttribute a = source.Attributes ["provider"];
  821. if (a == null){
  822. Console.Error.WriteLine ("Error: no provider in <source>");
  823. continue;
  824. }
  825. string provider = a.InnerText;
  826. a = source.Attributes ["basefile"];
  827. if (a == null){
  828. Console.Error.WriteLine ("Error: no basefile in <source>");
  829. continue;
  830. }
  831. string basefile = a.InnerText;
  832. a = source.Attributes ["path"];
  833. if (a == null){
  834. Console.Error.WriteLine ("Error: no path in <source>");
  835. continue;
  836. }
  837. string path = a.InnerText;
  838. string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
  839. HelpSource hs = GetHelpSource (provider, basefilepath);
  840. if (hs == null)
  841. continue;
  842. hs.RootTree = this;
  843. help_sources.Add (hs);
  844. name_to_hs [path] = hs;
  845. Node parent = LookupEntryPoint (path);
  846. if (parent == null){
  847. Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
  848. parent = third_party;
  849. }
  850. foreach (Node n in hs.Tree.Nodes){
  851. parent.AddNode (n);
  852. }
  853. parent.Sort ();
  854. }
  855. }
  856. // Delete nodes which does not have documentaiton (source)
  857. static bool PurgeNode(Node node)
  858. {
  859. bool purge = false;
  860. if (!node.Documented)
  861. {
  862. ArrayList del_child = new ArrayList();
  863. //Delete node unless any child has documentation
  864. bool purged_child = false;
  865. foreach (Node child in node.Nodes)
  866. {
  867. purged_child = PurgeNode(child);
  868. if (purged_child)
  869. {
  870. del_child.Add(child);
  871. }
  872. }
  873. // delete the node if all its children are to be deleted
  874. purge = (node.Nodes.Count == del_child.Count);
  875. // delete children
  876. foreach (Node child in del_child)
  877. {
  878. node.DelNode(child);
  879. }
  880. }
  881. return purge;
  882. }
  883. public static string[] GetSupportedFormats ()
  884. {
  885. return new string[]{
  886. "ecma",
  887. "ecmaspec",
  888. "error",
  889. "hb",
  890. "man",
  891. "simple",
  892. "xhtml"
  893. };
  894. }
  895. public static HelpSource GetHelpSource (string provider, string basefilepath)
  896. {
  897. try {
  898. switch (provider){
  899. case "ecma":
  900. return new EcmaHelpSource (basefilepath, false);
  901. case "ecma-uncompiled":
  902. return new EcmaUncompiledHelpSource (basefilepath);
  903. case "monohb":
  904. return new MonoHBHelpSource(basefilepath, false);
  905. case "xhtml": case "hb":
  906. return new XhtmlHelpSource (basefilepath, false);
  907. case "man":
  908. return new ManHelpSource (basefilepath, false);
  909. case "simple":
  910. return new SimpleHelpSource (basefilepath, false);
  911. case "error":
  912. return new ErrorHelpSource (basefilepath, false);
  913. case "ecmaspec":
  914. return new EcmaSpecHelpSource (basefilepath, false);
  915. case "addins":
  916. return new AddinsHelpSource (basefilepath, false);
  917. default:
  918. Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
  919. break;
  920. }
  921. return null;
  922. }
  923. catch (FileNotFoundException) {
  924. Console.Error.WriteLine ("Error: did not find one of the files in sources/"+basefilepath);
  925. return null;
  926. }
  927. }
  928. public static Provider GetProvider (string provider, params string[] basefilepaths)
  929. {
  930. switch (provider) {
  931. case "addins":
  932. return new AddinsProvider (basefilepaths [0]);
  933. case "ecma": {
  934. EcmaProvider p = new EcmaProvider ();
  935. foreach (string d in basefilepaths)
  936. p.AddDirectory (d);
  937. return p;
  938. }
  939. case "ecmaspec":
  940. return new EcmaSpecProvider (basefilepaths [0]);
  941. case "error":
  942. return new ErrorProvider (basefilepaths [0]);
  943. case "man":
  944. return new ManProvider (basefilepaths);
  945. case "simple":
  946. return new SimpleProvider (basefilepaths [0]);
  947. case "xhtml":
  948. case "hb":
  949. return new XhtmlProvider (basefilepaths [0]);
  950. default:
  951. throw new NotSupportedException (provider);
  952. }
  953. }
  954. //
  955. // Maintains the name to node mapping
  956. //
  957. Hashtable name_to_node = new Hashtable ();
  958. Hashtable name_to_hs = new Hashtable ();
  959. void Populate (Node parent, XmlNodeList xml_node_list)
  960. {
  961. foreach (XmlNode xml_node in xml_node_list){
  962. XmlAttribute e = xml_node.Attributes ["parent"];
  963. if (e != null && name_to_node.ContainsKey (e.InnerText)) {
  964. Node p = (Node) name_to_node [e.InnerText];
  965. xml_node.Attributes.Remove (e);
  966. Populate (p, xml_node.SelectNodes ("."));
  967. continue;
  968. }
  969. e = xml_node.Attributes ["label"];
  970. if (e == null){
  971. Console.Error.WriteLine ("`label' attribute missing in <node>");
  972. continue;
  973. }
  974. string label = e.InnerText;
  975. e = xml_node.Attributes ["name"];
  976. if (e == null){
  977. Console.Error.WriteLine ("`name' attribute missing in <node>");
  978. continue;
  979. }
  980. string name = e.InnerText;
  981. Node n = parent.LookupNode (label, "root:/" + name);
  982. n.EnsureNodes ();
  983. name_to_node [name] = n;
  984. XmlNodeList children = xml_node.SelectNodes ("./node");
  985. if (children != null)
  986. Populate (n, children);
  987. }
  988. }
  989. public Node LookupEntryPoint (string name)
  990. {
  991. return (Node) name_to_node [name];
  992. }
  993. ArrayList help_sources;
  994. DateTime lastHelpSourceTime;
  995. RootTree () : base (null, "Mono Documentation", "root:")
  996. {
  997. nodes = new ArrayList ();
  998. help_sources = new ArrayList ();
  999. lastHelpSourceTime = DateTime.MinValue;
  1000. }
  1001. public DateTime LastHelpSourceTime {
  1002. get {
  1003. return lastHelpSourceTime;
  1004. }
  1005. }
  1006. public static bool GetNamespaceAndType (string url, out string ns, out string type)
  1007. {
  1008. int nsidx = -1;
  1009. int numLt = 0;
  1010. for (int i = 0; i < url.Length; ++i) {
  1011. char c = url [i];
  1012. switch (c) {
  1013. case '<':
  1014. case '{':
  1015. ++numLt;
  1016. break;
  1017. case '>':
  1018. case '}':
  1019. --numLt;
  1020. break;
  1021. case '.':
  1022. if (numLt == 0)
  1023. nsidx = i;
  1024. break;
  1025. }
  1026. }
  1027. if (nsidx == -1) {
  1028. Console.Error.WriteLine ("Did not find dot in: " + url);
  1029. ns = null;
  1030. type = null;
  1031. return false;
  1032. }
  1033. ns = url.Substring (0, nsidx);
  1034. type = url.Substring (nsidx + 1);
  1035. //Console.Error.WriteLine ("GetNameSpaceAndType (ns={0}, type={1}", ns, type);
  1036. return true;
  1037. }
  1038. public XmlDocument GetHelpXml (string url)
  1039. {
  1040. string rest = url.Substring (2);
  1041. string ns, type;
  1042. if (!GetNamespaceAndType (rest, out ns, out type))
  1043. return null;
  1044. foreach (HelpSource hs in help_sources) {
  1045. EcmaHelpSource ehs = hs as EcmaHelpSource;
  1046. if (ehs == null)
  1047. continue;
  1048. string id = ehs.GetIdFromUrl ("T:", ns, type);
  1049. if (id == null)
  1050. continue;
  1051. XmlDocument doc = hs.GetHelpXmlWithChanges (id);
  1052. if (doc != null)
  1053. return doc;
  1054. }
  1055. return null;
  1056. }
  1057. public string TypeLookup (string url, out Node match_node)
  1058. {
  1059. string rest = Regex.Replace (url, @"^T:\s*", "");
  1060. string ns, type;
  1061. if (!GetNamespaceAndType (rest, out ns, out type)){
  1062. match_node = null;
  1063. return null;
  1064. }
  1065. foreach (HelpSource hs in help_sources){
  1066. string s = hs.RenderTypeLookup ("T:", ns, type, null, out match_node);
  1067. if (s != null) {
  1068. lastHelpSourceTime = hs.ZipFileWriteTime;
  1069. return s;
  1070. }
  1071. }
  1072. match_node = null;
  1073. return null;
  1074. }
  1075. public string MemberLookup (string prefix, string url, out Node match_node)
  1076. {
  1077. string rest = Regex.Replace (url, @"^.:\s*", "");
  1078. // Dots in the arg list (for methods) confuse this.
  1079. // Chop off the arg list for now and put it back later.
  1080. string arglist = "";
  1081. int argliststart = rest.IndexOf("(");
  1082. if (argliststart >= 0) {
  1083. arglist = rest.Substring(argliststart);
  1084. rest = rest.Substring(0, argliststart);
  1085. }
  1086. string ns_type, member;
  1087. if (prefix != "C:") {
  1088. int member_idx = rest.LastIndexOf (".");
  1089. // The dot in .ctor (if it's a M: link) would confuse this.
  1090. if (rest.EndsWith("..ctor")) member_idx--;
  1091. ns_type = rest.Substring (0, member_idx);
  1092. member = rest.Substring (member_idx + 1);
  1093. } else {
  1094. // C: links don't have the .ctor member part as it would in a M: link
  1095. // Even though externally C: links are different from M: links,
  1096. // C: links get transformed into M:-style links (with .ctor) here.
  1097. ns_type = rest;
  1098. member = ".ctor";
  1099. }
  1100. //Console.WriteLine ("NS_TYPE: {0} MEMBER: {1}", ns_type, member);
  1101. string ns, type;
  1102. if (!GetNamespaceAndType (ns_type, out ns, out type)){
  1103. match_node = null;
  1104. return null;
  1105. }
  1106. foreach (HelpSource hs in help_sources){
  1107. string s = hs.RenderTypeLookup (prefix, ns, type, member + arglist, out match_node);
  1108. if (s != null) {
  1109. lastHelpSourceTime = hs.ZipFileWriteTime;
  1110. return s;
  1111. }
  1112. }
  1113. match_node = null;
  1114. return null;
  1115. }
  1116. public Stream GetImage (string url)
  1117. {
  1118. if (url.StartsWith ("source-id:")){
  1119. string rest = url.Substring (10);
  1120. int p = rest.IndexOf (":");
  1121. string str_idx = rest.Substring (0, p);
  1122. int idx = 0;
  1123. try {
  1124. idx = Int32.Parse (str_idx);
  1125. } catch {
  1126. Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
  1127. return null;
  1128. }
  1129. HelpSource hs = GetHelpSourceFromId (idx);
  1130. lastHelpSourceTime = hs.ZipFileWriteTime;
  1131. return hs.GetImage (rest.Substring (p + 1));
  1132. } else {
  1133. System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof(RootTree));
  1134. return assembly.GetManifestResourceStream (url);
  1135. }
  1136. lastHelpSourceTime = DateTime.MinValue;
  1137. return null;
  1138. }
  1139. public HelpSource GetHelpSourceFromId (int id)
  1140. {
  1141. return (HelpSource) help_sources [id];
  1142. }
  1143. string home_cache;
  1144. /// <summary>
  1145. /// Allows every HelpSource to try to provide the content for this
  1146. /// URL.
  1147. /// </summary>
  1148. public string RenderUrl (string url, out Node match_node)
  1149. {
  1150. lastHelpSourceTime = DateTime.MinValue;
  1151. if (url == "root:") {
  1152. match_node = this;
  1153. // look whether there are contribs
  1154. GlobalChangeset chgs = EditingUtils.changes;
  1155. StringBuilder con = new StringBuilder ();
  1156. //add links to the contrib
  1157. int oldContrib = 0, contribs = 0;
  1158. con.Append ("<ul>");
  1159. foreach (DocSetChangeset dscs in chgs.DocSetChangesets)
  1160. foreach (FileChangeset fcs in dscs.FileChangesets)
  1161. foreach (Change c in fcs.Changes) {
  1162. if (c.NodeUrl == null) {
  1163. if (c.Serial == SettingsHandler.Settings.SerialNumber)
  1164. oldContrib++;
  1165. } else if (c.Serial == SettingsHandler.Settings.SerialNumber) {
  1166. contribs++;
  1167. con.Append (String.Format ("<li><a href=\"{0}\">{0}</a></li>", c.NodeUrl));
  1168. }
  1169. }
  1170. string contrib = (oldContrib + contribs) == 1?"There is {0} contribution":"There are {0} contributions";
  1171. con.Insert (0, String.Format (contrib, oldContrib + contribs) + " pending upload <i>(Contributing--&gt; Upload)</i>", 1);
  1172. con.Append ("</ul>");
  1173. if (oldContrib == 1)
  1174. con.Append ("<i>You have 1 contribution that is not listed below that will be sent the next time you upload contributions. Only contributions made from now on will be listed.</i>");
  1175. else if (oldContrib > 1)
  1176. con.Append ("<i>You have " + oldContrib + "contributions that are not listed below and will be sent the next time you upload contributions. Only contributions made from now on will be listed.</i>");
  1177. //start the rendering
  1178. if (!HelpSource.use_css) {
  1179. StringBuilder sb = new StringBuilder ("<table bgcolor=\"#b0c4de\" width=\"100%\" cellpadding=\"5\"><tr><td><h3>Mono Documentation Library</h3></td></tr></table>");
  1180. foreach (Node n in Nodes)
  1181. sb.AppendFormat ("<a href='{0}'>{1}</a><br/>", n.Element, n.Caption);
  1182. //contributions
  1183. sb.Append ("<br><table bgcolor=\"#fff3f3\" width=\"100%\" cellpadding=\"5\"><tr><td>");
  1184. sb.Append ("<h5>Contributions</h5><br>");
  1185. if ((oldContrib + contribs) == 0) {
  1186. sb.Append ("<p><b>You have not made any contributions yet.</b></p>");
  1187. sb.Append ("<p>The Documentation of the libraries is not complete and your contributions would be greatly appreciated. The procedure is easy, browse to the part of the documentation you want to contribute to and click on the <font color=\"blue\">[Edit]</font> link to start writing the documentation.</p>");
  1188. sb.Append ("<p>When you are happy with your changes, use the Contributing--&gt; Upload Contributions menu to send your contributions to our server.</p></div>");
  1189. } else {
  1190. sb.Append (con.ToString ());
  1191. }
  1192. sb.Append ("</td></tr></table>");
  1193. return sb.ToString ();
  1194. } else {
  1195. if (home_cache == null) {
  1196. System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HelpSource));
  1197. Stream hp_stream = assembly.GetManifestResourceStream ("home.html");
  1198. home_cache = (new StreamReader (hp_stream)).ReadToEnd ();
  1199. }
  1200. StringBuilder sb = new StringBuilder (home_cache);
  1201. // adjust fonts
  1202. sb.Replace ("@@FONT_FAMILY@@", SettingsHandler.Settings.preferred_font_family);
  1203. sb.Replace ("@@FONT_SIZE@@", SettingsHandler.Settings.preferred_font_size.ToString());
  1204. //contributions
  1205. if ((oldContrib + contribs) == 0) {
  1206. sb.Replace ("@@CONTRIB_DISP@@", "display: none;");
  1207. } else {
  1208. sb.Replace ("@@NO_CONTRIB_DISP@@", "display: none;");
  1209. sb.Replace ("@@CONTRIBS@@", con.ToString ());
  1210. }
  1211. // load the url of nodes
  1212. String add_str;
  1213. StringBuilder urls = new StringBuilder ();
  1214. foreach (Node n in Nodes) {
  1215. add_str = String.Format ("<li><a href=\"{0}\">{1}</a></li>", n.Element, n.Caption);
  1216. urls.Append (add_str);
  1217. }
  1218. sb.Replace ("@@API_DOCS@@", urls.ToString ());
  1219. return sb.ToString ();
  1220. }
  1221. }
  1222. if (url.StartsWith ("root:")) {
  1223. match_node = ((Node)name_to_node [url.Substring (6)]);
  1224. HelpSource hs = ((HelpSource)name_to_hs [url.Substring (6)]);
  1225. if (hs == null)
  1226. {
  1227. return GenerateNodeIndex(match_node);
  1228. }
  1229. Node dummy;
  1230. lastHelpSourceTime = hs.ZipFileWriteTime;
  1231. return hs.GetText ("root:", out dummy);
  1232. }
  1233. if (url.StartsWith ("source-id:")){
  1234. string rest = url.Substring (10);
  1235. int p = rest.IndexOf (":");
  1236. string str_idx = rest.Substring (0, p);
  1237. int idx = 0;
  1238. try {
  1239. idx = Int32.Parse (str_idx);
  1240. } catch {
  1241. Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, str_idx);
  1242. match_node = null;
  1243. return null;
  1244. }
  1245. HelpSource hs = (HelpSource) help_sources [idx];
  1246. // Console.WriteLine ("Attempting to get docs from: " + rest.Substring (p + 1));
  1247. lastHelpSourceTime = hs.ZipFileWriteTime;
  1248. return hs.GetText (rest.Substring (p + 1), out match_node);
  1249. }
  1250. if (url.Length < 2){
  1251. match_node = null;
  1252. return null;
  1253. }
  1254. string prefix = url.Substring (0, 2);
  1255. switch (prefix.ToUpper ()){
  1256. case "N:":
  1257. foreach (HelpSource hs in help_sources){
  1258. string s = hs.RenderNamespaceLookup (url, out match_node);
  1259. if (s != null) {
  1260. lastHelpSourceTime = hs.ZipFileWriteTime;
  1261. return s;
  1262. }
  1263. }
  1264. match_node = null;
  1265. return null;
  1266. case "T:":
  1267. return TypeLookup (url, out match_node);
  1268. case "M:":
  1269. case "F:":
  1270. case "P:":
  1271. case "E:":
  1272. case "C:":
  1273. case "O:":
  1274. return MemberLookup (prefix, url, out match_node);
  1275. default:
  1276. foreach (HelpSource hs in help_sources){
  1277. string s = hs.GetText (url, out match_node);
  1278. if (s != null) {
  1279. lastHelpSourceTime = hs.ZipFileWriteTime;
  1280. return s;
  1281. }
  1282. }
  1283. match_node = null;
  1284. return null;
  1285. }
  1286. }
  1287. public string GenerateNodeIndex (Node node)
  1288. {
  1289. StringBuilder buf = new StringBuilder();
  1290. buf.AppendFormat("<H3>{0}</H3>", node.Caption);
  1291. buf.Append("<ul>");
  1292. foreach (Node child in node.Nodes)
  1293. {
  1294. buf.AppendFormat("<li><a href=\"{0}\">{1}</a>", child.URL, child.Caption);
  1295. }
  1296. buf.Append("</ul>");
  1297. return buf.ToString();
  1298. }
  1299. public IndexReader GetIndex ()
  1300. {
  1301. //try to load from basedir
  1302. string index_file = Path.Combine (basedir, "monodoc.index");
  1303. if (File.Exists (index_file))
  1304. return IndexReader.Load (index_file);
  1305. //then, try to load from config dir
  1306. index_file = Path.Combine (SettingsHandler.Path, "monodoc.index");
  1307. return IndexReader.Load (index_file);
  1308. }
  1309. public static void MakeIndex ()
  1310. {
  1311. RootTree root = LoadTree ();
  1312. if (root == null)
  1313. return;
  1314. IndexMaker index_maker = new IndexMaker ();
  1315. foreach (HelpSource hs in root.help_sources){
  1316. hs.PopulateIndex (index_maker);
  1317. }
  1318. // if the user has no write permissions use config dir
  1319. string path = Path.Combine (root.basedir, "monodoc.index");
  1320. try {
  1321. index_maker.Save (path);
  1322. } catch (System.UnauthorizedAccessException) {
  1323. path = Path.Combine (SettingsHandler.Path, "monodoc.index");
  1324. try {
  1325. index_maker.Save (path);
  1326. } catch (System.UnauthorizedAccessException) {
  1327. Console.WriteLine ("Unable to write index file in {0}", Path.Combine (SettingsHandler.Path, "monodoc.index"));
  1328. return;
  1329. }
  1330. }
  1331. if (IsUnix){
  1332. // No octal in C#, how lame is that
  1333. chmod (path, 0x1a4);
  1334. }
  1335. Console.WriteLine ("Documentation index updated");
  1336. }
  1337. static bool IsUnix {
  1338. get {
  1339. int p = (int) Environment.OSVersion.Platform;
  1340. return ((p == 4) || (p == 128) || (p == 6));
  1341. }
  1342. }
  1343. // Search Index
  1344. public SearchableIndex GetSearchIndex ()
  1345. {
  1346. //try to load from basedir
  1347. string index_file = Path.Combine (basedir, "search_index");
  1348. if (Directory.Exists (index_file))
  1349. return SearchableIndex.Load (index_file);
  1350. //then, try to load from config dir
  1351. index_file = Path.Combine (SettingsHandler.Path, "search_index");
  1352. return SearchableIndex.Load (index_file);
  1353. }
  1354. public static void MakeSearchIndex ()
  1355. {
  1356. // Loads the RootTree
  1357. Console.WriteLine ("Loading the monodoc tree...");
  1358. RootTree root = LoadTree ();
  1359. if (root == null)
  1360. return;
  1361. string dir = Path.Combine (root.basedir, "search_index");
  1362. IndexWriter writer;
  1363. //try to create the dir to store the index
  1364. try {
  1365. if (!Directory.Exists (dir))
  1366. Directory.CreateDirectory (dir);
  1367. writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
  1368. } catch (UnauthorizedAccessException) {
  1369. //try in the .config directory
  1370. try {
  1371. dir = Path.Combine (SettingsHandler.Path, "search_index");
  1372. if (!Directory.Exists (dir))
  1373. Directory.CreateDirectory (dir);
  1374. writer = new IndexWriter(Lucene.Net.Store.FSDirectory.GetDirectory(dir, true), new StandardAnalyzer(), true);
  1375. } catch (UnauthorizedAccessException) {
  1376. Console.WriteLine ("You don't have permissions to write on " + dir);
  1377. return;
  1378. }
  1379. }
  1380. //Collect all the documents
  1381. Console.WriteLine ("Collecting and adding documents...");
  1382. foreach (HelpSource hs in root.HelpSources)
  1383. hs.PopulateSearchableIndex (writer);
  1384. //Optimize and close
  1385. Console.WriteLine ("Closing...");
  1386. writer.Optimize();
  1387. writer.Close();
  1388. }
  1389. public ICollection HelpSources { get { return new ArrayList(help_sources); } }
  1390. [System.Runtime.InteropServices.DllImport ("libc")]
  1391. static extern int chmod (string filename, int mode);
  1392. }
  1393. }