// MsdnDocumenter.cs - a MSDN-like documenter
// Copyright (C) 2001  Kral Ferch, Jason Diamond
//
// 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.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Reflection;
using NDoc.Core;

namespace NDoc.Documenter.Msdn
{
	/// <summary>The MsdnDocumenter class.</summary>
	public class MsdnDocumenter : BaseDocumenter
	{
		enum WhichType
		{
			Class,
			Interface,
			Structure,
			Enumeration,
			Delegate,
			Unknown
		};

		HtmlHelp htmlHelp;

		string resourceDirectory;

		XmlDocument xmlDocumentation;

		Hashtable lowerCaseTypeNames;
		Hashtable mixedCaseTypeNames;
		StringDictionary fileNames;
		StringDictionary elemNames;

		XslTransform xsltNamespace;
		XslTransform xsltNamespaceHierarchy;
		XslTransform xsltType;
		XslTransform xsltAllMembers;
		XslTransform xsltIndividualMembers;
		XslTransform xsltEvent;
		XslTransform xsltMember;
		XslTransform xsltMemberOverload;
		XslTransform xsltProperty;
		XslTransform xsltField;

		ArrayList documentedNamespaces;

		/// <summary>Initializes a new instance of the MsdnHelp class.</summary>
		public MsdnDocumenter() : base("MSDN")
		{
			lowerCaseTypeNames = new Hashtable();
			lowerCaseTypeNames.Add(WhichType.Class, "クラス");
			lowerCaseTypeNames.Add(WhichType.Interface, "インターフェイス");
			lowerCaseTypeNames.Add(WhichType.Structure, "構造体");
			lowerCaseTypeNames.Add(WhichType.Enumeration, "列挙型");
			lowerCaseTypeNames.Add(WhichType.Delegate, "デリゲート");

			mixedCaseTypeNames = new Hashtable();
			mixedCaseTypeNames.Add(WhichType.Class, "クラス");
			mixedCaseTypeNames.Add(WhichType.Interface, "インターフェイス");
			mixedCaseTypeNames.Add(WhichType.Structure, "構造体");
			mixedCaseTypeNames.Add(WhichType.Enumeration, "列挙型");
			mixedCaseTypeNames.Add(WhichType.Delegate, "デリゲート");

			fileNames = new StringDictionary();
			elemNames = new StringDictionary();

			Clear();
		}

		/// <summary>See IDocumenter.</summary>
		public override void Clear()
		{
			Config = new MsdnDocumenterConfig();
		}

		/// <summary>See <see cref="IDocumenter"/>.</summary>
		public override string MainOutputFile 
		{ 
			get 
			{
				if ((MyConfig.OutputTarget & OutputType.HtmlHelp) > 0)
				{
					return Path.Combine(MyConfig.OutputDirectory, 
						MyConfig.HtmlHelpName + ".chm");
				}
				else
				{
					return Path.Combine(MyConfig.OutputDirectory, "index.html");
				}
			} 
		}

		/// <summary>See <see cref="IDocumenter"/>.</summary>
		public override string CanBuild(Project project, bool checkInputOnly)
		{
			string result = base.CanBuild(project, checkInputOnly); 
			if (result != null)
			{
				return result;
			}

			if (checkInputOnly) 
			{
				return null;
			}

			string path = Path.Combine(MyConfig.OutputDirectory, 
				MyConfig.HtmlHelpName + ".chm");

			string temp = Path.Combine(MyConfig.OutputDirectory, "~chm.tmp");

			try
			{

				if (File.Exists(path))
				{
					//if we can move the file, then it is not open...
					File.Move(path, temp);
					File.Move(temp, path);
				}
			}
			catch (Exception)
			{
				result = "The compiled HTML Help file is probably open.\nPlease close it and try again.";
			}

			return result;
		}

		/// <summary>See <see cref="IDocumenter"/>.</summary>
		public override void Build(Project project)
		{
			try
			{
				OnDocBuildingStep(0, "Initializing...");

// Define this when you want to edit the stylesheets
// without having to shutdown the application to rebuild.
#if NO_RESOURCES
				resourceDirectory = Path.GetFullPath(Path.Combine(
					System.Windows.Forms.Application.StartupPath, @"..\..\..\Documenter\Msdn\"));
#else
				resourceDirectory = Path.Combine(Path.Combine(
					Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
					"NDoc"), "MSDN");

				EmbeddedResources.WriteEmbeddedResources(
					this.GetType().Module.Assembly,
					"NDoc.Documenter.Msdn.xslt",
					Path.Combine(resourceDirectory, "xslt"));
#endif

				// Create the html output directory if it doesn't exist.
				if (!Directory.Exists(MyConfig.OutputDirectory))
				{
					Directory.CreateDirectory(MyConfig.OutputDirectory);
				}
				else
				{
					//clean-up output path
					foreach (string file in Directory.GetFiles(MyConfig.OutputDirectory, "*.*"))
					{
						try
						{
							File.Delete(file);
						}
						catch (IOException)
						{
							Trace.WriteLine("Could not delete " + file + " from the output directory because it is in use.");
							// IOException means the file is in use. Swallow the exception and continue.
						}
					}
				}

				// Write the embedded css files to the html output directory
				EmbeddedResources.WriteEmbeddedResources(
					this.GetType().Module.Assembly,
					"NDoc.Documenter.Msdn.css",
					MyConfig.OutputDirectory);

				// Write the embedded icons to the html output directory
				EmbeddedResources.WriteEmbeddedResources(
					this.GetType().Module.Assembly,
					"NDoc.Documenter.Msdn.images",
					MyConfig.OutputDirectory);

				// Write the embedded scripts to the html output directory
				EmbeddedResources.WriteEmbeddedResources(
					this.GetType().Module.Assembly,
					"NDoc.Documenter.Msdn.scripts",
					MyConfig.OutputDirectory);

				// Write the external files (FilesToInclude) to the html output directory

				foreach( string srcFile in MyConfig.FilesToInclude.Split( '|' ) )
				{
					if ((srcFile == null) || (srcFile.Length == 0))
						continue;

					string dstFile = Path.Combine(MyConfig.OutputDirectory, Path.GetFileName(srcFile));
					File.Copy(srcFile, dstFile, true);
					File.SetAttributes(dstFile, FileAttributes.Archive);
				}

				OnDocBuildingStep(10, "Merging XML documentation...");
				// Let the Documenter base class do it's thing.
				MakeXml(project);

				// Load the XML documentation into a DOM.
				xmlDocumentation = new XmlDocument();
				xmlDocumentation.LoadXml(XmlBuffer);

				XmlNodeList typeNodes = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace/*[name()!='documentation']");

				if (typeNodes.Count == 0)
				{
					throw new DocumenterException("There are no documentable types in this project.");
				}

				XmlNode defaultNamespace =
					xmlDocumentation.SelectSingleNode("/ndoc/assembly/module/namespace");

				string defaultNamespaceName = (string)defaultNamespace.Attributes["name"].Value;
				string defaultTopic = defaultNamespaceName + ".html";

				// setup for root page
				string rootPageFileName = null;
				string rootPageTOCName = "Overview";

				if ((MyConfig.RootPageFileName != null) && (MyConfig.RootPageFileName != string.Empty))
				{
					rootPageFileName = MyConfig.RootPageFileName;
					defaultTopic = "default.html";

					// what to call the top page in the table of contents?
					if ((MyConfig.RootPageTOCName != null) && (MyConfig.RootPageTOCName != string.Empty))
					{
						rootPageTOCName = MyConfig.RootPageTOCName;
					}
				}

#if MONO //Environment.ExpandEnvironmentVariables is not implemented in mono v0.25
				string compiler = MyConfig.HtmlHelpCompilerFilename;
#else
				string compiler = Environment.ExpandEnvironmentVariables(
						MyConfig.HtmlHelpCompilerFilename);
#endif

				htmlHelp = new HtmlHelp(
					MyConfig.OutputDirectory,
					MyConfig.HtmlHelpName,
					defaultTopic,
					compiler,
					((MyConfig.OutputTarget & OutputType.HtmlHelp) == 0));

				htmlHelp.IncludeFavorites = MyConfig.IncludeFavorites;

				OnDocBuildingStep(25, "Building file mapping...");

				MakeFilenames(xmlDocumentation);

				OnDocBuildingStep(30, "Loading XSLT files...");

				MakeTransforms();

				OnDocBuildingStep(40, "Generating HTML pages...");

				htmlHelp.OpenProjectFile();

				if (!MyConfig.SplitTOCs)
				{
					htmlHelp.OpenContentsFile(string.Empty, true);
				}

				try
				{
					if (MyConfig.CopyrightHref != null && MyConfig.CopyrightHref != String.Empty)
					{
						if (!MyConfig.CopyrightHref.StartsWith("http:"))
						{
							string copyrightFile = Path.Combine(MyConfig.OutputDirectory, Path.GetFileName(MyConfig.CopyrightHref));
							File.Copy(MyConfig.CopyrightHref, copyrightFile, true);
							File.SetAttributes(copyrightFile, FileAttributes.Archive);
							htmlHelp.AddFileToProject(Path.GetFileName(MyConfig.CopyrightHref));
						}
					}

					// add root page if requested
					if (rootPageFileName != null)
					{
						if (!File.Exists(rootPageFileName))
						{
							throw new DocumenterException("Cannot find the documentation's root page file:\n" 
								+ rootPageFileName);
						}

						// add the file
						string rootPageOutputName = Path.Combine(MyConfig.OutputDirectory, "default.html");
						if (Path.GetFullPath(rootPageFileName) != Path.GetFullPath(rootPageOutputName))
						{
							File.Copy(rootPageFileName, rootPageOutputName, true);
							File.SetAttributes(rootPageOutputName, FileAttributes.Archive);
						}
						htmlHelp.AddFileToProject(Path.GetFileName(rootPageOutputName));
						htmlHelp.AddFileToContents(rootPageTOCName, 
							Path.GetFileName(rootPageOutputName));

						// depending on peer setting, make root page the container
						if (MyConfig.RootPageContainsNamespaces) htmlHelp.OpenBookInContents();
					}

					documentedNamespaces = new ArrayList();
					MakeHtmlForAssemblies();

					// close root book if applicable
					if (rootPageFileName != null)
					{
						if (MyConfig.RootPageContainsNamespaces) htmlHelp.CloseBookInContents();
					}
				}
				finally
				{
					if (!MyConfig.SplitTOCs)
					{
						htmlHelp.CloseContentsFile();
					}

					htmlHelp.CloseProjectFile();
				}

				htmlHelp.WriteEmptyIndexFile();

				if ((MyConfig.OutputTarget & OutputType.Web) > 0)
				{
					OnDocBuildingStep(75, "Generating HTML content file...");

					// Write the embedded online templates to the html output directory
					EmbeddedResources.WriteEmbeddedResources(
						this.GetType().Module.Assembly,
						"NDoc.Documenter.Msdn.onlinefiles",
						MyConfig.OutputDirectory);

					using (TemplateWriter indexWriter = new TemplateWriter(
							   Path.Combine(MyConfig.OutputDirectory, "index.html"),
							   new StreamReader(this.GetType().Module.Assembly.GetManifestResourceStream(
							   "NDoc.Documenter.Msdn.onlinetemplates.index.html"))))
					{
						indexWriter.CopyToLine("\t\t<title><%TITLE%></title>");
						indexWriter.WriteLine("\t\t<title>" + MyConfig.HtmlHelpName + "</title>");
						indexWriter.CopyToLine("\t\t<frame name=\"main\" src=\"<%HOME_PAGE%>\" frameborder=\"1\">");
						indexWriter.WriteLine("\t\t<frame name=\"main\" src=\"" + defaultTopic + "\" frameborder=\"1\">");
						indexWriter.CopyToEnd();
						indexWriter.Close();
					}

					//transform the HHC contents file into html
					XslTransform xsltContents = new XslTransform();
					MakeTransform(xsltContents, "htmlcontents.xslt");
					
					// TODO: 2003/07/11 Add
					/*********************************************************************/
					if (xsltContents != null)
					{
						StreamReader reader = new StreamReader(htmlHelp.GetPathToContentsFile());
						StreamWriter writer = new StreamWriter(htmlHelp.GetPathToContentsFile() + ".tmp", false, new UTF8Encoding());
						
						string tempText = reader.ReadLine();
						while (tempText != null)
						{
							writer.WriteLine(tempText);
							tempText = reader.ReadLine();
						}
						reader.Close();
						writer.Close();
					}
					/*********************************************************************/
					
					// Original
					/*
					xsltContents.Transform(htmlHelp.GetPathToContentsFile(), 
						Path.Combine(MyConfig.OutputDirectory, "contents.html"));
					*/
					
					// TODO: 2003/07/11 Edit
					xsltContents.Transform(htmlHelp.GetPathToContentsFile() + ".tmp", 
						Path.Combine(MyConfig.OutputDirectory, "contents.html"));
						
					// TODO: 2003/07/11 Add
					if (File.Exists(htmlHelp.GetPathToContentsFile() + ".tmp") == true)
					{
						File.Delete(htmlHelp.GetPathToContentsFile() + ".tmp");
					}
				}

				if ((MyConfig.OutputTarget & OutputType.HtmlHelp) > 0)
				{
					OnDocBuildingStep(85, "Compiling HTML Help file...");
					htmlHelp.CompileProject();
				}
				else
				{
					//remove .hhc file
					File.Delete(htmlHelp.GetPathToContentsFile());
				}

				OnDocBuildingStep(100, "Done.");
			}
			catch(Exception ex)
			{
				throw new DocumenterException(ex.Message, ex);
			}
		}

		private MsdnDocumenterConfig MyConfig
		{
			get
			{
				return (MsdnDocumenterConfig)Config;
			}
		}

		private void MakeTransform(
			XslTransform transform,
			string filename)
		{
			try
			{
				transform.Load(Path.Combine(Path.Combine(resourceDirectory, "xslt"), filename));
			}
			catch (Exception e)
			{
				throw new DocumenterException(
					"Error compiling the " +
					filename +
					" stylesheet: \n" + e.Message,
					e);
			}
		}

		private void MakeFilenames(XmlNode documentation)
		{
			XmlNodeList namespaces = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace");
			foreach (XmlElement namespaceNode in namespaces)
			{
				string namespaceName = namespaceNode.Attributes["name"].Value;
				string namespaceId = "N:" + namespaceName;
				fileNames[namespaceId] = GetFilenameForNamespace(namespaceName);
				elemNames[namespaceId] = namespaceName;

				XmlNodeList types = namespaceNode.SelectNodes("*[@id]");
				foreach (XmlElement typeNode in types)
				{
					string typeId = typeNode.Attributes["id"].Value;
					fileNames[typeId] = GetFilenameForType(typeNode);
					elemNames[typeId] = typeNode.Attributes["name"].Value;

					XmlNodeList members = typeNode.SelectNodes("*[@id]");
					foreach (XmlElement memberNode in members)
					{
						string id = memberNode.Attributes["id"].Value;
						switch (memberNode.Name)
						{
							case "constructor":
								fileNames[id] = GetFilenameForConstructor(memberNode);
								elemNames[id] = elemNames[typeId];
								break;
							case "field":
								if (typeNode.Name == "enumeration")
									fileNames[id] = GetFilenameForType(typeNode);
								else
									fileNames[id] = GetFilenameForField(memberNode);
								elemNames[id] = memberNode.Attributes["name"].Value;
								break;
							case "property":
								fileNames[id] = GetFilenameForProperty(memberNode);
								elemNames[id] = memberNode.Attributes["name"].Value;
								break;
							case "method":
								fileNames[id] = GetFilenameForMethod(memberNode);
								elemNames[id] = memberNode.Attributes["name"].Value;
								break;
							case "operator":
								fileNames[id] = GetFilenameForOperator(memberNode);
								elemNames[id] = memberNode.Attributes["name"].Value;
								break;
							case "event":
								fileNames[id] = GetFilenameForEvent(memberNode);
								elemNames[id] = memberNode.Attributes["name"].Value;
								break;
						}
					}
				}
			}
		}

		private void MakeTransforms()
		{
			OnDocBuildingProgress(0);
			Trace.Indent();

			xsltNamespace = new XslTransform();
			xsltNamespaceHierarchy = new XslTransform();
			xsltType = new XslTransform();
			xsltAllMembers = new XslTransform();
			xsltIndividualMembers = new XslTransform();
			xsltEvent = new XslTransform();
			xsltMember = new XslTransform();
			xsltMemberOverload = new XslTransform();
			xsltProperty = new XslTransform();
			xsltField = new XslTransform();

			Trace.WriteLine("namespace.xslt");
			OnDocBuildingProgress(10);
			MakeTransform(
				xsltNamespace,
				"namespace.xslt");

			Trace.WriteLine("namespacehierarchy.xslt");
			OnDocBuildingProgress(20);
			MakeTransform(
				xsltNamespaceHierarchy,
				"namespacehierarchy.xslt");

			Trace.WriteLine("type.xslt");
			OnDocBuildingProgress(30);
			MakeTransform(
				xsltType,
				"type.xslt");

			Trace.WriteLine("allmembers.xslt");
			OnDocBuildingProgress(40);
			MakeTransform(
				xsltAllMembers,
				"allmembers.xslt");

			Trace.WriteLine("individualmembers.xslt");
			OnDocBuildingProgress(50);
			MakeTransform(
				xsltIndividualMembers,
				"individualmembers.xslt");

			Trace.WriteLine("event.xslt");
			OnDocBuildingProgress(60);
			MakeTransform(
				xsltEvent,
				"event.xslt");

			Trace.WriteLine("member.xslt");
			OnDocBuildingProgress(70);
			MakeTransform(
				xsltMember,
				"member.xslt");

			Trace.WriteLine("memberoverload.xslt");
			OnDocBuildingProgress(80);
			MakeTransform(
				xsltMemberOverload,
				"memberoverload.xslt");

			Trace.WriteLine("property.xslt");
			OnDocBuildingProgress(90);
			MakeTransform(
				xsltProperty,
				"property.xslt");

			Trace.WriteLine("field.xslt");
			OnDocBuildingProgress(100);
			MakeTransform(
				xsltField,
				"field.xslt");

			Trace.Unindent();
		}

		private WhichType GetWhichType(XmlNode typeNode)
		{
			WhichType whichType;

			switch (typeNode.Name)
			{
				case "class":
					whichType = WhichType.Class;
					break;
				case "interface":
					whichType = WhichType.Interface;
					break;
				case "structure":
					whichType = WhichType.Structure;
					break;
				case "enumeration":
					whichType = WhichType.Enumeration;
					break;
				case "delegate":
					whichType = WhichType.Delegate;
					break;
				default:
					whichType = WhichType.Unknown;
					break;
			}

			return whichType;
		}

		private void MakeHtmlForAssemblies()
		{
#if DEBUG
			int start = Environment.TickCount;
#endif

			if (MyConfig.SortTOCByNamespace)
				MakeHtmlForAssembliesSorted();
			else
				MakeHtmlForAssembliesUnsorted();

#if DEBUG
			Trace.WriteLine("Making Html: " + ((Environment.TickCount - start)/1000.0).ToString() + " sec.");
#endif
		}

		private void MakeHtmlForAssembliesUnsorted()
		{
			XmlNodeList assemblyNodes = xmlDocumentation.SelectNodes("/ndoc/assembly");
			int[] indexes = SortNodesByAttribute(assemblyNodes, "name");

			int nNodes = assemblyNodes.Count;

			for (int i = 0; i < nNodes; i++)
			{
				XmlNode assemblyNode = assemblyNodes[indexes[i]];

				if (assemblyNode.ChildNodes.Count > 0)
				{
					string assemblyName = (string)assemblyNode.Attributes["name"].Value;

					if (MyConfig.SplitTOCs)
					{
						bool isDefault = (assemblyName == MyConfig.DefaulTOC);

						if (isDefault)
						{
							XmlNode defaultNamespace =
								xmlDocumentation.SelectSingleNode("/ndoc/assembly[@name='" 
								+ assemblyName + "']/module/namespace");

							if (defaultNamespace != null)
							{
								string defaultNamespaceName = (string)defaultNamespace.Attributes["name"].Value;
								htmlHelp.DefaultTopic = defaultNamespaceName + ".html";
							}
						}

						htmlHelp.OpenContentsFile(assemblyName, isDefault);
					}

					try
					{
						MakeHtmlForNamespaces(assemblyName);
					}
					finally
					{
						if (MyConfig.SplitTOCs)
						{
							htmlHelp.CloseContentsFile();
						}
					}
				}
			}
		}

		private void MakeHtmlForNamespaces(string assemblyName)
		{
			XmlNodeList namespaceNodes = xmlDocumentation.SelectNodes("/ndoc/assembly[@name=\"" + assemblyName + "\"]/module/namespace");
			int[] indexes = SortNodesByAttribute(namespaceNodes, "name");

			int nNodes = namespaceNodes.Count;

			for (int i = 0; i < nNodes; i++)
			{
				OnDocBuildingProgress(i*100/nNodes);

				XmlNode namespaceNode = namespaceNodes[indexes[i]];

				if (namespaceNode.ChildNodes.Count > 0)
				{
					string namespaceName = (string)namespaceNode.Attributes["name"].Value;

					MakeHtmlForNamespace(assemblyName, namespaceName);
				}
			}

			OnDocBuildingProgress(100);
		}

		private void MakeHtmlForAssembliesSorted()
		{
			XmlNodeList assemblyNodes = xmlDocumentation.SelectNodes("/ndoc/assembly");
			int[] indexes = SortNodesByAttribute(assemblyNodes, "name");

			System.Collections.Specialized.NameValueCollection namespaceAssemblies
				= new System.Collections.Specialized.NameValueCollection();

			int nNodes = assemblyNodes.Count;
			for (int i = 0; i < nNodes; i++)
			{
				XmlNode assemblyNode = assemblyNodes[indexes[i]];
				if (assemblyNode.ChildNodes.Count > 0)
				{
					string assemblyName = (string)assemblyNode.Attributes["name"].Value;
					GetNamespacesFromAssembly(assemblyName, namespaceAssemblies);
				}
			}

			nNodes = namespaceAssemblies.Count;
			string [] namespaces = namespaceAssemblies.AllKeys;
			Array.Sort(namespaces);
			for (int i = 0; i < nNodes; i++)
			{
				OnDocBuildingProgress(i*100/nNodes);
				string namespaceName = namespaces[i];
				foreach (string assemblyName in namespaceAssemblies.GetValues(i))
					MakeHtmlForNamespace(assemblyName, namespaceName);
			}

			OnDocBuildingProgress(100);
		}

		private void GetNamespacesFromAssembly(string assemblyName, System.Collections.Specialized.NameValueCollection namespaceAssemblies)
		{
			XmlNodeList namespaceNodes = xmlDocumentation.SelectNodes("/ndoc/assembly[@name=\"" + assemblyName + "\"]/module/namespace");
			foreach (XmlNode namespaceNode in namespaceNodes)
			{
				string namespaceName = (string)namespaceNode.Attributes["name"].Value;
				namespaceAssemblies.Add(namespaceName, assemblyName);
			}
		}

		private void MakeHtmlForNamespace(string assemblyName, string namespaceName)
		{
			if (documentedNamespaces.Contains(namespaceName)) 
				return;

			documentedNamespaces.Add(namespaceName);

			string fileName = GetFilenameForNamespace(namespaceName);
			htmlHelp.AddFileToContents(namespaceName, fileName);

			XsltArgumentList arguments = new XsltArgumentList();
			arguments.AddParam("namespace", String.Empty, namespaceName);
			arguments.AddParam("includeHierarchy", String.Empty, MyConfig.IncludeHierarchy);

			TransformAndWriteResult(xsltNamespace, arguments, fileName);

			arguments = new XsltArgumentList();
			arguments.AddParam("namespace", String.Empty, namespaceName);

			if (MyConfig.IncludeHierarchy)
			{
				TransformAndWriteResult(
					xsltNamespaceHierarchy,
					arguments,
					fileName.Insert(fileName.Length - 5, "Hierarchy"));
			}

			MakeHtmlForTypes(namespaceName);
		}

		private void MakeHtmlForTypes(string namespaceName)
		{
			XmlNodeList typeNodes =
				xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace[@name=\"" + namespaceName + "\"]/*[local-name()!='documentation']");

			int[] indexes = SortNodesByAttribute(typeNodes, "id");
			int nNodes = typeNodes.Count;

			htmlHelp.OpenBookInContents();

			for (int i = 0; i < nNodes; i++)
			{
				XmlNode typeNode = typeNodes[indexes[i]];

				WhichType whichType = GetWhichType(typeNode);

				switch(whichType)
				{
					case WhichType.Class:
						MakeHtmlForInterfaceOrClassOrStructure(whichType, typeNode);
						break;
					case WhichType.Interface:
						MakeHtmlForInterfaceOrClassOrStructure(whichType, typeNode);
						break;
					case WhichType.Structure:
						MakeHtmlForInterfaceOrClassOrStructure(whichType, typeNode);
						break;
					case WhichType.Enumeration:
						MakeHtmlForEnumerationOrDelegate(whichType, typeNode);
						break;
					case WhichType.Delegate:
						MakeHtmlForEnumerationOrDelegate(whichType, typeNode);
						break;
					default:
						break;
				}
			}

			htmlHelp.CloseBookInContents();
		}

		private void MakeHtmlForEnumerationOrDelegate(WhichType whichType, XmlNode typeNode)
		{
			string typeName = typeNode.Attributes["name"].Value;
			string typeID = typeNode.Attributes["id"].Value;
			string fileName = GetFilenameForType(typeNode);

			htmlHelp.AddFileToContents(typeName + " " + mixedCaseTypeNames[whichType], fileName);

			XsltArgumentList arguments = new XsltArgumentList();
			arguments.AddParam("type-id", String.Empty, typeID);
			TransformAndWriteResult(xsltType, arguments, fileName);
		}

		private void MakeHtmlForInterfaceOrClassOrStructure(
			WhichType whichType,
			XmlNode typeNode)
		{
			string typeName = typeNode.Attributes["name"].Value;
			string typeID = typeNode.Attributes["id"].Value;
			string fileName = GetFilenameForType(typeNode);

			htmlHelp.AddFileToContents(typeName + " " + mixedCaseTypeNames[whichType], fileName);

			bool hasMembers = typeNode.SelectNodes("constructor|field|property|method|operator|event").Count > 0;

			if (hasMembers)
			{
				htmlHelp.OpenBookInContents();
			}

			XsltArgumentList arguments = new XsltArgumentList();
			arguments.AddParam("type-id", String.Empty, typeID);
			TransformAndWriteResult(xsltType, arguments, fileName);

			if (hasMembers)
			{
				fileName = GetFilenameForTypeMembers(typeNode);
				htmlHelp.AddFileToContents(typeName + " メンバ", fileName);

				arguments = new XsltArgumentList();
				arguments.AddParam("id", String.Empty, typeID);
				TransformAndWriteResult(xsltAllMembers, arguments, fileName);

				MakeHtmlForConstructors(whichType, typeNode);
				MakeHtmlForFields(whichType, typeNode);
				MakeHtmlForProperties(whichType, typeNode);
				MakeHtmlForMethods(whichType, typeNode);
				MakeHtmlForOperators(whichType, typeNode);
				MakeHtmlForEvents(whichType, typeNode);

				htmlHelp.CloseBookInContents();
			}
		}

		private void MakeHtmlForConstructors(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList   constructorNodes;
			string        constructorID;
			string        typeName;
			string        typeID;
			string        fileName;

			typeName = typeNode.Attributes["name"].Value;
			typeID = typeNode.Attributes["id"].Value;
			constructorNodes = typeNode.SelectNodes("constructor");

			// If the constructor is overloaded then make an overload page.
			if (constructorNodes.Count > 1)
			{
				fileName = GetFilenameForConstructors(typeNode);
				htmlHelp.AddFileToContents(typeName + " コンストラクタ", fileName);

				htmlHelp.OpenBookInContents();

				constructorID = constructorNodes[0].Attributes["id"].Value;

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("member-id", String.Empty, constructorID);
				TransformAndWriteResult(xsltMemberOverload, arguments, fileName);
			}

			foreach (XmlNode constructorNode in constructorNodes)
			{
				constructorID = constructorNode.Attributes["id"].Value;
				fileName = GetFilenameForConstructor(constructorNode);

				if (constructorNodes.Count > 1)
				{
					XmlNodeList   parameterNodes = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace/" + lowerCaseTypeNames[whichType] + "[@name=\"" + typeName + "\"]/constructor[@id=\"" + constructorID + "\"]/parameter");
					htmlHelp.AddFileToContents(typeName + " コンストラクタ " + GetParamList(parameterNodes), fileName);
				}
				else
				{
					htmlHelp.AddFileToContents(typeName + " コンストラクタ", fileName);
				}

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("member-id", String.Empty, constructorID);
				TransformAndWriteResult(xsltMember, arguments, fileName);
			}

			if (constructorNodes.Count > 1)
			{
				htmlHelp.CloseBookInContents();
			}
		}

		private void MakeHtmlForFields(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList fields = typeNode.SelectNodes("field[not(@declaringType)]");

			if (fields.Count > 0)
			{
				string typeName = typeNode.Attributes["name"].Value;
				string typeID = typeNode.Attributes["id"].Value;
				string fileName = GetFilenameForFields(whichType, typeNode);

				htmlHelp.AddFileToContents("フィールド", fileName);

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("id", String.Empty, typeID);
				arguments.AddParam("member-type", String.Empty, "field");
				TransformAndWriteResult(xsltIndividualMembers, arguments, fileName);

				htmlHelp.OpenBookInContents();

				int[] indexes = SortNodesByAttribute(fields, "id");

				foreach (int index in indexes)
				{
					XmlNode field = fields[index];

					string fieldName = field.Attributes["name"].Value;
					string fieldID = field.Attributes["id"].Value;
					fileName = GetFilenameForField(field);
					htmlHelp.AddFileToContents(fieldName + " フェールド", fileName);

					arguments = new XsltArgumentList();
					arguments.AddParam("field-id", String.Empty, fieldID);
					TransformAndWriteResult(xsltField, arguments, fileName);
				}

				htmlHelp.CloseBookInContents();
			}
		}

		private void MakeHtmlForProperties(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList declaredPropertyNodes = typeNode.SelectNodes("property[not(@declaringType)]");

			if (declaredPropertyNodes.Count > 0)
			{
				XmlNodeList   propertyNodes;
				XmlNode     propertyNode;
				string        propertyName;
				string        propertyID;
				string        previousPropertyName;
				string        nextPropertyName;
				bool        bOverloaded = false;
				string        typeName;
				string        typeID;
				string        fileName;
				int         nNodes;
				int[]       indexes;
				int         i;

				typeName = typeNode.Attributes["name"].Value;
				typeID = typeNode.Attributes["id"].Value;
				propertyNodes = typeNode.SelectNodes("property[not(@declaringType)]");
				nNodes = propertyNodes.Count;

				indexes = SortNodesByAttribute(propertyNodes, "id");

				fileName = GetFilenameForProperties(whichType, typeNode);
				htmlHelp.AddFileToContents("プロパティ", fileName);

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("id", String.Empty, typeID);
				arguments.AddParam("member-type", String.Empty, "property");
				TransformAndWriteResult(xsltIndividualMembers, arguments, fileName);

				htmlHelp.OpenBookInContents();

				for (i = 0; i < nNodes; i++)
				{
					propertyNode = propertyNodes[indexes[i]];

					propertyName = (string)propertyNode.Attributes["name"].Value;
					propertyID = (string)propertyNode.Attributes["id"].Value;

					// If the method is overloaded then make an overload page.
					previousPropertyName = ((i - 1 < 0) || (propertyNodes[indexes[i - 1]].Attributes.Count == 0))
						? "" : propertyNodes[indexes[i - 1]].Attributes[0].Value;
					nextPropertyName = ((i + 1 == nNodes) || (propertyNodes[indexes[i + 1]].Attributes.Count == 0))
						? "" : propertyNodes[indexes[i + 1]].Attributes[0].Value;

					if ((previousPropertyName != propertyName) && (nextPropertyName == propertyName))
					{
						fileName = GetFilenameForPropertyOverloads(typeNode, propertyNode);
						htmlHelp.AddFileToContents(propertyName + " プロパティ", fileName);

						arguments = new XsltArgumentList();
						arguments.AddParam("member-id", String.Empty, propertyID);
						TransformAndWriteResult(xsltMemberOverload, arguments, fileName);

						htmlHelp.OpenBookInContents();

						bOverloaded = true;
					}

					fileName = GetFilenameForProperty(propertyNode);

					if (bOverloaded)
					{
						XmlNodeList parameterNodes = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace/" + lowerCaseTypeNames[whichType] + "[@name=\"" + typeName + "\"]/property[@id=\"" + propertyID + "\"]/parameter");
						htmlHelp.AddFileToContents(propertyName + " プロパティ " + GetParamList(parameterNodes), fileName);
					}
					else
					{
						htmlHelp.AddFileToContents(propertyName + " プロパティ", fileName);
					}

					XsltArgumentList arguments2 = new XsltArgumentList();
					arguments2.AddParam("property-id", String.Empty, propertyID);
					TransformAndWriteResult(xsltProperty, arguments2, fileName);

					if ((previousPropertyName == propertyName) && (nextPropertyName != propertyName))
					{
						htmlHelp.CloseBookInContents();
						bOverloaded = false;
					}
				}

				htmlHelp.CloseBookInContents();
			}
		}

		private string GetPreviousMethodName(XmlNodeList methodNodes, int[] indexes, int index)
		{
			while (--index >= 0)
			{
				if (methodNodes[indexes[index]].Attributes["declaringType"] == null)
					return methodNodes[indexes[index]].Attributes["name"].Value;
			}
			return null;
		}

		private string GetNextMethodName(XmlNodeList methodNodes, int[] indexes, int index)
		{
			while (++index < methodNodes.Count)
			{
				if (methodNodes[indexes[index]].Attributes["declaringType"] == null)
					return methodNodes[indexes[index]].Attributes["name"].Value;
			}
			return null;
		}

		// returns true, if method is neither overload of a method in the same class,
		// nor overload of a method in the base class.
		private bool IsMethodAlone(XmlNodeList methodNodes, int[] indexes, int index)
		{
			string name = methodNodes[indexes[index]].Attributes["name"].Value;
			int lastIndex = methodNodes.Count - 1;
			if (lastIndex <= 0)
				return true;
			bool previousNameDifferent = (index == 0)
				|| (methodNodes[indexes[index - 1]].Attributes["name"].Value != name);
			bool nextNameDifferent = (index == lastIndex)
				|| (methodNodes[indexes[index + 1]].Attributes["name"].Value != name);
			return (previousNameDifferent && nextNameDifferent);
		}

		private bool IsMethodFirstOverload(XmlNodeList methodNodes, int[] indexes, int index)
		{
			if ((methodNodes[indexes[index]].Attributes["declaringType"] != null)
				|| IsMethodAlone(methodNodes, indexes, index))
				return false;

			string name			= methodNodes[indexes[index]].Attributes["name"].Value;
			string previousName	= GetPreviousMethodName(methodNodes, indexes, index);
			return previousName != name;
		}

		private bool IsMethodLastOverload(XmlNodeList methodNodes, int[] indexes, int index)
		{
			if ((methodNodes[indexes[index]].Attributes["declaringType"] != null)
				|| IsMethodAlone(methodNodes, indexes, index))
				return false;

			string name		= methodNodes[indexes[index]].Attributes["name"].Value;
			string nextName	= GetNextMethodName(methodNodes, indexes, index);
			return nextName != name;
		}

		private void MakeHtmlForMethods(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList declaredMethodNodes = typeNode.SelectNodes("method[not(@declaringType)]");

			if (declaredMethodNodes.Count > 0)
			{
				bool bOverloaded = false;
				string fileName;

				string typeName = typeNode.Attributes["name"].Value;
				string typeID = typeNode.Attributes["id"].Value;
				XmlNodeList methodNodes = typeNode.SelectNodes("method");
				int nNodes = methodNodes.Count;

				int[] indexes = SortNodesByAttribute(methodNodes, "id");

				fileName = GetFilenameForMethods(whichType, typeNode);
				htmlHelp.AddFileToContents("メソッド", fileName);

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("id", String.Empty, typeID);
				arguments.AddParam("member-type", String.Empty, "method");
				TransformAndWriteResult(xsltIndividualMembers, arguments, fileName);

				htmlHelp.OpenBookInContents();

				for (int i = 0; i < nNodes; i++)
				{
					XmlNode methodNode = methodNodes[indexes[i]];
					string methodName = (string)methodNode.Attributes["name"].Value;
					string methodID = (string)methodNode.Attributes["id"].Value;

					if (IsMethodFirstOverload(methodNodes, indexes, i))
					{
						bOverloaded = true;

						fileName = GetFilenameForMethodOverloads(typeNode, methodNode);
						htmlHelp.AddFileToContents(methodName + " メソッド", fileName);

						arguments = new XsltArgumentList();
						arguments.AddParam("member-id", String.Empty, methodID);
						TransformAndWriteResult(xsltMemberOverload, arguments, fileName);

						htmlHelp.OpenBookInContents();
					}

					if (methodNode.Attributes["declaringType"] == null)
					{
						fileName = GetFilenameForMethod(methodNode);

						if (bOverloaded)
						{
							XmlNodeList parameterNodes = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace/" + lowerCaseTypeNames[whichType] + "[@name=\"" + typeName + "\"]/method[@id=\"" + methodID + "\"]/parameter");
							htmlHelp.AddFileToContents(methodName + " メソッド " + GetParamList(parameterNodes), fileName);
						}
						else
						{
							htmlHelp.AddFileToContents(methodName + " メソッド", fileName);
						}

						XsltArgumentList arguments2 = new XsltArgumentList();
						arguments2.AddParam("member-id", String.Empty, methodID);
						TransformAndWriteResult(xsltMember, arguments2, fileName);
					}

					if (bOverloaded && IsMethodLastOverload(methodNodes, indexes, i))
					{
						bOverloaded = false;
						htmlHelp.CloseBookInContents();
					}
				}

				htmlHelp.CloseBookInContents();
			}
		}

		private void MakeHtmlForOperators(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList operators = typeNode.SelectNodes("operator");

			if (operators.Count > 0)
			{
				string typeName = (string)typeNode.Attributes["name"].Value;
				string typeID = (string)typeNode.Attributes["id"].Value;
				XmlNodeList opNodes = typeNode.SelectNodes("operator");
				string fileName = GetFilenameForOperators(whichType, typeNode);
				bool bOverloaded = false;

				string title = "Operators";

				if (typeNode.SelectSingleNode("operator[@name = 'op_Explicit' or @name = 'op_Implicit']") != null)
				{
					title += " and Type Conversions";
				}

				htmlHelp.AddFileToContents(title, fileName);

				XsltArgumentList arguments = new XsltArgumentList();
				arguments.AddParam("id", String.Empty, typeID);
				arguments.AddParam("member-type", String.Empty, "operator");
				TransformAndWriteResult(xsltIndividualMembers, arguments, fileName);

				htmlHelp.OpenBookInContents();

				int[] indexes = SortNodesByAttribute(operators, "id");
				int nNodes = opNodes.Count;

				for (int i = 0; i < nNodes; i++)
				{
					XmlNode operatorNode = operators[indexes[i]];
					string operatorID = operatorNode.Attributes["id"].Value;

					if (IsMethodFirstOverload(opNodes, indexes, i))
					{
						string opName = (string)operatorNode.Attributes["name"].Value;
						if ((opName != "op_Implicit") 
							&& (opName != "op_Implicit"))
						{
							bOverloaded = true;

							fileName = GetFilenameForOperatorsOverloads(typeNode, operatorNode);
							htmlHelp.AddFileToContents(GetOperatorName(operatorNode), fileName);

							arguments = new XsltArgumentList();
							arguments.AddParam("member-id", String.Empty, operatorID);
							TransformAndWriteResult(xsltMemberOverload, arguments, fileName);

							htmlHelp.OpenBookInContents();
						}
					}


					fileName = GetFilenameForOperator(operatorNode);
					if (bOverloaded)
					{
						XmlNodeList parameterNodes = xmlDocumentation.SelectNodes("/ndoc/assembly/module/namespace/" + lowerCaseTypeNames[whichType] + "[@name=\"" + typeName + "\"]/operator[@id=\"" + operatorID + "\"]/parameter");
						htmlHelp.AddFileToContents(GetOperatorName(operatorNode) + GetParamList(parameterNodes), fileName);
					}
					else
					{
						htmlHelp.AddFileToContents(GetOperatorName(operatorNode), fileName);
					}

					arguments = new XsltArgumentList();
					arguments.AddParam("member-id", String.Empty, operatorID);
					TransformAndWriteResult(xsltMember, arguments, fileName);

					if (bOverloaded && IsMethodLastOverload(opNodes, indexes, i))
					{
						bOverloaded = false;
						htmlHelp.CloseBookInContents();
					}
				}

				htmlHelp.CloseBookInContents();
			}
		}

		private string GetOperatorName(XmlNode operatorNode)
		{
			string name = operatorNode.Attributes["name"].Value;

			switch (name)
			{
				case "op_UnaryPlus":
					return "Unary Plus Operator";
				case "op_UnaryNegation":
					return "Unary Negation Operator";
				case "op_LogicalNot":
					return "Logical Not Operator";
				case "op_OnesComplement":
					return "Ones Complement Operator";
				case "op_Increment":
					return "Increment Operator";
				case "op_Decrement":
					return "Decrement Operator";
				case "op_True":
					return "True Operator";
				case "op_False":
					return "False Operator";
				case "op_Addition":
					return "Addition Operator";
				case "op_Subtraction":
					return "Subtraction Operator";
				case "op_Multiply":
					return "Multiplication Operator";
				case "op_Division":
					return "Division Operator";
				case "op_Modulus":
					return "Modulus Operator";
				case "op_BitwiseAnd":
					return "Bitwise And Operator";
				case "op_BitwiseOr":
					return "Bitwise Or Operator";
				case "op_ExclusiveOr":
					return "Exclusive Or Operator";
				case "op_LeftShift":
					return "Left Shift Operator";
				case "op_RightShift":
					return "Right Shift Operator";
				case "op_Equality":
					return "Equality Operator";
				case "op_Inequality":
					return "Inequality Operator";
				case "op_LessThan":
					return "Less Than Operator";
				case "op_GreaterThan":
					return "Greater Than Operator";
				case "op_LessThanOrEqual":
					return "Less Than Or Equal Operator";
				case "op_GreaterThanOrEqual":
					return "Greater Than Or Equal Operator";
				case "op_Explicit":
					XmlNode parameterNode = operatorNode.SelectSingleNode("parameter");
					string from = parameterNode.Attributes["type"].Value;
					string to = operatorNode.Attributes["returnType"].Value;
					return "Explicit " + StripNamespace(from) + " to " + StripNamespace(to) + " Conversion";
				case "op_Implicit":
					XmlNode parameterNode2 = operatorNode.SelectSingleNode("parameter");
					string from2 = parameterNode2.Attributes["type"].Value;
					string to2 = operatorNode.Attributes["returnType"].Value;
					return "Implicit " + StripNamespace(from2) + " to " + StripNamespace(to2) + " Conversion";
				default:
					return "ERROR";
			}
		}

		private string StripNamespace(string name)
		{
			string result = name;

			int lastDot = name.LastIndexOf('.');

			if (lastDot != -1)
			{
				result = name.Substring(lastDot + 1);
			}

			return result;
		}

		private void MakeHtmlForEvents(WhichType whichType, XmlNode typeNode)
		{
			XmlNodeList declaredEventNodes = typeNode.SelectNodes("event[not(@declaringType)]");

			if (declaredEventNodes.Count > 0)
			{
				XmlNodeList events = typeNode.SelectNodes("event");

				if (events.Count > 0)
				{
					string typeName = (string)typeNode.Attributes["name"].Value;
					string typeID = (string)typeNode.Attributes["id"].Value;
					string fileName = GetFilenameForEvents(whichType, typeNode);

					htmlHelp.AddFileToContents("イベント", fileName);

					XsltArgumentList arguments = new XsltArgumentList();
					arguments.AddParam("id", String.Empty, typeID);
					arguments.AddParam("member-type", String.Empty, "event");
					TransformAndWriteResult(xsltIndividualMembers, arguments, fileName);

					htmlHelp.OpenBookInContents();

					int[] indexes = SortNodesByAttribute(events, "id");

					foreach (int index in indexes)
					{
						XmlNode eventElement = events[index];

						if (eventElement.Attributes["declaringType"] == null)
						{
							string eventName = (string)eventElement.Attributes["name"].Value;
							string eventID = (string)eventElement.Attributes["id"].Value;

							fileName = GetFilenameForEvent(eventElement);
							htmlHelp.AddFileToContents(eventName + " イベント", fileName);

							arguments = new XsltArgumentList();
							arguments.AddParam("event-id", String.Empty, eventID);
							TransformAndWriteResult(xsltEvent, arguments, fileName);
						}
					}

					htmlHelp.CloseBookInContents();
				}
			}
		}

		private string GetParamList(XmlNodeList parameterNodes)
		{
			int numberOfNodes = parameterNodes.Count;
			int nodeIndex = 1;
			string paramList = "(";

			foreach (XmlNode parameterNode in parameterNodes)
			{
				paramList += StripNamespace(parameterNode.Attributes["type"].Value);

				if (nodeIndex < numberOfNodes)
				{
					paramList += ", ";
				}

				nodeIndex++;
			}

			paramList += ")";

			return paramList;
		}

		private int[] SortNodesByAttribute(XmlNodeList nodes, string attributeName)
		{
			int length = nodes.Count;
			string[] names = new string[length];
			int[] indexes = new int[length];
			int i = 0;

			foreach (XmlNode node in nodes)
			{
				names[i] = (string)node.Attributes[attributeName].Value;
				indexes[i] = i++;
			}

			Array.Sort(names, indexes);

			return indexes;
		}

		private void TransformAndWriteResult(
			XslTransform transform,
			XsltArgumentList arguments,
			string filename)
		{
			Trace.WriteLine(filename);
#if DEBUG
			int start = Environment.TickCount;
#endif

			ExternalHtmlProvider htmlProvider = new ExternalHtmlProvider(MyConfig, filename);
			StreamWriter streamWriter = null;
			
			// TODO: Edit (UTF8, Shift_JIS 両出力化)
			try
			{ 
				// とりあえず、UTF-8 で出力させる
				streamWriter =  new StreamWriter(
					File.Open(Path.Combine(MyConfig.OutputDirectory, filename) + ".tmp", FileMode.Create),
					new UTF8Encoding(true));
				
				arguments.AddParam("ndoc-title", String.Empty, MyConfig.Title);
				arguments.AddParam("ndoc-vb-syntax", String.Empty, MyConfig.ShowVisualBasic);
				arguments.AddParam("ndoc-omit-object-tags", String.Empty, ((MyConfig.OutputTarget & OutputType.HtmlHelp) == 0));
				arguments.AddParam("ndoc-document-attributes", String.Empty, MyConfig.DocumentAttributes);
				arguments.AddParam("ndoc-documented-attributes", String.Empty, MyConfig.DocumentedAttributes);

				MsdnXsltUtilities utilities = new MsdnXsltUtilities(fileNames, elemNames, MyConfig.LinkToSdkDocVersion);

				arguments.AddParam("ndoc-sdk-doc-base-url", String.Empty, utilities.SdkDocBaseUrl);
				arguments.AddParam("ndoc-sdk-doc-file-ext", String.Empty, utilities.SdkDocExt);

				arguments.AddExtensionObject("urn:NDocUtil", utilities);
				arguments.AddExtensionObject("urn:NDocExternalHtml", htmlProvider);

				transform.Transform(xmlDocumentation, arguments, streamWriter);
				
				streamWriter.Close();
				streamWriter = null;
				
				// UTF-8 で作成したファイルを、Shift_JIS に変換する
				StreamReader streamReader = new StreamReader(Path.Combine(MyConfig.OutputDirectory, filename) + ".tmp", new UTF8Encoding(true));
				streamWriter =  new StreamWriter(Path.Combine(MyConfig.OutputDirectory, filename), false, Encoding.GetEncoding(932));
				string tempText = streamReader.ReadLine();
				
				while (tempText != null)
				{
					tempText = tempText.Replace("UTF-8", "Shift_JIS");
					tempText = tempText.Replace("utf-8", "Shift_JIS");
					tempText = tempText.Replace("Utf-8", "Shift_JIS");
					tempText = tempText.Replace("&amp;", "&");
					
					streamWriter.WriteLine(tempText);
					tempText = streamReader.ReadLine();
				}
				
				streamWriter.Close();
				streamReader.Close();
				streamWriter = null;
				streamReader = null;
			}
			finally
			{
				if (File.Exists(Path.Combine(MyConfig.OutputDirectory, filename) + ".tmp") == true)
				{
					File.Delete(Path.Combine(MyConfig.OutputDirectory, filename) + ".tmp");
				}
			}
			// ここまで

#if DEBUG
			Trace.WriteLine((Environment.TickCount - start).ToString() + " msec.");
#endif
			htmlHelp.AddFileToProject(filename);
		}

		private string RemoveChar(string s, char c)
		{
			StringBuilder builder = new StringBuilder();

			foreach (char ch in s.ToCharArray())
			{
				if (ch != c)
				{
					builder.Append(ch);
				}
			}

			return builder.ToString();
		}

		private string GetFilenameForNamespace(string namespaceName)
		{
			string fileName = namespaceName + ".html";
			return fileName;
		}

		private string GetFilenameForType(XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + ".html";
			return fileName;
		}

		private string GetFilenameForTypeMembers(XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Members.html";
			return fileName;
		}

		private string GetFilenameForConstructors(XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Constructor.html";
			return fileName;
		}

		private string GetFilenameForConstructor(XmlNode constructorNode)
		{
			string constructorID = (string)constructorNode.Attributes["id"].Value;
			int dotHash = constructorID.IndexOf(".#"); // constructors could be #ctor or #cctor

			string fileName = constructorID.Substring(2, dotHash - 2);

			fileName += "Constructor";

			if (constructorNode.Attributes["overload"] != null)
			{
				fileName += (string)constructorNode.Attributes["overload"].Value;
			}

			fileName += ".html";

			return fileName;
		}

		private string GetFilenameForFields(WhichType whichType, XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Fields.html";
			return fileName;
		}

		private string GetFilenameForField(XmlNode fieldNode)
		{
			string fieldID = (string)fieldNode.Attributes["id"].Value;
			string fileName = fieldID.Substring(2) + ".html";
			return fileName;
		}

		private string GetFilenameForOperators(WhichType whichType, XmlNode typeNode)
		{
			string typeID = typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Operators.html";
			return fileName;
		}

		private string GetFilenameForOperatorsOverloads(XmlNode typeNode, XmlNode opNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string opName = (string)opNode.Attributes["name"].Value;
			string fileName = typeID.Substring(2) + "." + opName + ".html";
			return fileName;
		}

		private string GetFilenameForOperator(XmlNode operatorNode)
		{
			string operatorID = operatorNode.Attributes["id"].Value;
			string fileName = operatorID.Substring(2);

//			int opIndex = fileName.IndexOf("op_");
//
//			if (opIndex != -1)
//			{
//				fileName = fileName.Remove(opIndex, 3);
//			}

			int leftParenIndex = fileName.IndexOf('(');

			if (leftParenIndex != -1)
			{
				fileName = fileName.Substring(0, leftParenIndex);
			}

			if (operatorNode.Attributes["overload"] != null)
			{
				fileName += operatorNode.Attributes["overload"].Value;
			}

			fileName += ".html";

			return fileName;
		}

		private string GetFilenameForEvents(WhichType whichType, XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Events.html";
			return fileName;
		}

		private string GetFilenameForEvent(XmlNode eventNode)
		{
			string eventID = (string)eventNode.Attributes["id"].Value;
			string fileName = eventID.Substring(2) + ".html";
			return fileName;
		}

		private string GetFilenameForProperties(WhichType whichType, XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Properties.html";
			return fileName;
		}

		private string GetFilenameForPropertyOverloads(XmlNode typeNode, XmlNode propertyNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string propertyName = (string)propertyNode.Attributes["name"].Value;
			string fileName = typeID.Substring(2) + propertyName + ".html";
			return fileName;
		}

		private string GetFilenameForProperty(XmlNode propertyNode)
		{
			string propertyID = (string)propertyNode.Attributes["id"].Value;
			string fileName = propertyID.Substring(2);

			int leftParenIndex = fileName.IndexOf('(');

			if (leftParenIndex != -1)
			{
				fileName = fileName.Substring(0, leftParenIndex);
			}

			if (propertyNode.Attributes["overload"] != null)
			{
				fileName += (string)propertyNode.Attributes["overload"].Value;
			}

			fileName += ".html";

			return fileName;
		}

		private string GetFilenameForMethods(WhichType whichType, XmlNode typeNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string fileName = typeID.Substring(2) + "Methods.html";
			return fileName;
		}

		private string GetFilenameForMethodOverloads(XmlNode typeNode, XmlNode methodNode)
		{
			string typeID = (string)typeNode.Attributes["id"].Value;
			string methodName = (string)methodNode.Attributes["name"].Value;
			string fileName = typeID.Substring(2) + "." + methodName + "_overloads.html";
			return fileName;
		}

		private string GetFilenameForMethod(XmlNode methodNode)
		{
			string methodID = (string)methodNode.Attributes["id"].Value;
			string fileName = methodID.Substring(2);

			int leftParenIndex = fileName.IndexOf('(');

			if (leftParenIndex != -1)
			{
				fileName = fileName.Substring(0, leftParenIndex);
			}

			fileName = RemoveChar(fileName, '#');

			if (methodNode.Attributes["overload"] != null)
			{
				fileName += "_overload_" + (string)methodNode.Attributes["overload"].Value;
			}

			fileName += ".html";

			return fileName;
		}
	}
}
