Logo Search packages:      
Sourcecode: ndoc version File versions  Download package

ExternalXmlSummaryCache.cs

// Copyright (C) 2004  Kevin Downs
// Parts Copyright (c) 2002 Jean-Claude Manoli
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

using System;
using System.IO;
using System.Collections;
using System.Globalization;
using System.Xml;
using System.Reflection;
using Microsoft.Win32;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace NDoc.Core.Reflection
{
      /// <summary>
      /// Caches XML summaries.
      /// </summary>
00033       internal class ExternalXmlSummaryCache
      {
            private Hashtable cachedDocs;
            private Hashtable summaries;
            private string localizationLanguage;

            /// <summary>
            /// Initializes a new instance of the <see cref="ExternalXmlSummaryCache" />
            /// class.
            /// </summary>
00043             public ExternalXmlSummaryCache(string localizationLanguage)
            {
                  Flush();
                  this.localizationLanguage = localizationLanguage;
            }

            /// <summary>
            /// Flushes the <see cref="ExternalXmlSummaryCache" />.
            /// </summary>
00052             public void Flush()
            {
                  cachedDocs = new Hashtable();
                  summaries = new Hashtable();
            }

            /// <summary>
            /// Adds given XML document to the summary cache.
            /// </summary>
            /// <param name="xmlFileName">The filename of XML document to cache.</param>
00062             public void AddXmlDoc(string xmlFileName)
            {
                  int start = Environment.TickCount;

                  XmlTextReader reader = null;
                  try
                  {
                        reader = new XmlTextReader(xmlFileName);
                        CacheSummaries(reader);
                  }
                  finally
                  {
                        if (reader != null) reader.Close();
                  }
                  Debug.WriteLine("Cached doc : " + Path.GetFileName(xmlFileName) + " (" + ((Environment.TickCount - start) / 1000.0).ToString() + " sec.)");
            }

            /// <summary>
            /// Gets the xml documentation for the assembly of the specified type.
            /// </summary>
00082             public void GetXmlFor(Type type)
            {
                  string searchedDoc = (string)cachedDocs[type.Assembly.FullName];

                  if (searchedDoc == null)
                  {
                        //Debug.WriteLine("Attempting to locate XML docs for " + type.Assembly.FullName);
                        Type t = Type.GetType(type.AssemblyQualifiedName);
                        if (t != null)
                        {
                              Assembly a = t.Assembly;
                              string assemblyPath = a.Location;
                                    
                              if (assemblyPath.Length > 0)
                              {
                                    string docPath = Path.ChangeExtension(assemblyPath, ".xml");

                                    //if not found, try loading __AssemblyInfo__.ini
                                    if (!File.Exists(docPath))
                                    {
                                          string infoPath = Path.Combine(
                                                Path.GetDirectoryName(docPath), "__AssemblyInfo__.ini");
                                          docPath = null;

                                          if (File.Exists(infoPath))
                                          {
                                                //Debug.WriteLine("Loading __AssemblyInfo__.ini.");
                                                TextReader reader = new StreamReader(infoPath);
                                                string line;
                                                try
                                                {
                                                      while ((line = reader.ReadLine()) != null)
                                                      {
                                                            if (line.StartsWith("URL=file:///"))
                                                            {
                                                                  docPath = Path.ChangeExtension(line.Substring(12), ".xml");
                                                                  break;
                                                            }
                                                      }
                                                }
                                                finally
                                                {
                                                      reader.Close();
                                                }
                                          }
                                    }

#if (!MONO)
                                    //TODO: search in the mono lib folder, if they ever give us the xml documentation
                                    // If still not found, try locating the assembly in the Framework folder
                                    if ((docPath == null) || (!File.Exists(docPath)))
                                    {
#if (NET_1_0)
                                          FileVersionInfo version = FileVersionInfo.GetVersionInfo(assemblyPath);
                                          string stringVersion = string.Format(
                                                "v{0}.{1}.{2}", 
                                                version.FileMajorPart, 
                                                version.FileMinorPart, 
                                                version.FileBuildPart);
                                          string frameworkPath = this.GetDotnetFrameworkPath(stringVersion);
#else
                                          string frameworkPath = this.GetDotnetFrameworkPath(a.ImageRuntimeVersion);
#endif
                                          if (frameworkPath != null)
                                          {
                                                string localizedFrameworkPath = Path.Combine(frameworkPath, localizationLanguage);
                                                if (Directory.Exists(localizedFrameworkPath))
                                                {
                                                      docPath = Path.Combine(localizedFrameworkPath, a.GetName().Name + ".xml");
                                                }
                                                if ((docPath == null) || (!File.Exists(docPath)))
                                                {
                                                      docPath = Path.Combine(frameworkPath, a.GetName().Name + ".xml");
                                                }
                                          }
                                    }
#endif

                                    if ((docPath != null) && (File.Exists(docPath)))
                                    {
                                          Debug.WriteLine("Docs found : " + docPath);
                                          AddXmlDoc(docPath);
                                          searchedDoc = docPath;
                                    }
                              }
                        }


                        //if the doc was still not found, create an empty document filename
                        if (searchedDoc == null)
                        {
                              Trace.WriteLine("XML Doc not found for " + type.Assembly.FullName);
                              searchedDoc = "";
                        }
                        //cache the document path
                        cachedDocs.Add(type.Assembly.FullName, searchedDoc);
                  }
            }

            /// <summary>
            /// Caches summaries for all members in XML documentation file.
            /// </summary>
            /// <param name="reader">XmlTextReader for XML Documentation</param>
            /// <remarks>If a member does not have a summary, a zero-length string is stored instead.</remarks>
00186             private void CacheSummaries(XmlTextReader reader)
            {
                  object oMember = reader.NameTable.Add("member");
                  object oSummary = reader.NameTable.Add("summary");

                  reader.MoveToContent();

                  string MemberID = "";
                  string Summary = "";
                  while (reader.Read()) 
                  {
                        switch (reader.NodeType)
                        {
                              case XmlNodeType.Element : 

                                    if (reader.Name.Equals(oMember)) 
                                    {
                                          MemberID = reader.GetAttribute("name");
                                          Summary = "";
                                    }      
                                    if (reader.Name.Equals(oSummary)) 
                                    {
                                          Summary = reader.ReadInnerXml();
                                          Summary = PreprocessDoc(MemberID, Summary);
                                    }
                                    break;

                              case XmlNodeType.EndElement : 
 
                                    if (reader.Name.Equals(oMember)) 
                                    {
                                          if (!summaries.ContainsKey(MemberID))
                                          {
                                                summaries.Add(MemberID, Summary);
                                          }
                                    }
                                    break;

                              default : 
                                    break;
                        }
                  }
            }

            /// <summary>
            /// Preprocess documentation before placing it in the cache.
            /// </summary>
            /// <param name="id">Member name 'id' to which the docs belong</param>
            /// <param name="doc">A string containing the members documentation</param>
            /// <returns>processed doc string</returns>
00236             private string PreprocessDoc(string id, string doc)
            {
                  //create an XmlDocument containg the memeber's documentation
                  XmlTextReader reader=new XmlTextReader(new StringReader("<root>" + doc + "</root>"));
                  reader.WhitespaceHandling=WhitespaceHandling.All;
                  
                  XmlDocument xmldoc = new XmlDocument();
                  xmldoc.PreserveWhitespace=true;
                  xmldoc.Load(reader);
 
                  //
                  CleanupNodes(xmldoc.DocumentElement.ChildNodes);
                  //
                  ProcessSeeLinks(id, xmldoc.DocumentElement.ChildNodes);
                  return xmldoc.DocumentElement.InnerXml;
            }

            /// <summary>
            /// strip out redundant newlines and spaces from documentation.
            /// </summary>
            /// <param name="nodes">list of nodes</param>
00257             private void CleanupNodes(XmlNodeList nodes)
            {
                  foreach (XmlNode node in nodes)
                  {
                        if (node.NodeType == XmlNodeType.Element) 
                        {
                              CleanupNodes(node.ChildNodes);

                              // Trim attribute values...
                              foreach(XmlNode attr in node.Attributes)
                              {
                                    attr.Value=attr.Value.Trim();
                              }
                        }
                        if (node.NodeType == XmlNodeType.Text)
                        {
                              node.Value = ((string)node.Value).Replace("\t", "    ").Replace("\n", " ").Replace("\r", " ").Replace("        ", " ").Replace("    ", " ").Replace("   ", " ").Replace("  ", " ");
                        }
                  }
            }

            /// <summary>
            /// Add 'nolink' attributes to self referencing or duplicate see tags.
            /// </summary>
            /// <param name="id">current member name 'id'</param>
            /// <param name="nodes">list of top-level nodes</param>
            /// <remarks>
            /// </remarks>
00285             private void ProcessSeeLinks(string id, XmlNodeList nodes)
            {
                  foreach (XmlNode node in nodes)
                  {
                        if (node.NodeType == XmlNodeType.Element) 
                        {
                              Hashtable linkTable=null;
                              MarkupSeeLinks(ref linkTable, id, node);
                        }
                  }
            }

            /// <summary>
            /// Search tags for duplicate or self-referencing see links.
            /// </summary>
            /// <param name="linkTable">A table of previous links.</param>
            /// <param name="id">current member name 'id'</param>
            /// <param name="node">an Xml Node containing a doc tag</param>
00303             private void MarkupSeeLinks(ref Hashtable linkTable, string id, XmlNode node)
            {
                  if (node.LocalName=="see")
                  {
                        //we will only do this for crefs
                        XmlAttribute cref = node.Attributes["cref"];
                        if (cref !=null)
                        {
                              if (cref.Value==id) //self referencing tag
                              {
                                    XmlAttribute dup = node.OwnerDocument.CreateAttribute("nolink");
                                    dup.Value="true";
                                    node.Attributes.Append(dup);
                              }
                              else
                              {
                                    if (linkTable==null)
                                    {
                                          //assume an resonable initial table size,
                                          //so we don't have to resize often.
                                          linkTable = new Hashtable(16);
                                    }
                                    if (linkTable.ContainsKey(cref.Value))
                                    {
                                          XmlAttribute dup = node.OwnerDocument.CreateAttribute("nolink");
                                          dup.Value="true";
                                          node.Attributes.Append(dup);
                                    }
                                    else
                                    {
                                          linkTable.Add(cref.Value,null);
                                    }
                              }
                        }
                  }
                        
                  //search this tags' children
                  foreach (XmlNode childnode in node.ChildNodes)
                  {
                        if (childnode.NodeType == XmlNodeType.Element) 
                        {
                              MarkupSeeLinks(ref linkTable, id, childnode);
                        }
                  }
            }


            /// <summary>
            /// Returns the original summary for a member inherited from a specified type. 
            /// </summary>
            /// <param name="memberID">The member ID to lookup.</param>
            /// <param name="declaringType">The type that declares that member.</param>
            /// <returns>The summary xml.  If not found, returns an zero length string.</returns>
00356             public string GetSummary(string memberID, Type declaringType)
            {
                  //extract member type (T:, P:, etc.)
                  string memberType = memberID.Substring(0, 2);

                  //extract member name
                  int i = memberID.IndexOf('(');
                  string memberName;
                  if (i > -1)
                  {
                        memberName = memberID.Substring(memberID.LastIndexOf('.', i) + 1);
                  }
                  else
                  {
                        memberName = memberID.Substring(memberID.LastIndexOf('.') + 1);
                  }

                  //the member id in the declaring assembly
                  string key = memberType + declaringType.FullName.Replace("+", ".") + "." + memberName;

                  //check the summaries cache first
                  string summary = (string)summaries[key];

                  if (summary == null)
                  {
                        //lookup the xml document
                        GetXmlFor(declaringType);

                        //the summary should now be cached (if it exists!)
                        //so lets have another go at getting it...
                        summary = (string)summaries[key];

                        //if no summary was not found, create an blank one
                        if (summary == null)
                        {
                              //Debug.WriteLine("#NoSummary#\t" + key); 
                              summary = "";
                              //cache the blank so we don't search for it again
                              summaries.Add(key, summary);
                        }

                  }
                  return "<summary>" + summary + "</summary>";
            }

            private string GetDotnetFrameworkPath(string version)
            {
                  using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"))
                  {
                        if (regKey == null)
                              return null;

                        string installRoot = regKey.GetValue("InstallRoot") as string;

                        if (installRoot == null)
                              return null;

                        return Path.Combine(installRoot, version);
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index