diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-20 21:18:59 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-21 08:06:22 -0700 |
commit | bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8 (patch) | |
tree | c91f66e642c4d26fca266e226b3f2765f546d700 /tools/Sandcastle/Source/BuildAssembler/BuildComponents | |
parent | 627014f0bbc3fd576277375e70f8391d150b0a67 (diff) | |
download | DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.zip DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.tar.gz DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.tar.bz2 |
Switched out the Sandcastle binaries for the source code.
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildComponents')
43 files changed, 12222 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj new file mode 100644 index 0000000..7b3191e --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj @@ -0,0 +1,153 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.30729</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{30773718-BC7C-4FCC-A9C2-7EE61DF4EC41}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>BuildComponents</RootNamespace> + <AssemblyName>BuildComponents</AssemblyName> + <SccProjectName> + </SccProjectName> + <SccLocalPath> + </SccLocalPath> + <SccAuxPath> + </SccAuxPath> + <SccProvider> + </SccProvider> + <SignAssembly>false</SignAssembly> + <AssemblyOriginatorKeyFile>../../../key.snk</AssemblyOriginatorKeyFile> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>2.0</OldToolsVersion> + <UpgradeBackupLocation> + </UpgradeBackupLocation> + <IsWebBootstrapper>false</IsWebBootstrapper> + <PublishUrl>publish\</PublishUrl> + <Install>true</Install> + <InstallFrom>Disk</InstallFrom> + <UpdateEnabled>false</UpdateEnabled> + <UpdateMode>Foreground</UpdateMode> + <UpdateInterval>7</UpdateInterval> + <UpdateIntervalUnits>Days</UpdateIntervalUnits> + <UpdatePeriodically>false</UpdatePeriodically> + <UpdateRequired>false</UpdateRequired> + <MapFileExtensions>true</MapFileExtensions> + <ApplicationRevision>0</ApplicationRevision> + <ApplicationVersion>1.0.0.%2a</ApplicationVersion> + <UseApplicationTrust>false</UseApplicationTrust> + <BootstrapperEnabled>true</BootstrapperEnabled> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WebDocsDebug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\WebDocsDebug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression> + <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile> + <ErrorReport>prompt</ErrorReport> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Sandcastle|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Sandcastle\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression> + <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile> + <ErrorReport>prompt</ErrorReport> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.configuration" /> + <Reference Include="System.Data" /> + <Reference Include="System.Web" /> + <Reference Include="System.Web.Services" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="HxfGeneratorComponent.cs" /> + <Compile Include="IntellisenseComponent2.cs" /> + <Compile Include="MsdnResolver.cs" /> + <Compile Include="References.cs" /> + <Compile Include="ResolveReferenceLinksComponent2.cs" /> + <Compile Include="CloneComponent.cs" /> + <Compile Include="CodeReference.cs" /> + <Compile Include="CopyFromFileComponent.cs" /> + <Compile Include="CopyFromFiles.cs" /> + <Compile Include="CopyFromIndexComponent.cs" /> + <Compile Include="DisplayComponent.cs" /> + <Compile Include="ExampleComponent.cs" /> + <Compile Include="ForEachComponent.cs" /> + <Compile Include="IfThenComponent.cs" /> + <Compile Include="IndexedFileCache.cs" /> + <Compile Include="IntellisenseComponent.cs" /> + <Compile Include="LiveExampleComponent.cs" /> + <Compile Include="MsdnService.cs" /> + <Compile Include="PlatformsComponent.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="ResolveArtLinksComponent.cs" /> + <Compile Include="ResolveConceptualLinksComponent.cs" /> + <Compile Include="SaveComponent.cs" /> + <Compile Include="SharedContentComponent.cs" /> + <Compile Include="SwitchComponent.cs" /> + <Compile Include="SyntaxComponent.cs" /> + <Compile Include="TargetCollection.cs" /> + <Compile Include="Targets.cs" /> + <Compile Include="TaskGrabberComponent.cs" /> + <Compile Include="TransformComponent.cs" /> + <Compile Include="ValidateComponent.cs" /> + <Compile Include="WdxResolveConceptualLinksComponent.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\BuildAssemblerLibrary\BuildAssemblerLibrary.csproj"> + <Project>{399E78F8-4954-409E-991A-37DA9D0579CC}</Project> + <Name>BuildAssemblerLibrary</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <BootstrapperPackage Include="Microsoft.Net.Framework.2.0"> + <Visible>False</Visible> + <ProductName>.NET Framework 2.0 %28x86%29</ProductName> + <Install>true</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.0"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.0 %28x86%29</ProductName> + <Install>false</Install> + </BootstrapperPackage> + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5"> + <Visible>False</Visible> + <ProductName>.NET Framework 3.5</ProductName> + <Install>false</Install> + </BootstrapperPackage> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <!-- Copy the output assemblies to a common binaries directory (ProductionTools). --> + <Target Name="AfterBuild"> + <CreateItem Include="$(OutputPath)\$(AssemblyName).*"> + <Output TaskParameter="Include" ItemName="ProductionFiles" /> + </CreateItem> + <Copy SourceFiles="@(ProductionFiles)" DestinationFolder="..\..\..\ProductionTools" /> + </Target> +</Project>
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj.vspscc b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj.vspscc new file mode 100644 index 0000000..b6d3289 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CloneComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CloneComponent.cs new file mode 100644 index 0000000..c97d68d --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CloneComponent.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using System.Reflection; + +namespace Microsoft.Ddue.Tools { + + public class CloneComponent : BuildComponent { + + private List<IEnumerable<BuildComponent>> branches = new List<IEnumerable<BuildComponent>>(); + + public CloneComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNodeIterator branch_nodes = configuration.Select("branch"); + foreach (XPathNavigator branch_node in branch_nodes) { + BuildComponent[] branch = BuildAssembler.LoadComponents(branch_node); + branches.Add(branch); + } + + } + + public override void Apply (XmlDocument document, string key) { + + foreach (IEnumerable<BuildComponent> branch in branches) { + XmlDocument subdocument = document.Clone() as XmlDocument; + foreach(BuildComponent component in branch) { + component.Apply(subdocument, key); + } + } + + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CodeReference.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CodeReference.cs new file mode 100644 index 0000000..974a28d --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CodeReference.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Text.RegularExpressions; +using System.Collections.Generic; + + +namespace Microsoft.Ddue.Tools { + + public enum CodeReferenceType { + Invalid, // not initialized, invalid reference string + Snippet, // MSDN style snippet for all build targets + Msdn, // MSDN style snippet for MSDN build only + Run, // runnable code sample with pop-up source browser for WebDocs build + View // code sample shown in pop-up source browser for WebDocs build + } + + /// <summary> + /// The CodeReference class encapsulates DDUE code reference elements and provides easy access + /// to individual code reference parts, such as the type, name, title, etc.. + /// </summary> + /// <remarks> + /// Examples of valid code reference strings include: + /// - SampleName#SnippetNumber + /// - SampleName#SnippetNumber1,SnippetNumber2,SnippetNumber3 + /// - msdn:SampleName#SnippetNumber + /// - run:SampleName + /// - run:SampleName;title text + /// - run:SampleName#startPage.aspx + /// - run:SampleName/path/to/startPage.aspx + /// - run:SampleName#startPage.aspx;title text + /// - run:SampleName/path/to/startPage.aspx;title text + /// - view:SampleName + /// - view:SampleName#defaultFile + /// - view:SampleName/path/to/defaultFile + /// - view:SampleName#defaultFile;title text + /// - view:SampleName/path/to/defaultFile;title text + /// </remarks> + public class CodeReference { + + string _ddueCodeReference; + public string DdueCodeReference { + get { return _ddueCodeReference; } + } + + CodeReferenceType _type; + public CodeReferenceType Type { + get { return _type; } + } + + string _exampleName; + public string ExampleName { + get { return _exampleName; } + } + + string _examplePath; + public string ExamplePath { + get { return _examplePath; } + } + + string _snippetId; + public string SnippetId { + get { return _snippetId; } + } + + string _title; + public string Title { + get { return _title; } + } + + public CodeReference(string ddueCodeReference) { + _ddueCodeReference = ddueCodeReference; + Parse(); + } + + static Regex codeReferenceRx = new Regex( + @"^\s*(" + + @"((?<type>msdn|run|view):)?" + + @"(?<examplePath>(" + + @"(?<exampleName>[\w\.,\-]+)" + + @"((#(?<snippetId>[\w,]+))|(([/\\#,\.\w\-]+)?))" + + @"))" + + @"(;(?<title>.*))?" + + @")\s*$", + RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + void Parse() { + Match m = codeReferenceRx.Match(DdueCodeReference); + if (m.Success) { + _exampleName = m.Groups["exampleName"].Value; + _snippetId = m.Groups["snippetId"].Value; + _examplePath = m.Groups["examplePath"].Value; + _title = m.Groups["title"].Value; + // The default value of _type is CodeReferenceType.Invalid, if it isn't set in the following + // block. + if (m.Groups["type"].Length > 0) { + try { + _type = (CodeReferenceType)Enum.Parse(typeof(CodeReferenceType), m.Groups["type"].Value, true); + } + catch (ArgumentException) { + // _type = CodeReferenceType.Invalid + } + } + else if (m.Groups["exampleName"].Length > 0 && m.Groups["snippetId"].Length > 0) { + _type = CodeReferenceType.Snippet; + } + } + } + + public override string ToString() { + return DdueCodeReference; + } + } +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ComputeHashComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ComputeHashComponent.cs new file mode 100644 index 0000000..37755ab --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ComputeHashComponent.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using System.Xml.XPath; + + +namespace Microsoft.Ddue.Tools { + + internal class HashComputation { + + public HashComputation (string input, string output) { + Input = XPathExpression.Compile(input); + Output = XPathExpression.Compile(output); + } + + public XPathExpression Input; + public XPathExpression Output; + + } + + public class ComputeHashComponent : BuildComponent { + + public ComputeHashComponent (XPathNavigator configuration) : base(configuration) { + + if (configuration == null) throw new ArgumentNullException("configuraton"); + + XPathNodeIterator hash_nodes = configuration.Select("hash"); + foreach (XPathNavigator hash_node in hash_nodes) { + string input_xpath = hash_node.GetAttribute("input", String.Empty); + string output_xpath = hash_node.GetAttribute("output", String.Empty); + computations.Add( new HashComputation(input_xpath, output_xpath) ); + } + + } + + // A list of the hash computations to do + + private List<HashComputation> computations = new List<HashComputation>(); + + // Logic to compute a unique hash of a comment id string + + private static Guid ComputeHash (string key) { + byte[] input = Encoding.UTF8.GetBytes(key); + byte[] output = md5.ComputeHash(input); + return( new Guid(output) ); + } + + private static HashAlgorithm md5 = new MD5CryptoServiceProvider(); + + // The actual action of the component + + public override void Apply (XmlDocument document, string key) { + + Guid id = ComputeHash(key); + + foreach (HashComputation computation in computations) { + XPathNavigator output = document.CreateNavigator().SelectSingleNode(computation.Output); + if (output == null) continue; + output.SetValue(id.ToString()); + } + + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFileComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFileComponent.cs new file mode 100644 index 0000000..54d26e0 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFileComponent.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class CopyFromFileComponent : BuildComponent { + + public CopyFromFileComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + if (configuration == null) throw new ArgumentNullException("configuration"); + + string data_name = null; + + // get information about the data file + XPathNodeIterator data_nodes = configuration.Select("data"); + foreach (XPathNavigator data_node in data_nodes) { + string data_file = data_node.GetAttribute("file", String.Empty); + if (String.IsNullOrEmpty(data_file)) WriteMessage(MessageLevel.Error, "Data elements must have a file attribute specifying a file from which to load data."); + data_file = Environment.ExpandEnvironmentVariables(data_file); + + data_name = data_node.GetAttribute("name", String.Empty); + if (String.IsNullOrEmpty(data_name)) data_name = Guid.NewGuid().ToString(); + + // load a schema, if one is specified + string schema_file = data_node.GetAttribute("schema", String.Empty); + XmlReaderSettings settings = new XmlReaderSettings(); + if (!String.IsNullOrEmpty(schema_file)) { + settings.Schemas.Add(null, schema_file); + } + + // load the document + WriteMessage(MessageLevel.Info, String.Format("Loading data file '{0}'.", data_file) ); + using (XmlReader reader = XmlReader.Create(data_file, settings)) { + XPathDocument data_document = new XPathDocument(reader); + Data.Add(data_name, data_document); + } + } + + + // get the source and target expressions for each copy command + XPathNodeIterator copy_nodes = configuration.Select("copy"); + foreach (XPathNavigator copy_node in copy_nodes) { + string source_name = copy_node.GetAttribute("name", String.Empty); + if (String.IsNullOrEmpty(source_name)) source_name = data_name; + + XPathDocument source_document = (XPathDocument) Data[source_name]; + + string source_xpath = copy_node.GetAttribute("source", String.Empty); + if (String.IsNullOrEmpty(source_xpath)) throw new ConfigurationErrorsException("When instantiating a CopyFromFile component, you must specify a source xpath format using the source attribute."); + string target_xpath = copy_node.GetAttribute("target", String.Empty); + if (String.IsNullOrEmpty(target_xpath)) throw new ConfigurationErrorsException("When instantiating a CopyFromFile component, you must specify a target xpath format using the target attribute."); + copy_commands.Add( new CopyFromFileCommand(source_document, source_xpath, target_xpath) ); + } + + } + + // private XPathDocument data_document; + + private List<CopyFromFileCommand> copy_commands = new List<CopyFromFileCommand>(); + + private CustomContext context = new CustomContext(); + + // the work of the component + + public override void Apply (XmlDocument document, string key) { + + // set the key in the XPath context + context["key"] = key; + + // iterate over the copy commands + foreach (CopyFromFileCommand copy_command in copy_commands) { + + // extract the target node + XPathExpression target_xpath = copy_command.Target.Clone(); + target_xpath.SetContext(context); + XPathNavigator target = document.CreateNavigator().SelectSingleNode(target_xpath); + + // warn if target not found? + if (target == null) { + continue; + } + + // extract the source nodes + XPathExpression source_xpath = copy_command.Source.Clone(); + source_xpath.SetContext(context); + XPathNodeIterator sources = copy_command.SourceDocument.CreateNavigator().Select(source_xpath); + + // warn if source not found? + + // append the source nodes to the target node + foreach (XPathNavigator source in sources) { + target.AppendChild(source); + } + + } + + } + + } + + + // a representation of a copying operation + + internal class CopyFromFileCommand { + + public CopyFromFileCommand (XPathDocument source_document, string source_xpath, string target_xpath) { + this.source_document = source_document; + source = XPathExpression.Compile(source_xpath); + target = XPathExpression.Compile(target_xpath); + } + + private XPathDocument source_document; + + private XPathExpression source; + + private XPathExpression target; + + public XPathDocument SourceDocument { + get { + return(source_document); + } + } + + public XPathExpression Source { + get { + return(source); + } + } + + public XPathExpression Target { + get { + return(target); + } + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFiles.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFiles.cs new file mode 100644 index 0000000..788fafd --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFiles.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class CopyFromFilesComponent : BuildComponent { + + public CopyFromFilesComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + XPathNodeIterator copy_nodes = configuration.Select("copy"); + foreach (XPathNavigator copy_node in copy_nodes) { + + string root_value = copy_node.GetAttribute("base", String.Empty); + if (String.IsNullOrEmpty(root_value)) root_value = Environment.CurrentDirectory; + root_value = Environment.ExpandEnvironmentVariables(root_value); + if (!Directory.Exists(root_value)) WriteMessage(MessageLevel.Error, String.Format("The base directory '{0}' does not exist.", root_value)); + + string file_value = copy_node.GetAttribute("file", String.Empty); + if (String.IsNullOrEmpty(file_value)) WriteMessage(MessageLevel.Error, "Each copy element must have a file attribute specifying the file to copy from."); + + string source_value = copy_node.GetAttribute("source", String.Empty); + string target_value = copy_node.GetAttribute("target", String.Empty); + + CopyFromFilesCommand copy_command = new CopyFromFilesCommand(root_value, file_value, source_value, target_value); + copy_commands.Add(copy_command); + } + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} copy commands.", copy_commands.Count)); + } + + List<CopyFromFilesCommand> copy_commands = new List<CopyFromFilesCommand>(); + + private CustomContext context = new CustomContext(); + + public override void Apply (XmlDocument document, string key) { + context["key"] = key; + foreach (CopyFromFilesCommand copy_command in copy_commands) { + copy_command.Apply(document, context); + } + } + + } + + internal class CopyFromFilesCommand { + + public CopyFromFilesCommand (string root, string file, string source, string target) { + root_directory = root; + file_expression = XPathExpression.Compile(file); + source_expression = XPathExpression.Compile(source); + target_expression = XPathExpression.Compile(target); + } + + private string root_directory; + + private XPathExpression file_expression; + + private XPathExpression source_expression; + + private XPathExpression target_expression; + + public void Apply (XmlDocument targetDocument, IXmlNamespaceResolver context) { + + XPathExpression local_file_expression = file_expression.Clone(); + local_file_expression.SetContext(context); + + XPathExpression local_source_expression = source_expression.Clone(); + local_source_expression.SetContext(context); + + XPathExpression local_target_expression = target_expression.Clone(); + local_target_expression.SetContext(context); + + string file_name = (string) targetDocument.CreateNavigator().Evaluate(local_file_expression); + string file_path = Path.Combine(root_directory, file_name); + + if (!File.Exists(file_path)) return; + XPathDocument sourceDocument = new XPathDocument(file_path); + + XPathNavigator target_node = targetDocument.CreateNavigator().SelectSingleNode(local_target_expression); + if (target_node == null) return; + + XPathNodeIterator source_nodes = sourceDocument.CreateNavigator().Select(local_source_expression); + foreach (XPathNavigator source_node in source_nodes) { + target_node.AppendChild(source_node); + } + + } + } +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromIndexComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromIndexComponent.cs new file mode 100644 index 0000000..abb3644 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromIndexComponent.cs @@ -0,0 +1,674 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Xml; +using System.Xml.XPath; +using System.Reflection; +using System.Xml.Xsl; /* for custom context stuff */ + +namespace Microsoft.Ddue.Tools { + + public class CopyFromIndexComponent : BuildComponent { + + // XPath search patterns + + // List of copy components + private List<CopyComponent> components = new List<CopyComponent>(); + + // what to copy + private List<CopyCommand> copy_commands = new List<CopyCommand>(); + + // a context in which to evaluate XPath expressions + private CustomContext context = new CustomContext(); + + public CopyFromIndexComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) { + + // set up the context + XPathNodeIterator context_nodes = configuration.Select("context"); + foreach (XPathNavigator context_node in context_nodes) { + string prefix = context_node.GetAttribute("prefix", String.Empty); + string name = context_node.GetAttribute("name", String.Empty); + context.AddNamespace(prefix, name); + } + + // set up the indices + XPathNodeIterator index_nodes = configuration.Select("index"); + foreach (XPathNavigator index_node in index_nodes) { + + // get the name of the index + string name = index_node.GetAttribute("name", String.Empty); + if (String.IsNullOrEmpty(name)) throw new ConfigurationErrorsException("Each index must have a unique name."); + + // get the xpath for value nodes + string value_xpath = index_node.GetAttribute("value", String.Empty); + if (String.IsNullOrEmpty(value_xpath)) WriteMessage(MessageLevel.Error, "Each index element must have a value attribute containing an XPath that describes index entries."); + + // get the xpath for keys (relative to value nodes) + string key_xpath = index_node.GetAttribute("key", String.Empty); + if (String.IsNullOrEmpty(key_xpath)) WriteMessage(MessageLevel.Error, "Each index element must have a key attribute containing an XPath (relative to the value XPath) that evaluates to the entry key."); + + // get the cache size + int cache = 10; + string cache_value = index_node.GetAttribute("cache", String.Empty); + if (!String.IsNullOrEmpty(cache_value)) cache = Convert.ToInt32(cache_value); + + // create the index + IndexedDocumentCache index = new IndexedDocumentCache(this, key_xpath, value_xpath, context, cache); + + // search the data directories for entries + XPathNodeIterator data_nodes = index_node.Select("data"); + foreach (XPathNavigator data_node in data_nodes) { + + string base_value = data_node.GetAttribute("base", String.Empty); + if (!String.IsNullOrEmpty(base_value)) base_value = Environment.ExpandEnvironmentVariables(base_value); + + bool recurse = false; + string recurse_value = data_node.GetAttribute("recurse", String.Empty); + if (!String.IsNullOrEmpty(recurse_value)) recurse = (bool)Convert.ToBoolean(recurse_value); + + // get the files + string files = data_node.GetAttribute("files", String.Empty); + if (String.IsNullOrEmpty(files)) WriteMessage(MessageLevel.Error, "Each data element must have a files attribute specifying which files to index."); + // if ((files == null) || (files.Length == 0)) throw new ConfigurationErrorsException("When instantiating a CopyFromDirectory component, you must specify a directory path using the files attribute."); + files = Environment.ExpandEnvironmentVariables(files); + + WriteMessage(MessageLevel.Info, String.Format("Searching for files that match '{0}'.", files)); + index.AddDocuments(base_value, files, recurse); + + } + WriteMessage(MessageLevel.Info, String.Format("Indexed {0} elements in {1} files.", index.Count, index.DocumentCount)); + + Data.Add(name, index); + + } + + // get the copy commands + XPathNodeIterator copy_nodes = configuration.Select("copy"); + foreach (XPathNavigator copy_node in copy_nodes) { + + string source_name = copy_node.GetAttribute("name", String.Empty); + if (String.IsNullOrEmpty(source_name)) throw new ConfigurationErrorsException("Each copy command must specify an index to copy from."); + + string key_xpath = copy_node.GetAttribute("key", String.Empty); + + string source_xpath = copy_node.GetAttribute("source", String.Empty); + if (String.IsNullOrEmpty(source_xpath)) throw new ConfigurationErrorsException("When instantiating a CopyFromDirectory component, you must specify a source xpath format using the source attribute."); + + string target_xpath = copy_node.GetAttribute("target", String.Empty); + if (String.IsNullOrEmpty(target_xpath)) throw new ConfigurationErrorsException("When instantiating a CopyFromDirectory component, you must specify a target xpath format using the target attribute."); + + string attribute_value = copy_node.GetAttribute("attribute", String.Empty); + + string ignoreCase_value = copy_node.GetAttribute("ignoreCase", String.Empty); + + string missingEntryValue = copy_node.GetAttribute("missing-entry", String.Empty); + string missingSourceValue = copy_node.GetAttribute("missing-source", String.Empty); + string missingTargetValue = copy_node.GetAttribute("missing-target", String.Empty); + + IndexedDocumentCache index = (IndexedDocumentCache)Data[source_name]; + + CopyCommand copyCommand = new CopyCommand(index, key_xpath, source_xpath, target_xpath, attribute_value, ignoreCase_value); + if (!String.IsNullOrEmpty(missingEntryValue)) { + try { + copyCommand.MissingEntry = (MessageLevel)Enum.Parse(typeof(MessageLevel), missingEntryValue, true); + } catch (ArgumentException) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a message level.", missingEntryValue)); + } + } + if (!String.IsNullOrEmpty(missingSourceValue)) { + try { + copyCommand.MissingSource = (MessageLevel)Enum.Parse(typeof(MessageLevel), missingSourceValue, true); + } catch (ArgumentException) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a message level.", missingSourceValue)); + } + } + if (!String.IsNullOrEmpty(missingTargetValue)) { + try { + copyCommand.MissingTarget = (MessageLevel)Enum.Parse(typeof(MessageLevel), missingTargetValue, true); + } catch (ArgumentException) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a message level.", missingTargetValue)); + } + } + + copy_commands.Add(copyCommand); + + } + + XPathNodeIterator component_nodes = configuration.Select("components/component"); + foreach (XPathNavigator component_node in component_nodes) { + + // get the data to load the component + string assembly_path = component_node.GetAttribute("assembly", String.Empty); + if (String.IsNullOrEmpty(assembly_path)) WriteMessage(MessageLevel.Error, "Each component element must have an assembly attribute."); + string type_name = component_node.GetAttribute("type", String.Empty); + if (String.IsNullOrEmpty(type_name)) WriteMessage(MessageLevel.Error, "Each component element must have a type attribute."); + + // expand environment variables in the path + assembly_path = Environment.ExpandEnvironmentVariables(assembly_path); + + //Console.WriteLine("loading {0} from {1}", type_name, assembly_path); + try + { + Assembly assembly = Assembly.LoadFrom(assembly_path); + CopyComponent component = (CopyComponent)assembly.CreateInstance(type_name, false, BindingFlags.Public | BindingFlags.Instance, null, new Object[2] { component_node.Clone(), Data }, null, null); + + if (component == null) + { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' does not exist in the assembly '{1}'.", type_name, assembly_path)); + } + else + { + components.Add(component); + } + + } + catch (IOException e) + { + WriteMessage(MessageLevel.Error, String.Format("A file access error occured while attempting to load the build component '{0}'. The error message is: {1}", assembly_path, e.Message)); + } + catch (BadImageFormatException e) + { + WriteMessage(MessageLevel.Error, String.Format("A syntax generator assembly '{0}' is invalid. The error message is: {1}.", assembly_path, e.Message)); + } + catch (TypeLoadException e) + { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' does not exist in the assembly '{1}'. The error message is: {2}", type_name, assembly_path, e.Message)); + } + catch (MissingMethodException e) + { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' in the assembly '{1}' does not have an appropriate constructor. The error message is: {2}", type_name, assembly_path, e.Message)); + } + catch (TargetInvocationException e) + { + WriteMessage(MessageLevel.Error, String.Format("An error occured while attempting to instantiate the type '{0}' in the assembly '{1}'. The error message is: {2}", type_name, assembly_path, e.InnerException.Message)); + } + catch (InvalidCastException e) + { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' in the assembly '{1}' is not a SyntaxGenerator.", type_name, assembly_path)); + } + } + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} copy components.", components.Count)); + + } + + // the actual work of the component + + public override void Apply(XmlDocument document, string key) { + + // set the key in the XPath context + context["key"] = key; + + // perform each copy action + foreach (CopyCommand copy_command in copy_commands) { + + // get the source comment + XPathExpression key_expression = copy_command.Key.Clone(); + key_expression.SetContext(context); + // Console.WriteLine(key_expression.Expression); + string key_value = (string)document.CreateNavigator().Evaluate(key_expression); + // Console.WriteLine("got key '{0}'", key_value); + XPathNavigator data = copy_command.Index.GetContent(key_value); + + if (data == null && copy_command.IgnoreCase == "true") data = copy_command.Index.GetContent(key_value.ToLower()); + + // notify if no entry + if (data == null) { + WriteMessage(copy_command.MissingEntry, String.Format("No index entry found for key '{0}'.", key_value)); + continue; + } + + // get the target node + String target_xpath = copy_command.Target.Clone().ToString(); + XPathExpression target_expression = XPathExpression.Compile(string.Format(target_xpath, key_value)); + target_expression.SetContext(context); + + XPathNavigator target = document.CreateNavigator().SelectSingleNode(target_expression); + + // notify if no target found + if (target == null) { + WriteMessage(copy_command.MissingTarget, String.Format("Target node '{0}' not found.", target_expression.Expression)); + continue; + } + + // get the source nodes + XPathExpression source_expression = copy_command.Source.Clone(); + source_expression.SetContext(context); + XPathNodeIterator sources = data.CreateNavigator().Select(source_expression); + + // append the source nodes to the target node + int source_count = 0; + foreach (XPathNavigator source in sources) { + source_count++; + + // If attribute=true, add the source attributes to current target. + // Otherwise append source as a child element to target + if (copy_command.Attribute == "true" && source.HasAttributes) { + string source_name = source.LocalName; + XmlWriter attributes = target.CreateAttributes(); + + source.MoveToFirstAttribute(); + string attrFirst = target.GetAttribute(string.Format("{0}_{1}", source_name, source.Name), string.Empty); + if (string.IsNullOrEmpty(attrFirst)) attributes.WriteAttributeString(string.Format("{0}_{1}", source_name, source.Name), source.Value); + + while (source.MoveToNextAttribute()) { + string attrNext = target.GetAttribute(string.Format("{0}_{1}", source_name, source.Name), string.Empty); + if (string.IsNullOrEmpty(attrNext)) attributes.WriteAttributeString(string.Format("{0}_{1}", source_name, source.Name), source.Value); + } + attributes.Close(); + } + else target.AppendChild(source); + } + + // notify if no source found + if (source_count == 0) { + WriteMessage(copy_command.MissingSource, String.Format("Source node '{0}' not found.", source_expression.Expression)); + } + + foreach (CopyComponent component in components) + { + component.Apply(document, key); + } + } + } + + internal void WriteHelperMessage(MessageLevel level, string message) { + WriteMessage(level, message); + } + + } + + // the storage system + + public class IndexedDocumentCache { + + public IndexedDocumentCache(CopyFromIndexComponent component, string keyXPath, string valueXPath, XmlNamespaceManager context, int cacheSize) { + + if (component == null) throw new ArgumentNullException("component"); + if (cacheSize < 0) throw new ArgumentOutOfRangeException("cacheSize"); + + this.component = component; + + try { + keyExpression = XPathExpression.Compile(keyXPath); + } catch (XPathException) { + component.WriteHelperMessage(MessageLevel.Error, String.Format("The key expression '{0}' is not a valid XPath expression.", keyXPath)); + } + keyExpression.SetContext(context); + + try { + valueExpression = XPathExpression.Compile(valueXPath); + } catch (XPathException) { + component.WriteHelperMessage(MessageLevel.Error, String.Format("The value expression '{0}' is not a valid XPath expression.", valueXPath)); + } + valueExpression.SetContext(context); + + this.cacheSize = cacheSize; + + // set up the cache + cache = new Dictionary<string, IndexedDocument>(cacheSize); + queue = new Queue<string>(cacheSize); + } + + // index component to which the cache belongs + private CopyFromIndexComponent component; + + public CopyFromIndexComponent Component { + get { + return (component); + } + } + + // search pattern for index values + private XPathExpression valueExpression; + + public XPathExpression ValueExpression { + get { + return (valueExpression); + } + } + + // search pattern for the index keys (relative to the index value node) + private XPathExpression keyExpression; + + public XPathExpression KeyExpression { + get { + return (keyExpression); + } + } + + // a index mapping keys to the files that contain them + private Dictionary<string, string> index = new Dictionary<string, string>(); + + public void AddDocument(string file) { + + // load the document + IndexedDocument document = new IndexedDocument(this, file); + + // record the keys + string[] keys = document.GetKeys(); + foreach (string key in keys) { + if (index.ContainsKey(key)) { + component.WriteHelperMessage(MessageLevel.Warn, String.Format("Entries for the key '{0}' occur in both '{1}' and '{2}'. The last entry will be used.", key, index[key], file)); + } + index[key] = file; + + } + + } + + public void AddDocuments(string wildcardPath) { + string directory_part = Path.GetDirectoryName(wildcardPath); + if (String.IsNullOrEmpty(directory_part)) directory_part = Environment.CurrentDirectory; + directory_part = Path.GetFullPath(directory_part); + string file_part = Path.GetFileName(wildcardPath); + //Console.WriteLine("{0}::{1}", directory_part, file_part); + string[] files = Directory.GetFiles(directory_part, file_part); + foreach (string file in files) { + AddDocument(file); + } + + //Console.WriteLine(files.Length); + documentCount += files.Length; + } + + public void AddDocuments(string baseDirectory, string wildcardPath, bool recurse) { + + string path; + if (String.IsNullOrEmpty(baseDirectory)) { + path = wildcardPath; + } else { + path = Path.Combine(baseDirectory, wildcardPath); + } + + AddDocuments(path); + + if (recurse) { + string[] subDirectories = Directory.GetDirectories(baseDirectory); + foreach (string subDirectory in subDirectories) AddDocuments(subDirectory, wildcardPath, recurse); + } + } + + private int documentCount; + + public int DocumentCount { + get { + return (documentCount); + } + } + + // a simple caching mechanism + + int cacheSize; + + // an improved cache + + // this cache keeps track of the order that files are loaded in, and always unloads the oldest one + // this is better, but a document that is often accessed gets no "points", so it will eventualy be + // thrown out even if it is used regularly + + private Dictionary<string, IndexedDocument> cache; + + private Queue<string> queue; + + public IndexedDocument GetDocument(string key) { + + // look up the file corresponding to the key + string file; + if (index.TryGetValue(key, out file)) { + + // now look for that file in the cache + IndexedDocument document; + if (!cache.TryGetValue(file, out document)) { + + // not in the cache, so load it + document = new IndexedDocument(this, file); + + // if the cache is full, remove a document + if (cache.Count >= cacheSize) { + string fileToUnload = queue.Dequeue(); + cache.Remove(fileToUnload); + } + + // add it to the cache + cache.Add(file, document); + queue.Enqueue(file); + + } + + // XPathNavigator content = document.GetContent(key); + return (document); + + } else { + // there is no such key + return (null); + } + + } + + + public XPathNavigator GetContent(string key) { + + IndexedDocument document = GetDocument(key); + if (document == null) { + return (null); + } else { + return (document.GetContent(key)); + } + + } + + public int Count { + get { + return (index.Count); + } + } + + } + + // a file that we have indexed + + public class IndexedDocument { + + public IndexedDocument(IndexedDocumentCache cache, string file) { + + if (cache == null) throw new ArgumentNullException("cache"); + if (file == null) throw new ArgumentNullException("file"); + + // remember the file + this.file = file; + + // load the document + try { + XPathDocument document = new XPathDocument(file, XmlSpace.Preserve); + //XPathDocument document = new XPathDocument(file); + + // search for value nodes + XPathNodeIterator valueNodes = document.CreateNavigator().Select(cache.ValueExpression); + // Console.WriteLine("found {0} instances of '{1}' (key xpath is '{2}')", valueNodes.Count, valueExpression.Expression, keyExpression.Expression); + + // get the key string for each value node and record it in the index + foreach (XPathNavigator valueNode in valueNodes) { + + XPathNavigator keyNode = valueNode.SelectSingleNode(cache.KeyExpression); + if (keyNode == null) { + // Console.WriteLine("null key"); + continue; + } + + string key = keyNode.Value; + index[key] = valueNode; + if (!index.ContainsKey(key)) { + //index.Add(key, valueNode); + } else { + // Console.WriteLine("Repeat key '{0}'", key); + } + } + + } catch (IOException e) { + cache.Component.WriteHelperMessage(MessageLevel.Error, String.Format("An access error occured while attempting to load the file '{0}'. The error message is: {1}", file, e.Message)); + } catch (XmlException e) { + cache.Component.WriteHelperMessage(MessageLevel.Error, String.Format("The indexed document '{0}' is not a valid XML document. The error message is: {1}", file, e.Message)); + } + // Console.WriteLine("indexed {0} keys", index.Count); + + + } + + // the indexed file + + private string file; + + // the index that maps keys to positions in the file + + Dictionary<string, XPathNavigator> index = new Dictionary<string, XPathNavigator>(); + + // public methods + + public string File { + get { + return (file); + } + } + + public XPathNavigator GetContent(string key) { + XPathNavigator value = index[key]; + if (value == null) { + return (null); + } else { + return (value.Clone()); + } + } + + public string[] GetKeys() { + string[] keys = new string[Count]; + index.Keys.CopyTo(keys, 0); + return (keys); + } + + public int Count { + get { + return (index.Count); + } + } + + } + + internal class CopyCommand { + + public CopyCommand(IndexedDocumentCache source_index, string key_xpath, string source_xpath, string target_xpath, string attribute_value, string ignoreCase_value) { + this.cache = source_index; + if (String.IsNullOrEmpty(key_xpath)) { + // Console.WriteLine("null key xpath"); + key = XPathExpression.Compile("string($key)"); + } else { + // Console.WriteLine("compiling key xpath '{0}'", key_xpath); + key = XPathExpression.Compile(key_xpath); + } + source = XPathExpression.Compile(source_xpath); + target = target_xpath; + attribute = attribute_value; + ignoreCase = ignoreCase_value; + } + + private IndexedDocumentCache cache; + + private XPathExpression key; + + private XPathExpression source; + + private String target; + + private String attribute; + + private String ignoreCase; + + private MessageLevel missingEntry = MessageLevel.Ignore; + + private MessageLevel missingSource = MessageLevel.Ignore; + + private MessageLevel missingTarget = MessageLevel.Ignore; + + public IndexedDocumentCache Index { + get { + return (cache); + } + } + + public XPathExpression Key { + get { + return (key); + } + } + + public XPathExpression Source { + get { + return (source); + } + } + + public String Target { + get { + return (target); + } + } + + public String Attribute + { + get + { + return (attribute); + } + } + + public String IgnoreCase + { + get + { + return (ignoreCase); + } + } + + public MessageLevel MissingEntry { + get { + return (missingEntry); + } + set { + missingEntry = value; + } + } + + public MessageLevel MissingSource { + get { + return (missingSource); + } + set { + missingSource = value; + } + } + + public MessageLevel MissingTarget { + get { + return (missingTarget); + } + set { + missingTarget = value; + } + } + + } + + // the abstract CopyComponent + public abstract class CopyComponent + { + + public CopyComponent(XPathNavigator configuration, Dictionary<string, object> data) { } + + public abstract void Apply(XmlDocument document, string key); + + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/DisplayComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/DisplayComponent.cs new file mode 100644 index 0000000..a6d9ca2 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/DisplayComponent.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + + public class DisplayComponent : BuildComponent { + + private string xpath_format = "/"; + + public DisplayComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + XPathNavigator xpath_format_node = configuration.SelectSingleNode("xpath"); + if (xpath_format_node != null) xpath_format = xpath_format_node.Value; + } + + public override void Apply (XmlDocument document, string key) { + string xpath = String.Format(xpath_format, key); + + Object result = document.CreateNavigator().Evaluate(xpath); + + if (result == null) { + Console.WriteLine("null result"); + return; + } + + XPathNodeIterator nodes = result as XPathNodeIterator; + if (nodes != null) { + foreach (XPathNavigator node in nodes) { + Console.WriteLine(node.OuterXml); + } + return; + } + + Console.WriteLine(result.ToString()); + + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ExampleComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ExampleComponent.cs new file mode 100644 index 0000000..d0d52d9 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ExampleComponent.cs @@ -0,0 +1,549 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Configuration; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + // a component to replace code references with snippets from a file + + public class ExampleComponent : BuildComponent { + + // instantiation logic + + public ExampleComponent(BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNodeIterator contentNodes = configuration.Select("examples"); + foreach (XPathNavigator contentNode in contentNodes) { + string file = contentNode.GetAttribute("file", String.Empty); + file = Environment.ExpandEnvironmentVariables(file); + if (String.IsNullOrEmpty(file)) WriteMessage(MessageLevel.Error, String.Format("Each examples element must contain a file attribute.")); + LoadContent(file); + } + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} code snippets", snippets.Count)); + + XPathNodeIterator colorsNodes = configuration.Select("colors"); + foreach (XPathNavigator colorsNode in colorsNodes) { + string language = colorsNode.GetAttribute("language", String.Empty); + List<ColorizationRule> rules = new List<ColorizationRule>(); + + XPathNodeIterator colorNodes = colorsNode.Select("color"); + foreach (XPathNavigator colorNode in colorNodes) { + string pattern = colorNode.GetAttribute("pattern", String.Empty); + string region = colorNode.GetAttribute("region", String.Empty); + string name = colorNode.GetAttribute("class", String.Empty); + if (String.IsNullOrEmpty(region)) { + rules.Add( new ColorizationRule(pattern, name) ); + } else { + rules.Add( new ColorizationRule(pattern, region, name) ); + } + } + + colorization[language] = rules; + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} colorization rules for the language '{1}'.", rules.Count, language)); + } + + context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); + + selector = XPathExpression.Compile("//ddue:codeReference"); + selector.SetContext(context); + } + + // snippet loading logic + + private void LoadContent(string file) { + + SnippetIdentifier key = new SnippetIdentifier(); + string language; + + WriteMessage(MessageLevel.Info, String.Format("Loading code snippet file '{0}'.", file)); + try { + XmlReaderSettings settings = new XmlReaderSettings(); + settings.CheckCharacters = false; + XmlReader reader = XmlReader.Create(file, settings); + + try { + reader.MoveToContent(); + while (!reader.EOF) { + if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "item")) { + key = new SnippetIdentifier(reader.GetAttribute("id")); + reader.Read(); + } else if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "sampleCode")) { + language = reader.GetAttribute("language"); + + string content = reader.ReadString(); + + // If the element is empty, ReadString does not advance the reader, so we must do it manually + if (String.IsNullOrEmpty(content)) reader.Read(); + + if (!IsLegalXmlText(content)) { + Console.WriteLine("Snippet '{0}' language '{1}' contains illegal characters.", key, language); + throw new InvalidOperationException(); + } + + content = StripLeadingSpaces(content); + + StoredSnippet snippet = new StoredSnippet(content, language); + List<StoredSnippet> values; + if (!snippets.TryGetValue(key, out values)) { + values = new List<StoredSnippet>(); + snippets.Add(key, values); + } + values.Add(snippet); + } else { + reader.Read(); + } + } + } catch (XmlException e) { + WriteMessage(MessageLevel.Warn, String.Format("The contents of the snippet file '{0}' are not well-formed XML. The error message is: {1}. Some snippets may be lost.", file, e.Message)); + } finally { + reader.Close(); + } + + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to read the snippet file '{0}'. The error message is: {1}", file, e.Message)); + } + + } + + // logic for checking XML + + private bool IsLegalXmlCharacter (char c) { + if (c < 0x20) { + return ( (c == 0x09) || (c == 0x0A) || (c == 0x0D) ); + } else { + if (c < 0xD800) { + return(true); + } else { + return( (c >= 0xE000) && (c <= 0xFFFD) ); + } + } + } + + private bool IsLegalXmlText (string text) { + foreach (char c in text) { + if (!IsLegalXmlCharacter(c)) return (false); + } + return (true); + } + + // the snippet store + + private Dictionary<SnippetIdentifier,List<StoredSnippet>> snippets = new Dictionary<SnippetIdentifier,List<StoredSnippet>>(); + + // the actual work of the component + + public override void Apply(XmlDocument document, string key) { + XPathNodeIterator nodesIterator = document.CreateNavigator().Select(selector); + XPathNavigator[] nodes = BuildComponentUtilities.ConvertNodeIteratorToArray(nodesIterator); + foreach (XPathNavigator node in nodes) { + + string reference = node.Value; + + // check for validity of reference + if (validSnippetReference.IsMatch(reference)) { + + + SnippetIdentifier[] identifiers = SnippetIdentifier.ParseReference(reference); + + if (identifiers.Length == 1) { + // one snippet referenced + + SnippetIdentifier identifier = identifiers[0]; + List<StoredSnippet> values; + if (snippets.TryGetValue(identifier, out values)) { + + XmlWriter writer = node.InsertAfter(); + writer.WriteStartElement("snippets"); + writer.WriteAttributeString("reference", reference); + + foreach (StoredSnippet value in values) { + writer.WriteStartElement("snippet"); + writer.WriteAttributeString("language", value.Language); + + if (colorization.ContainsKey(value.Language)) { + WriteColorizedSnippet(ColorizeSnippet(value.Text, colorization[value.Language]), writer); + + } else { + writer.WriteString(value.Text); + //writer.WriteString(System.Web.HttpUtility.HtmlDecode(value.Text)); + } + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + writer.Close(); + + } else { + WriteMessage(MessageLevel.Warn, String.Format("No snippet with identifier '{0}' was found.", identifier)); + } + } else { + // multiple snippets referenced + + // create structure that maps language -> snippets + Dictionary<string,List<StoredSnippet>> map = new Dictionary<string,List<StoredSnippet>>(); + foreach (SnippetIdentifier identifier in identifiers) { + List<StoredSnippet> values; + if (snippets.TryGetValue(identifier, out values)) { + foreach (StoredSnippet value in values) { + List<StoredSnippet> pieces; + if (!map.TryGetValue(value.Language, out pieces)) { + pieces = new List<StoredSnippet>(); + map.Add(value.Language, pieces); + } + pieces.Add(value); + } + } + } + + XmlWriter writer = node.InsertAfter(); + writer.WriteStartElement("snippets"); + writer.WriteAttributeString("reference", reference); + + foreach (KeyValuePair<string,List<StoredSnippet>> entry in map) { + writer.WriteStartElement("snippet"); + writer.WriteAttributeString("language", entry.Key); + + List<StoredSnippet> values = entry.Value; + for (int i=0; i<values.Count; i++) { + if (i>0) writer.WriteString("\n...\n\n\n"); + writer.WriteString(values[i].Text); + // writer.WriteString(System.Web.HttpUtility.HtmlDecode(values[i].Text)); + } + + writer.WriteEndElement(); + } + + writer.WriteEndElement(); + writer.Close(); + + } + + } else { + WriteMessage(MessageLevel.Warn, String.Format("The code reference '{0}' is not well-formed", reference)); + } + + node.DeleteSelf(); + + } + } + + private XPathExpression selector; + + private XmlNamespaceManager context = new CustomContext(); + + private static Regex validSnippetReference = new Regex(@"^[^#\a\b\f\n\r\t\v]+#(\w+,)*\w+$", RegexOptions.Compiled); + + // colorization logic + + private Dictionary<string,List<ColorizationRule>> colorization = new Dictionary<string,List<ColorizationRule>>(); + + private static ICollection<Region> ColorizeSnippet (string text, List<ColorizationRule> rules) { + + // Console.WriteLine("colorizing: '{0}'", text); + + // create a linked list consiting entirely of one uncolored region + LinkedList<Region> regions = new LinkedList<Region>(); + regions.AddFirst( new Region(text) ); + + // loop over colorization rules + foreach (ColorizationRule rule in rules) { + + // loop over regions + + LinkedListNode<Region> node = regions.First; + while(node != null) { + + // only try to colorize uncolored regions + if (node.Value.ClassName != null) { + node = node.Next; + continue; + } + + // find matches in the region + string regionText = node.Value.Text; + Capture[] matches = rule.Apply(regionText); + + // if no matches were found, continue to the next region + if (matches.Length == 0) { + node = node.Next; + continue; + } + + // we found matches; break the region into colored and uncolered subregions + + // index is where we are looking from; index-1 is the end of the last match + int index = 0; + + LinkedListNode<Region> referenceNode = node; + + foreach (Capture match in matches) { + + // create a leading uncolored region + if (match.Index > index) { + //Console.WriteLine("uncolored: {0} '{1}' -> {2} '{3}'", index, regionText[index], match.Index - 1, regionText[match.Index - 1]); + Region uncoloredRegion = new Region(regionText.Substring(index, match.Index-index)); + referenceNode = regions.AddAfter(referenceNode, uncoloredRegion); + } + + // create a colored region + // Console.WriteLine("name = {0}", rule.ClassName); + //Console.WriteLine("colored: {0} '{1}' -> {2} '{3}'", match.Index, regionText[match.Index], match.Index + match.Length - 1, regionText[match.Index + match.Length - 1]); + Region coloredRegion = new Region(rule.ClassName, regionText.Substring(match.Index, match.Length)); + referenceNode = regions.AddAfter(referenceNode, coloredRegion); + + index = match.Index + match.Length; + + } + + // create a trailing uncolored region + if (index < regionText.Length) { + Region uncoloredRegion = new Region(regionText.Substring(index)); + referenceNode = regions.AddAfter(referenceNode, uncoloredRegion); + } + + // remove the original node + regions.Remove(node); + + node = referenceNode.Next; + } + + + } + return(regions); + + } + + private static void WriteColorizedSnippet (ICollection<Region> regions, XmlWriter writer) { + foreach (Region region in regions) { + // Console.WriteLine("writing {0}", region.ClassName); + if (region.ClassName == null) { + writer.WriteString(region.Text); + } else { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", region.ClassName); + writer.WriteString(region.Text); + writer.WriteEndElement(); + } + } + } + + private static string StripLeadingSpaces (string text) { + + if (text == null) throw new ArgumentNullException("text"); + + // Console.WriteLine("BEFORE:"); + // Console.WriteLine(text); + + // split the text into lines + string[] lines = text.Split('\n'); + + // no need to do this if there is only one line + if (lines.Length == 1) return(lines[0]); + + // figure out how many leading spaces to delete + int spaces = Int32.MaxValue; + for (int i=0; i<lines.Length; i++) { + + string line = lines[i]; + + // skip empty lines + if (line.Length == 0) continue; + + // determine the number of leading spaces + int index = 0; + while (index < line.Length) { + if (line[index] != ' ') break; + index++; + } + + if (index == line.Length) { + // lines that are all spaces should just be treated as empty + lines[i] = String.Empty; + } else { + // otherwise, keep track of the minimum number of leading spaces + if (index < spaces) spaces = index; + } + + } + + // Console.WriteLine("SPACES = {0}", spaces); + + // re-form the string with leading spaces deleted + StringBuilder result = new StringBuilder(); + foreach (string line in lines) { + if (line.Length == 0) { + result.AppendLine(); + } else { + result.AppendLine(line.Substring(spaces)); + } + } + // Console.WriteLine("AFTER:"); + // Console.WriteLine(result.ToString()); + return(result.ToString()); + + } + + } + + internal struct SnippetIdentifier { + + public SnippetIdentifier (string exampleId, string snippetId) { + this.exampleId = exampleId.ToLower(); + this.snippetId = snippetId.ToLower(); + } + + public SnippetIdentifier (string identifier) { + int index = identifier.LastIndexOf('#'); + exampleId = identifier.Substring(0,index).ToLower(); + snippetId = identifier.Substring(index+1).ToLower(); + } + + private string exampleId; + + private string snippetId; + + public string Example { + get { + return(exampleId); + } + } + + public string Snippet { + get { + return(snippetId); + } + } + + public override string ToString() { + return(String.Format("{0}#{1}", exampleId, snippetId)); + } + + public static SnippetIdentifier[] ParseReference (string reference) { + + int index = reference.IndexOf('#'); + if (index < 0) return(new SnippetIdentifier[0]); + + string example = reference.Substring(0,index); + string[] snippets = reference.Substring(index+1).Split(','); + + SnippetIdentifier[] identifiers = new SnippetIdentifier[snippets.Length]; + for (int i=0; i<snippets.Length; i++) { + identifiers[i] = new SnippetIdentifier(example, snippets[i]); + } + return(identifiers); + + } + + } + + internal class StoredSnippet { + + public StoredSnippet (string text, string language) { + this.text = text; + this.language = language; + } + + private string text; + + private Region[] regions; + + private string language; + + public string Text { + get { + return(text); + } + } + + public string Language { + get { + return(language); + } + } + + public Region[] Regions { + get { + return(regions); + } + } + + } + + internal class ColorizationRule { + + public ColorizationRule (string pattern, string className) : this(pattern, null, className) {} + + public ColorizationRule (string pattern, string region, string className) { + this.pattern = new Regex(pattern, RegexOptions.Compiled|RegexOptions.Multiline); + this.region = region; + this.className = className; + } + + private Regex pattern; + + private string region; + + private string className; + + public string ClassName { + get { + return(className); + } + } + + public Capture[] Apply (string text) { + + MatchCollection matches = pattern.Matches(text); + Capture[] captures = new Capture[matches.Count]; + + if (region == null) { + matches.CopyTo(captures, 0); + return(captures); + } else { + for (int i=0; i<captures.Length; i++) { + captures[i] = matches[i].Groups[region]; + } + return(captures); + } + + } + + } + + internal struct Region { + + public Region (string text) : this(null, text) {} + + public Region (string className, string text) { + this.className = className; + this.text = text; + } + + private string className; + + private string text; + + + public string ClassName { + get { + return(className); + } + } + + public string Text { + get { + return(text); + } + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ForEachComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ForEachComponent.cs new file mode 100644 index 0000000..d073daf --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ForEachComponent.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class ForEachComponent : BuildComponent { + + public ForEachComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // set up the context + XPathNodeIterator context_nodes = configuration.Select("context"); + foreach (XPathNavigator context_node in context_nodes) + { + string prefix = context_node.GetAttribute("prefix", String.Empty); + string name = context_node.GetAttribute("name", String.Empty); + context.AddNamespace(prefix, name); + } + + // load the expression format + XPathNavigator variable_node = configuration.SelectSingleNode("variable"); + if (variable_node == null) throw new ConfigurationErrorsException("When instantiating a ForEach component, you must specify a variable using the <variable> element."); + string xpath_format = variable_node.GetAttribute("expression", String.Empty); + if ((xpath_format == null) || (xpath_format.Length == 0)) throw new ConfigurationErrorsException("When instantiating a ForEach component, you must specify a variable expression using the expression attribute"); + xpath = XPathExpression.Compile(xpath_format); + + // load the subcomponents + WriteMessage(MessageLevel.Info, "Loading subcomponents."); + XPathNavigator components_node = configuration.SelectSingleNode("components"); + if (components_node == null) throw new ConfigurationErrorsException("When instantiating a ForEach component, you must specify subcomponents using the <components> element."); + + components = BuildAssembler.LoadComponents(components_node); + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} subcomponents.", components.Count)); + + } + + // the format string for the variable expression + private XPathExpression xpath; + + // the xpath context + private CustomContext context = new CustomContext(); + + // the subcomponents + private ICollection<BuildComponent> components; + + // the work of the component + + public override void Apply (XmlDocument document, string key) { + + // adjust the context + context["key"] = key; + + // evaluate the condition + XPathExpression xpath_local = xpath.Clone(); + xpath_local.SetContext(context); + + Object result = document.CreateNavigator().Evaluate(xpath_local); + + // try to intrepret the result as a node set + XPathNodeIterator result_node_iterator = result as XPathNodeIterator; + + if (result_node_iterator != null) { + XPathNavigator[] result_nodes = BuildComponentUtilities.ConvertNodeIteratorToArray(result_node_iterator); + //Console.WriteLine("{0} node-set result", result_nodes.Length); + // if it is, apply the child components to each node value + foreach (XPathNavigator result_node in result_nodes) { + // Console.WriteLine(result_node.Value); + ApplyComponents(document, result_node.Value); + } + } else { + //Console.WriteLine("non-node-set result"); + // if it isn't, apply the child components to the string value of the result + ApplyComponents(document, result.ToString()); + + } + + + } + + private void ApplyComponents (XmlDocument document, string key) { + foreach (BuildComponent component in components) { + component.Apply(document, key); + } + } + + + public override void Dispose () { + foreach (BuildComponent component in components) { + component.Dispose(); + } + base.Dispose(); + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/HxfGeneratorComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/HxfGeneratorComponent.cs new file mode 100644 index 0000000..65ba716 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/HxfGeneratorComponent.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using System.Collections.Generic; + +namespace Microsoft.Ddue.Tools { + + public class FileCreatedEventArgs : EventArgs { + + private string filePath; + private string hxfPath; + + public FileCreatedEventArgs(string filePath, string hxfPath) { + this.filePath = filePath; + this.hxfPath = hxfPath; + } + + public string FilePath { + get { + return(filePath); + } + } + + public string HxfPath { + get { + return (hxfPath); + } + } + + } + + public class HxfGeneratorComponent : BuildComponent { + + public HxfGeneratorComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // get configuration data + inputValue = configuration.GetAttribute("input", String.Empty); + if (!String.IsNullOrEmpty(inputValue)) inputValue = Environment.ExpandEnvironmentVariables(inputValue); + outputValue = configuration.GetAttribute("output", String.Empty); + if (!String.IsNullOrEmpty(outputValue)) outputValue = Environment.ExpandEnvironmentVariables(outputValue); + + // subscribe to component events + assembler.ComponentEvent += new EventHandler(FileCreatedHandler); + } + + private string inputValue; + + private string outputValue; + + private XmlWriter writer; + + private Dictionary<string, XmlWriter> writers = new Dictionary<string, XmlWriter>(); + + private void FileCreatedHandler (Object o, EventArgs e) { + FileCreatedEventArgs fe = e as FileCreatedEventArgs; + if (fe == null) return; + + string path = Path.Combine(fe.HxfPath, outputValue); + + XmlWriter tempWriter; + if (!writers.TryGetValue(path, out tempWriter)) { + if (writer != null) { + writer.WriteEndDocument(); + writer.Close(); + } + WriteFile(path); + } + + WriteFileElement(fe.FilePath); + + } + + private void WriteFileElement (string url) { + writer.WriteStartElement("File"); + writer.WriteAttributeString("Url", url); + writer.WriteEndElement(); + } + + public override void Dispose() { + writer.WriteEndDocument(); + writer.Close(); + base.Dispose(); + } + + public void WriteFile(string path) { + XmlWriterSettings writerSettings = new XmlWriterSettings(); + writerSettings.Indent = true; + writer = XmlWriter.Create(path); + writer.WriteStartDocument(); + writer.WriteStartElement("HelpFileList"); + writer.WriteAttributeString("DTDVersion", "1.0"); + + // use the input to seed the output + if (!String.IsNullOrEmpty(inputValue)) { + + try { + TextReader reader = File.OpenText(inputValue); + + try { + while (true) { + string line = reader.ReadLine(); + if (line == null) break; + WriteFileElement(line); + } + } + finally { + reader.Close(); + } + } + catch (IOException ex) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to copy the input HxF data. The error message is:", ex.Message)); + } + } + + writers.Add(path, writer); + } + + // don't do anything for individual files + public override void Apply (XmlDocument document, string key) {} + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IfThenComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IfThenComponent.cs new file mode 100644 index 0000000..7d8f423 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IfThenComponent.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +using System.Reflection; + +namespace Microsoft.Ddue.Tools { + + public class IfThenComponent : BuildComponent { + + private XPathExpression condition; + + private IEnumerable<BuildComponent> true_branch = new List<BuildComponent>(); + + private IEnumerable<BuildComponent> false_branch = new List<BuildComponent>(); + + public IfThenComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // get the condition + XPathNavigator if_node = configuration.SelectSingleNode("if"); + if (if_node == null) throw new ConfigurationErrorsException("You must specify a condition using the <if> element."); + string condition_xpath = if_node.GetAttribute("condition", String.Empty); + if (String.IsNullOrEmpty(condition_xpath)) throw new ConfigurationErrorsException(); + condition = XPathExpression.Compile(condition_xpath); + + // construct the true branch + XPathNavigator then_node = configuration.SelectSingleNode("then"); + if (then_node != null) true_branch = BuildAssembler.LoadComponents(then_node); + + // construct the false branch + XPathNavigator else_node = configuration.SelectSingleNode("else"); + if (else_node != null) false_branch = BuildAssembler.LoadComponents(else_node); + + // keep a pointer to the context for future use + context = assembler.Context; + + } + + private BuildContext context; + + public override void Apply (XmlDocument document, string key) { + + // set up the test + context["key"] = key; + XPathExpression test = condition.Clone(); + test.SetContext(context.XsltContext); + + // evaluate the condition + bool result = (bool) document.CreateNavigator().Evaluate(test); + + // on the basis of the condition, execute either the true or the false branch + if (result) { + foreach (BuildComponent component in true_branch) { + component.Apply(document, key); + } + } else { + foreach (BuildComponent component in false_branch) { + component.Apply(document, key); + } + } + + } + + public override void Dispose () { + foreach (BuildComponent component in true_branch) { + component.Dispose(); + } + foreach (BuildComponent component in false_branch) { + component.Dispose(); + } + base.Dispose(); + } + + } + + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IndexedFileCache.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IndexedFileCache.cs new file mode 100644 index 0000000..62425d8 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IndexedFileCache.cs @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools +{ + // the storage system + + internal class IndexedFileCache + { + + public IndexedFileCache(string keyXPath, string valueXPath, int cacheSize) + { + valueExpression = XPathExpression.Compile(valueXPath); + + keyExpression = XPathExpression.Compile(keyXPath); + + _cacheSize = cacheSize; + + cache = new Dictionary<string, IndexedFile>(_cacheSize); + + lruLinkedList = new LinkedList<string>(); + } + + // search pattern for value nodes to be mapped + private XPathExpression valueExpression; + + // search pattern for the key identifier (relative to the value node) + private XPathExpression keyExpression; + + // an index mapping topic IDs to files + private Dictionary<string, string> idToFileMap = new Dictionary<string, string>(); + + public int ParseDocuments(string wildcardPath) + { + string directoryPart = Path.GetDirectoryName(wildcardPath); + if (String.IsNullOrEmpty(directoryPart)) directoryPart = Environment.CurrentDirectory; + directoryPart = Path.GetFullPath(directoryPart); + string filePart = Path.GetFileName(wildcardPath); + string[] files = Directory.GetFiles(directoryPart, filePart); + // WriteMessage(MessageLevel.Info, String.Format("Found {0} files.", files.Length) ); + foreach (string file in files) + { + ParseDocument(file); + } + return (files.Length); + } + + private void ParseDocument(string file) + { + try + { + XPathDocument document = new XPathDocument(file); + XPathNodeIterator valueNodes = document.CreateNavigator().Select(valueExpression); + foreach (XPathNavigator valueNode in valueNodes) + { + XPathNavigator keyNode = valueNode.SelectSingleNode(keyExpression); + if (keyNode == null) continue; + string key = keyNode.Value; + + // log multiple occurences of a single id + if (idToFileMap.ContainsKey(key)) + { + // WriteMessage(MessageLevel.Warn, String.Format("Entries for the key '{0}' occur in both '{1}' and '{2}'. The first entry will be used.", key, idToFileMap[key], file)); + } + else + { + idToFileMap[key] = file; + } + } + } + catch (XmlException) + { + // WriteMessage(MessageLevel.Error, e.Message); + } + } + + // a simple document caching mechanism + + private int _cacheSize = 10; + + private LinkedList<String> lruLinkedList; + + private Dictionary<string, IndexedFile> cache; + + + private IndexedFile GetCachedDocument(string identifier) + { + string file; + if (idToFileMap.TryGetValue(identifier, out file)) + { + IndexedFile document; + if (cache.TryGetValue(file, out document)) + { + // move the file from its current position to the head of the lru linked list + lruLinkedList.Remove(document.ListNode); + lruLinkedList.AddFirst(document.ListNode); + } + else + { + // not in the cache, so load and index a new source file + document = new IndexedFile(file, valueExpression, keyExpression); + if (cache.Count >= _cacheSize) + { + // the cache is full + // the last node in the linked list has the path of the next file to remove from the cache + if (lruLinkedList.Last != null) + { + cache.Remove(lruLinkedList.Last.Value); + lruLinkedList.RemoveLast(); + } + } + // add the new file to the cache and to the head of the lru linked list + cache.Add(file, document); + document.ListNode = lruLinkedList.AddFirst(file); + } + return (document); + } + else + { + return (null); + } + } + + public XPathNavigator GetContent(string identifier) + { + + // get the document containing the identifier + IndexedFile document = GetCachedDocument(identifier); + if (document == null) return (null); + + // select the comment part of the document + return document.GetContent(identifier); + } + + public int Count + { + get + { + return (idToFileMap.Count); + } + } + + } + + internal class IndexedFile + { + Dictionary<string, XPathNavigator> valueIndex = new Dictionary<string, XPathNavigator>(); + + public IndexedFile(string filepath, XPathExpression valueExpression, XPathExpression keyExpression) + { + XPathDocument xpDoc = new XPathDocument(filepath); + XPathNodeIterator valueNodes = xpDoc.CreateNavigator().Select(valueExpression); + foreach (XPathNavigator valueNode in valueNodes) + { + XPathNavigator keyNode = valueNode.SelectSingleNode(keyExpression); + if (keyNode == null) + continue; + string key = keyNode.Value; + if (!valueIndex.ContainsKey(key)) + valueIndex.Add(key, valueNode); + } + } + + public XPathNavigator GetContent(string key) + { + return valueIndex[key]; + } + + private LinkedListNode<string> listNode; + public LinkedListNode<string> ListNode + { + get + { + return (listNode); + } + set + { + listNode = value; + } + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent.cs new file mode 100644 index 0000000..4d67803 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent.cs @@ -0,0 +1,333 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class IntellisenseComponent : BuildComponent { + + public IntellisenseComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNavigator output_node = configuration.SelectSingleNode("output"); + if (output_node != null) { + + string directory_value = output_node.GetAttribute("directory", String.Empty); + if (!String.IsNullOrEmpty(directory_value)) { + directory = Environment.ExpandEnvironmentVariables(directory_value); + if (!Directory.Exists(directory)) WriteMessage(MessageLevel.Error, String.Format("The output directory '{0}' does not exist.", directory)); + } + } + + // a way to get additional information into the intellisense file + XPathNodeIterator input_nodes = configuration.Select("input"); + foreach (XPathNavigator input_node in input_nodes) { + string file_value = input_node.GetAttribute("file", String.Empty); + if (!String.IsNullOrEmpty(file_value)) { + string file = Environment.ExpandEnvironmentVariables(file_value); + ReadInputFile(file); + } + } + + context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); + + summaryExpression.SetContext(context); + memberSummaryExpression.SetContext(context); + returnsExpression.SetContext(context); + parametersExpression.SetContext(context); + parameterNameExpression.SetContext(context); + templatesExpression.SetContext(context); + templateNameExpression.SetContext(context); + exceptionExpression.SetContext(context); + exceptionCrefExpression.SetContext(context); + } + + // input content store + + private void ReadInputFile (string file) { + try { + XPathDocument document = new XPathDocument(file); + + XPathNodeIterator member_nodes = document.CreateNavigator().Select("/metadata/topic[@id]"); + foreach (XPathNavigator member_node in member_nodes) { + string id = member_node.GetAttribute("id", String.Empty); + content[id] = member_node.Clone(); + } + + WriteMessage(MessageLevel.Info, String.Format("Read {0} input content nodes.", member_nodes.Count)); + + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("The input file '{0}' is not a well-formed XML file. The error message is: {1}", file, e.Message)); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An error occured while attempting to access the fileThe input file '{0}'. The error message is: {1}", file, e.Message)); + } + + + } + private Dictionary<string, XPathNavigator> content = new Dictionary<string, XPathNavigator>(); + + // the action of the component + + public override void Apply (XmlDocument document, string id) { + + // only generate intellisense if id corresponds to an allowed intellisense ID + if (id.Length < 2) return; + if (id[1] != ':') return; + if (!((id[0] == 'T') || (id[0] == 'M') || (id[0] == 'P') || (id[0] == 'F') || (id[0] == 'E') || (id[0] == 'N'))) return; + + XPathNavigator root = document.CreateNavigator().SelectSingleNode("/document/comments"); + + string assembly; + + if ((string)root.Evaluate(groupExpression) == "namespace") { + // get the assembly for the namespace + //assembly = (string) root.Evaluate(namespaceAssemblyExpression); + // Assign general name for namespace assemblies since they do not belong to any specific assembly + assembly = "namespaces"; + } else { + // get the assembly for the API + assembly = (string) root.Evaluate(assemblyExpression); + } + + if (String.IsNullOrEmpty(assembly)) return; + + // try/catch block for capturing errors + try { + + // get the writer for the assembly + XmlWriter writer; + if (!writers.TryGetValue(assembly, out writer)) { + + // create a writer for the assembly + string name = Path.Combine(directory, assembly + ".xml"); + // Console.WriteLine("creating {0}", name); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + + try { + writer = XmlWriter.Create(name, settings); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to create the intellisense output file '{0}'. The error message is: {1}", name, e.Message)); + } + + + writers.Add(assembly, writer); + + // write out the initial data + writer.WriteStartDocument(); + writer.WriteStartElement("doc"); + //do not generate assembly nodes for namespace topics + if ((string)root.Evaluate(groupExpression) != "namespace") + { + writer.WriteStartElement("assembly"); + writer.WriteElementString("name", assembly); + writer.WriteEndElement(); + } + writer.WriteStartElement("members"); + } + + writer.WriteStartElement("member"); + writer.WriteAttributeString("name", id); + + // summary + WriteSummary(root, summaryExpression, writer); + + // return value + XPathNavigator returns = root.SelectSingleNode(returnsExpression); + if (returns != null) { + writer.WriteStartElement("returns"); + + XmlReader reader = returns.ReadSubtree(); + CopyContent(reader, writer); + reader.Close(); + + writer.WriteEndElement(); + } + + // parameters + XPathNodeIterator parameters = root.Select(parametersExpression); + foreach (XPathNavigator parameter in parameters) { + + string name = (string)parameter.Evaluate(parameterNameExpression); + + XmlReader reader = parameter.ReadSubtree(); + + writer.WriteStartElement("param"); + writer.WriteAttributeString("name", name); + CopyContent(reader, writer); + writer.WriteEndElement(); + + reader.Close(); + } + + // templates + XPathNodeIterator templates = root.Select(templatesExpression); + foreach (XPathNavigator template in templates) { + + string name = (string)template.Evaluate(templateNameExpression); + + XmlReader reader = template.ReadSubtree(); + + writer.WriteStartElement("typeparam"); + writer.WriteAttributeString("name", name); + CopyContent(reader, writer); + writer.WriteEndElement(); + + reader.Close(); + } + + // exceptions + XPathNodeIterator exceptions = root.Select(exceptionExpression); + foreach (XPathNavigator exception in exceptions) { + + string exceptionCref = (string)exception.Evaluate(exceptionCrefExpression); + + XmlReader reader = exception.ReadSubtree(); + + writer.WriteStartElement("exception"); + writer.WriteAttributeString("cref", exceptionCref); + CopyContent(reader, writer); + writer.WriteEndElement(); + + reader.Close(); + } + + // stored contents + XPathNavigator input; + if (content.TryGetValue(id, out input)) { + XPathNodeIterator input_nodes = input.SelectChildren(XPathNodeType.Element); + foreach (XPathNavigator input_node in input_nodes) { + input_node.WriteSubtree(writer); + } + } + + writer.WriteFullEndElement(); + + // enumeration members + string subgroup = (string)root.Evaluate(subgroupExpression); + if (subgroup == "enumeration") { + + XPathNodeIterator elements = (XPathNodeIterator)root.Evaluate(elementsExpression); + foreach (XPathNavigator element in elements) { + + string api = (string)element.GetAttribute("api", string.Empty); + writer.WriteStartElement("member"); + writer.WriteAttributeString("name", api); + + //summary + WriteSummary(element, memberSummaryExpression, writer); + + writer.WriteFullEndElement(); + } + } + + + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to write intellisense data. The error message is: {0}", e.Message)); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("Intellisense data was not valid XML. The error message is: {0}", e.Message)); + } + + } + + public override void Dispose () { + //Console.WriteLine("disposing intellisense writer..."); + foreach (XmlWriter writer in writers.Values) { + writer.WriteEndDocument(); + writer.Close(); + } + } + + private void WriteSummary(XPathNavigator node, XPathExpression expression, XmlWriter writer) { + XPathNavigator summary = node.SelectSingleNode(expression); + if (summary != null) { + writer.WriteStartElement("summary"); + + XmlReader reader = summary.ReadSubtree(); + CopyContent(reader, writer); + reader.Close(); + + writer.WriteEndElement(); + } + else { + // Console.WriteLine("no summary"); + } + } + + private void CopyContent (XmlReader reader, XmlWriter writer) { + reader.MoveToContent(); + while (true) { + + //Console.WriteLine("{0} {1}", reader.ReadState, reader.NodeType); + + if (reader.NodeType == XmlNodeType.Text) { + writer.WriteString(reader.ReadString()); + } else if (reader.NodeType == XmlNodeType.Element) { + //Console.WriteLine(reader.LocalName); + if (reader.LocalName == "codeEntityReference") { + writer.WriteStartElement("see"); + writer.WriteAttributeString("cref", reader.ReadElementString()); + writer.WriteEndElement(); + } else if (reader.LocalName == "parameterReference") { + writer.WriteStartElement("paramref"); + writer.WriteAttributeString("name", reader.ReadElementString()); + writer.WriteEndElement(); + } else if (reader.LocalName == "link") { + string displayText = reader.ReadElementString(); + if (displayText.StartsWith("GTMT#")) { + writer.WriteString(displayText.Substring(displayText.IndexOf("#") + 1)); + } else { + writer.WriteString(displayText); + } + } else { + reader.Read(); + } + } else { + if (!reader.Read()) break; + } + + } + + } + + private string directory = String.Empty; + + private Dictionary<string,XmlWriter> writers = new Dictionary<string,XmlWriter>(); + + private XPathExpression assemblyExpression = XPathExpression.Compile("string(/document/reference/containers/library/@assembly)"); + + private XPathExpression namespaceAssemblyExpression = XPathExpression.Compile("string(/document/reference/elements/element/containers/library/@assembly)"); + + private XPathExpression summaryExpression = XPathExpression.Compile("ddue:dduexml/ddue:summary"); + + private XPathExpression returnsExpression = XPathExpression.Compile("ddue:dduexml/ddue:returnValue"); + + private XPathExpression parametersExpression = XPathExpression.Compile("ddue:dduexml/ddue:parameters/ddue:parameter/ddue:content"); + + private XPathExpression parameterNameExpression = XPathExpression.Compile("string(../ddue:parameterReference)"); + + private XPathExpression templatesExpression = XPathExpression.Compile("ddue:dduexml/ddue:genericParameters/ddue:genericParameter/ddue:content"); + + private XPathExpression templateNameExpression = XPathExpression.Compile("string(../ddue:parameterReference)"); + + private XPathExpression exceptionExpression = XPathExpression.Compile("ddue:dduexml/ddue:exceptions/ddue:exception/ddue:content"); + + private XPathExpression exceptionCrefExpression = XPathExpression.Compile("string(../ddue:codeEntityReference)"); + + private XPathExpression subgroupExpression = XPathExpression.Compile("string(/document/reference/apidata/@subgroup)"); + + private XPathExpression groupExpression = XPathExpression.Compile("string(/document/reference/apidata/@group)"); + + private XPathExpression elementsExpression = XPathExpression.Compile("/document/reference/elements/element"); + + private XPathExpression memberSummaryExpression = XPathExpression.Compile("ddue:summary"); + + private XmlNamespaceManager context = new CustomContext(); + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent2.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent2.cs new file mode 100644 index 0000000..35531c2 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent2.cs @@ -0,0 +1,419 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class IntellisenseComponent2 : BuildComponent { + + public IntellisenseComponent2 (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNavigator output_node = configuration.SelectSingleNode("output"); + if (output_node != null) { + + string directory_value = output_node.GetAttribute("directory", String.Empty); + if (!String.IsNullOrEmpty(directory_value)) { + directory = Environment.ExpandEnvironmentVariables(directory_value); + if (!Directory.Exists(directory)) WriteMessage(MessageLevel.Error, String.Format("The output directory '{0}' does not exist.", directory)); + } + } + + XPathNavigator expression_node = configuration.SelectSingleNode("expressions"); + if (expression_node != null) { + + string root = expression_node.GetAttribute("root", string.Empty); + try { + rootExpression = XPathExpression.Compile(root); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", root)); + } + + string assembly = expression_node.GetAttribute("assembly", string.Empty); + try { + assemblyExpression = XPathExpression.Compile(assembly); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", assembly)); + } + + string summary = expression_node.GetAttribute("summary", string.Empty); + try { + summaryExpression = XPathExpression.Compile(summary); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", summary)); + } + + string parameters = expression_node.GetAttribute("parameters", string.Empty); + try { + parametersExpression = XPathExpression.Compile(parameters); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", parameters)); + } + + string parameterContent = expression_node.GetAttribute("parameterContent", string.Empty); + try { + parameterContentExpression = XPathExpression.Compile(parameterContent); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", parameterContent)); + } + + string templates = expression_node.GetAttribute("templates", string.Empty); + try { + templatesExpression = XPathExpression.Compile(templates); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", templates)); + } + + string templateContent = expression_node.GetAttribute("templateContent", string.Empty); + try { + templateContentExpression = XPathExpression.Compile(templateContent); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", templateContent)); + } + + string returns = expression_node.GetAttribute("returns", string.Empty); + try { + returnsExpression = XPathExpression.Compile(returns); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", returns)); + } + + string exception = expression_node.GetAttribute("exception", string.Empty); + try { + exceptionExpression = XPathExpression.Compile(exception); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", exception)); + } + + string exceptionCref = expression_node.GetAttribute("exceptionCref", string.Empty); + try { + exceptionCrefExpression = XPathExpression.Compile(exceptionCref); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", exceptionCref)); + } + + string enumeration = expression_node.GetAttribute("enumeration", string.Empty); + try { + enumerationExpression = XPathExpression.Compile(enumeration); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", enumeration)); + } + + string enumerationApi = expression_node.GetAttribute("enumerationApi", string.Empty); + try { + enumerationApiExpression = XPathExpression.Compile(enumerationApi); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", enumerationApi)); + } + + string memberSummary = expression_node.GetAttribute("memberSummary", string.Empty); + try { + memberSummaryExpression = XPathExpression.Compile(memberSummary); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("The expression '{0}' is not a valid XPath expression.", memberSummary)); + } + + } + + // a way to get additional information into the intellisense file + XPathNodeIterator input_nodes = configuration.Select("input"); + foreach (XPathNavigator input_node in input_nodes) { + string file_value = input_node.GetAttribute("file", String.Empty); + if (!String.IsNullOrEmpty(file_value)) { + string file = Environment.ExpandEnvironmentVariables(file_value); + ReadInputFile(file); + } + } + } + + // input content store + + private void ReadInputFile (string file) { + try { + XPathDocument document = new XPathDocument(file); + + XPathNodeIterator member_nodes = document.CreateNavigator().Select("/metadata/topic[@id]"); + foreach (XPathNavigator member_node in member_nodes) { + string id = member_node.GetAttribute("id", String.Empty); + content[id] = member_node.Clone(); + } + + WriteMessage(MessageLevel.Info, String.Format("Read {0} input content nodes.", member_nodes.Count)); + + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("The input file '{0}' is not a well-formed XML file. The error message is: {1}", file, e.Message)); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An error occured while attempting to access the fileThe input file '{0}'. The error message is: {1}", file, e.Message)); + } + + + } + private Dictionary<string, XPathNavigator> content = new Dictionary<string, XPathNavigator>(); + + // the action of the component + + public override void Apply (XmlDocument document, string id) { + + // only generate intellisense if id corresponds to an allowed intellisense ID + if (id.Length < 2) return; + if (id[1] != ':') return; + if (!((id[0] == 'T') || (id[0] == 'M') || (id[0] == 'P') || (id[0] == 'F') || (id[0] == 'E') || (id[0] == 'N'))) return; + + XPathNavigator root = document.CreateNavigator().SelectSingleNode(rootExpression); + + // get the assembly information + string assembly = (string) root.Evaluate(assemblyExpression); + + if (String.IsNullOrEmpty(assembly)) { + assembly = "namespaces"; + } + + // try/catch block for capturing errors + try { + + // get the writer for the assembly + XmlWriter writer; + if (!writers.TryGetValue(assembly, out writer)) { + + // create a writer for the assembly + string name = Path.Combine(directory, assembly + ".xml"); + // Console.WriteLine("creating {0}", name); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + + try { + writer = XmlWriter.Create(name, settings); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to create the intellisense output file '{0}'. The error message is: {1}", name, e.Message)); + } + + writers.Add(assembly, writer); + + // write out the initial data + writer.WriteStartDocument(); + writer.WriteStartElement("doc"); + //do not generate assembly nodes for namespace topics + if (assembly != "namespaces") + { + writer.WriteStartElement("assembly"); + writer.WriteElementString("name", assembly); + writer.WriteEndElement(); + } + writer.WriteStartElement("members"); + } + + writer.WriteStartElement("member"); + writer.WriteAttributeString("name", id); + + // summary + WriteSummary(root, summaryExpression, writer); + + // return value + XPathNavigator returns = root.SelectSingleNode(returnsExpression); + if (returns != null) { + writer.WriteStartElement("returns"); + XmlReader reader = returns.ReadSubtree(); + + CopyContent(reader, writer); + reader.Close(); + + writer.WriteEndElement(); + } + + // parameters + XPathNodeIterator parameters = root.Select(parametersExpression); + foreach (XPathNavigator parameter in parameters) { + + string name = (string)parameter.GetAttribute("paramName", string.Empty); + + XPathNavigator parameterContent = parameter.SelectSingleNode(parameterContentExpression); + + if (parameterContent == null) continue; + + XmlReader reader = parameterContent.ReadSubtree(); + + writer.WriteStartElement("param"); + writer.WriteAttributeString("name", name); + CopyContent(reader, writer); + writer.WriteFullEndElement(); + + reader.Close(); + } + + // templates + XPathNodeIterator templates = root.Select(templatesExpression); + foreach (XPathNavigator template in templates) { + + string name = (string)template.GetAttribute("paramName", string.Empty); + + XPathNavigator templateContent = template.SelectSingleNode(templateContentExpression); + + if (templateContent == null) continue; + + XmlReader reader = templateContent.ReadSubtree(); + + writer.WriteStartElement("typeparam"); + writer.WriteAttributeString("name", name); + CopyContent(reader, writer); + writer.WriteFullEndElement(); + + reader.Close(); + } + + // exceptions + XPathNodeIterator exceptions = root.Select(exceptionExpression); + foreach (XPathNavigator exception in exceptions) { + + XPathNavigator exceptionCref = exception.SelectSingleNode(exceptionCrefExpression); + + if (exceptionCref == null) continue; + + string cref = exceptionCref.GetAttribute("target", string.Empty); + XmlReader reader = exception.ReadSubtree(); + + writer.WriteStartElement("exception"); + writer.WriteAttributeString("cref", cref); + CopyContent(reader, writer); + writer.WriteFullEndElement(); + + reader.Close(); + } + + // stored contents + XPathNavigator input; + if (content.TryGetValue(id, out input)) { + XPathNodeIterator input_nodes = input.SelectChildren(XPathNodeType.Element); + foreach (XPathNavigator input_node in input_nodes) { + input_node.WriteSubtree(writer); + } + } + + writer.WriteFullEndElement(); + + // enumeration members + XPathNodeIterator enumerationIterator = root.Select(enumerationExpression); + + foreach (XPathNavigator enumeration in enumerationIterator) { + + XPathNavigator enumApi = enumeration.SelectSingleNode(enumerationApiExpression); + + if (enumApi == null) continue; + + string api = (string)enumApi.GetAttribute("target", string.Empty); + writer.WriteStartElement("member"); + writer.WriteAttributeString("name", api); + + //summary + WriteSummary(enumeration, memberSummaryExpression, writer); + + writer.WriteFullEndElement(); + } + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to write intellisense data. The error message is: {0}", e.Message)); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("Intellisense data was not valid XML. The error message is: {0}", e.Message)); + } + + } + + public override void Dispose () { + //Console.WriteLine("disposing intellisense writer..."); + foreach (XmlWriter writer in writers.Values) { + writer.WriteEndDocument(); + writer.Close(); + } + } + + private void WriteSummary(XPathNavigator node, XPathExpression expression, XmlWriter writer) { + + XPathNavigator summary = node.SelectSingleNode(expression); + if (summary != null) { + writer.WriteStartElement("summary"); + + XmlReader reader = summary.ReadSubtree(); + + CopyContent(reader, writer); + reader.Close(); + + writer.WriteEndElement(); + } else { + // Console.WriteLine("no summary"); + } + } + + private void CopyContent (XmlReader reader, XmlWriter writer) { + reader.MoveToContent(); + while (true) { + + if (reader.NodeType == XmlNodeType.Text) { + writer.WriteString(reader.ReadString()); + } else if (reader.NodeType == XmlNodeType.Element) { + if (reader.LocalName == "span" && (reader.GetAttribute("sdata",string.Empty) == "cer")) { + writer.WriteStartElement("see"); + writer.WriteAttributeString("cref", reader.GetAttribute("target", string.Empty)); + writer.WriteEndElement(); + reader.Skip(); + } + else if (reader.LocalName == "span" && (reader.GetAttribute("sdata", string.Empty) == "paramReference")) + { + writer.WriteStartElement("paramref"); + writer.WriteAttributeString("name", reader.ReadElementString()); + writer.WriteEndElement(); + } else if (reader.LocalName == "span" && (reader.GetAttribute("sdata",string.Empty) == "link")) { + writer.WriteString(reader.ReadElementString()); + } + else if (reader.LocalName == "span" && (reader.GetAttribute("sdata", string.Empty) == "langKeyword")) + { + string keyword = reader.GetAttribute("value", string.Empty); + writer.WriteString(keyword); + reader.Skip(); + } + else { + reader.Read(); + } + } else { + if (!reader.Read()) break; + } + + } + + } + + private string directory = String.Empty; + + private Dictionary<string,XmlWriter> writers = new Dictionary<string,XmlWriter>(); + + private XPathExpression rootExpression; + + private XPathExpression assemblyExpression; + + private XPathExpression summaryExpression; + + private XPathExpression parametersExpression; + + private XPathExpression parameterContentExpression; + + private XPathExpression templatesExpression; + + private XPathExpression templateContentExpression; + + private XPathExpression returnsExpression; + + private XPathExpression exceptionExpression; + + private XPathExpression exceptionCrefExpression; + + private XPathExpression enumerationExpression; + + private XPathExpression enumerationApiExpression; + + private XPathExpression memberSummaryExpression; + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs new file mode 100644 index 0000000..fd1b5fc --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.XPath; + + +namespace Microsoft.Ddue.Tools { + + /// <summary> + /// The LiveExampleComponent replaces ddue:CodeReference elements with links to runnable code samples, + /// if the type is "run" or "view". All other kinds of code samples are passed through to the + /// standard example component, except that the type prefix is removed. A Parsnip "approval" file can + /// be used to omit code samples that did not pass validation, or to replace broken samples with a message + /// to that effect. + /// </summary> + public class LiveExampleComponent : BuildComponent { + + readonly string wdxNamespace = "http://temp.uri/wdx"; + readonly string wdxPrefix = "wdx"; + + XPathExpression selector; + XmlNamespaceManager context; + + bool omitBadExamples; + bool runBadExamples; + + Dictionary<string, SampleInfo> sampleInfoTable; + + public LiveExampleComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) { + + XPathNavigator parsnip_node = configuration.SelectSingleNode("parsnip"); + string approvedFile = null; + if (parsnip_node != null) { + approvedFile = parsnip_node.GetAttribute("approved-file", String.Empty); + + string omitBadExamplesValue = parsnip_node.GetAttribute("omit-bad-examples", String.Empty); + if (!string.IsNullOrEmpty(omitBadExamplesValue)) + omitBadExamples = Boolean.Parse(omitBadExamplesValue); + + string runBadExamplesValue = parsnip_node.GetAttribute("run-bad-examples", String.Empty); + if (!string.IsNullOrEmpty(runBadExamplesValue)) + runBadExamples = Boolean.Parse(runBadExamplesValue); + } + + if (string.IsNullOrEmpty(approvedFile)) + WriteMessage(MessageLevel.Warn, "No approved samples file specified; all available samples will be included."); + else + LoadApprovedFile(approvedFile); + + context = new CustomContext(); + context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); + + selector = XPathExpression.Compile("//ddue:codeReference"); + selector.SetContext(context); + } + + static XPathNavigator[] ConvertIteratorToArray(XPathNodeIterator iterator) { + XPathNavigator[] result = new XPathNavigator[iterator.Count]; + for (int i = 0; i < result.Length; i++) { + iterator.MoveNext(); + result[i] = iterator.Current.Clone(); + } + return (result); + } + + public override void Apply(XmlDocument document, string key) { + XPathNodeIterator nodesIterator = document.CreateNavigator().Select(selector); + XPathNavigator[] nodes = ConvertIteratorToArray(nodesIterator); + + foreach (XPathNavigator node in nodes) { + CodeReference cref = new CodeReference(node.Value); + + SampleInfo si = null; + if (sampleInfoTable != null && cref.ExampleName != null) + sampleInfoTable.TryGetValue(cref.ExampleName, out si); + + WriteMessage(MessageLevel.Info, string.Format("*** codeReference={0}; approved={1}; type={2}", + node.Value, (si == null) ? false : si.IsApproved("CS"), cref.Type)); + + + switch (cref.Type) { + case CodeReferenceType.Msdn: + // TODO: remove "msdn:" from code reference and let ExampleComponent handle the snippet. + // We'll either pass this through to the regular ExampleComponent or delete the node. + WriteMessage(MessageLevel.Warn, "MSDN-only links not implemented yet."); + break; + + case CodeReferenceType.Run: + case CodeReferenceType.View: + if (si != null || !omitBadExamples) { + WriteMessage(MessageLevel.Info, string.Format("+ LiveCode: Kind={0}, SampleName={1}", cref.Type.ToString(), cref.ExamplePath)); + XmlWriter writer = node.InsertAfter(); + writer.WriteStartElement(wdxPrefix, "LiveCode", wdxNamespace); + writer.WriteAttributeString("Kind", cref.Type.ToString()); + writer.WriteAttributeString("SampleName", cref.ExamplePath); + writer.WriteAttributeString("runat", "server"); + writer.WriteEndElement(); + writer.Close(); + node.DeleteSelf(); + } + break; + + case CodeReferenceType.Snippet: + // Ignore; let ExampleComponent handle the snippet. + break; + + default: + WriteMessage(MessageLevel.Warn, string.Format("Invalid code example reference ignored: '{0}'", node.Value)); + break; + } + } + } + + /// <summary> + /// LoadApprovedFile reads the Parsnip approval file into a memory structure for easy lookup. We're assuming that the + /// content of this file is well formed and valid. Semantic errors will be silently ignored. Only samples with + /// approved units will be added to the lookup table. + /// </summary> + void LoadApprovedFile(string pathName) { + WriteMessage(MessageLevel.Info, string.Format("Loading Parsnip 'Approved' file: {0}", pathName)); + sampleInfoTable = new Dictionary<string, SampleInfo>(); + using (XmlReader reader = XmlReader.Create(pathName)) { + SampleInfo si = null; + string sample_name = null; + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.Element: + if (reader.Name == "Sample") { + sample_name = reader.GetAttribute("name"); + if (!string.IsNullOrEmpty(sample_name)) + si = new SampleInfo(sample_name); + } + else if (si != null && reader.Name == "Unit") { + if (reader.GetAttribute("include") == "true") + si.AddApprovedUnit(reader.GetAttribute("name")); + } + break; + case XmlNodeType.EndElement: + if (reader.Name == "Sample") { + if (si != null) { + try { + if (si.ApprovedUnitsCount > 0) + sampleInfoTable.Add(si.Name, si); + } + catch (Exception x) { + WriteMessage(MessageLevel.Warn, string.Format("Sample {0} cannot be loaded {1}", si.Name, x.Message)); + } + } + si = null; + } + break; + default: + break; + } + } + } + } + } + + internal class SampleInfo { + string _name; + internal string Name { + get { return _name; } + } + + List<string> _approvedUnits; + + internal SampleInfo(string name) { + _name = name; + } + + internal void AddApprovedUnit(string unit_name) { + if (_approvedUnits == null) + _approvedUnits = new List<string>(); + _approvedUnits.Add(unit_name); + } + + internal bool IsApproved(string unit_name) { + return (_approvedUnits == null) ? false : _approvedUnits.Contains(unit_name); + } + + internal int ApprovedUnitsCount { + get { return (_approvedUnits == null) ? 0 : _approvedUnits.Count; } + } + } +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Makefile.org b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Makefile.org new file mode 100644 index 0000000..46dba8f --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Makefile.org @@ -0,0 +1,13 @@ +LIBRARY = ..\BuildAssembler.exe + +FLOW_COMPONENTS = IfThenComponent.cs ForEachComponent.cs CloneComponent.cs +DIAGNOSTIC_COMPONENTS = DisplayComponent.cs ValidateComponent.cs +PROCESSING_COMPONENTS = CopyFromFileComponent.cs CopyFromDirectoryComponent.cs SharedContentComponent.cs TransformComponent.cs SaveComponent.cs ResolveLinksComponent.cs + +COMPONENTS = $(FLOW_COMPONENTS) $(DIAGNOSTIC_COMPONENTS) $(PROCESSING_COMPONENTS) + +all: BuildComponents.dll + +BuildComponents.dll: *Component.cs CustomContext.cs $(LIBRARY) + csc /t:library /out:BuildComponents.dll *Component.cs CustomContext.cs /r:$(LIBRARY) + copy BuildComponents.dll ..\..\..\ProductionTools\BuildComponents\ diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Microsoft.Ddue.Tools.BuildComponents.asmmeta b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Microsoft.Ddue.Tools.BuildComponents.asmmeta new file mode 100644 index 0000000..1c21d0f --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Microsoft.Ddue.Tools.BuildComponents.asmmeta @@ -0,0 +1,1255 @@ +.assembly extern Microsoft.Ddue.Tools.BuildAssemblerLibrary +{ + .publickeytoken = (4D DC 2B E8 53 17 C2 74) +} +.assembly extern System +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89) +} +.assembly extern System.Web.Services +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A) +} +.assembly extern System.Xml +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89) +} +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89) +} +.assembly Microsoft.Ddue.Tools.BuildComponents +{ + .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 EF 09 EE D7 93 0B 34 BA 88 83 E7 DB 9F 08 F5 DF A0 D9 F1 7A 97 8E 98 F3 99 03 36 B2 2A 75 D6 BB 2C 25 90 6C 4F 4E 5D 42 60 00 A9 22 00 A9 CE FE 3F 5E C4 22 BA 1E FF 47 7D C4 14 E7 52 C3 30 07 1C 66 1C 58 3F 28 48 0C 03 35 94 CE 5F A5 FC 44 94 D2 A4 42 95 E5 A3 3E AD B2 FD FF 45 13 77 FD BE 62 48 38 EF 02 BF 22 54 00 56 5D DB DA 10 D8 7E 77 F9 7F 9F 20 60 11 4B 49 3A 4D 62 FE C3 C3) + .hash algorithm 0x00008004 +} +.class public ContentService + extends [System.Web.Services]System.Web.Services.Protocols.SoapHttpClientProtocol +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .method public hidebysig specialname + instance void add_GetContentCompleted(class GetContentCompletedEventHandler 'value') + synchronized + { + ret + } + .method public hidebysig specialname + instance void remove_GetContentCompleted(class GetContentCompletedEventHandler 'value') + synchronized + { + ret + } + .method public hidebysig specialname + instance void add_GetNavigationPathsCompleted(class GetNavigationPathsCompletedEventHandler 'value') + synchronized + { + ret + } + .method public hidebysig specialname + instance void remove_GetNavigationPathsCompleted(class GetNavigationPathsCompletedEventHandler 'value') + synchronized + { + ret + } + .method public hidebysig + instance class getContentResponse GetContent(class getContentRequest getContentRequest) + { + ret + } + .method public hidebysig + instance class [mscorlib]System.IAsyncResult BeginGetContent(class getContentRequest getContentRequest, class [mscorlib]System.AsyncCallback callback, object asyncState) + { + ret + } + .method public hidebysig + instance class getContentResponse EndGetContent(class [mscorlib]System.IAsyncResult asyncResult) + { + ret + } + .method public hidebysig + instance void GetContentAsync(class getContentRequest getContentRequest) + { + ret + } + .method public hidebysig + instance void GetContentAsync(class getContentRequest getContentRequest, object userState) + { + ret + } + .method public hidebysig + instance class getNavigationPathsResponse GetNavigationPaths(class getNavigationPathsRequest getNavigationPathsRequest) + { + ret + } + .method public hidebysig + instance class [mscorlib]System.IAsyncResult BeginGetNavigationPaths(class getNavigationPathsRequest getNavigationPathsRequest, class [mscorlib]System.AsyncCallback callback, object asyncState) + { + ret + } + .method public hidebysig + instance class getNavigationPathsResponse EndGetNavigationPaths(class [mscorlib]System.IAsyncResult asyncResult) + { + ret + } + .method public hidebysig + instance void GetNavigationPathsAsync(class getNavigationPathsRequest getNavigationPathsRequest) + { + ret + } + .method public hidebysig + instance void GetNavigationPathsAsync(class getNavigationPathsRequest getNavigationPathsRequest, object userState) + { + ret + } + .method public hidebysig + instance void CancelAsync(object userState) + { + ret + } + .event GetContentCompletedEventHandler GetContentCompleted + { + .addon instance void ContentService::add_GetContentCompleted(class GetContentCompletedEventHandler) + .removeon instance void ContentService::remove_GetContentCompleted(class GetContentCompletedEventHandler) + } + .event GetNavigationPathsCompletedEventHandler GetNavigationPathsCompleted + { + .addon instance void ContentService::add_GetNavigationPathsCompleted(class GetNavigationPathsCompletedEventHandler) + .removeon instance void ContentService::remove_GetNavigationPathsCompleted(class GetNavigationPathsCompletedEventHandler) + } +} +.class public GetContentCompletedEventArgs + extends [System]System.ComponentModel.AsyncCompletedEventArgs +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class getContentResponse get_Result() + { + ret + } + .property instance class getContentResponse Result() + { + .get instance class getContentResponse GetContentCompletedEventArgs::get_Result() + } +} +.class public sealed GetContentCompletedEventHandler + extends [mscorlib]System.MulticastDelegate +{ + .method public hidebysig specialname + instance void .ctor(object 'object', native int 'method') + runtime + { + } + .method public virtual hidebysig newslot + instance void Invoke(object sender, class GetContentCompletedEventArgs e) + runtime + { + } + .method public virtual hidebysig newslot + instance class [mscorlib]System.IAsyncResult BeginInvoke(object sender, class GetContentCompletedEventArgs e, class [mscorlib]System.AsyncCallback callback, object 'object') + runtime + { + } + .method public virtual hidebysig newslot + instance void EndInvoke(class [mscorlib]System.IAsyncResult result) + runtime + { + } +} +.class public GetNavigationPathsCompletedEventArgs + extends [System]System.ComponentModel.AsyncCompletedEventArgs +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class getNavigationPathsResponse get_Result() + { + ret + } + .property instance class getNavigationPathsResponse Result() + { + .get instance class getNavigationPathsResponse GetNavigationPathsCompletedEventArgs::get_Result() + } +} +.class public sealed GetNavigationPathsCompletedEventHandler + extends [mscorlib]System.MulticastDelegate +{ + .method public hidebysig specialname + instance void .ctor(object 'object', native int 'method') + runtime + { + } + .method public virtual hidebysig newslot + instance void Invoke(object sender, class GetNavigationPathsCompletedEventArgs e) + runtime + { + } + .method public virtual hidebysig newslot + instance class [mscorlib]System.IAsyncResult BeginInvoke(object sender, class GetNavigationPathsCompletedEventArgs e, class [mscorlib]System.AsyncCallback callback, object 'object') + runtime + { + } + .method public virtual hidebysig newslot + instance void EndInvoke(class [mscorlib]System.IAsyncResult result) + runtime + { + } +} +.class public serializable availableVersionAndLocale + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_locale() + { + ret + } + .method public hidebysig specialname + instance void set_locale(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_version() + { + ret + } + .method public hidebysig specialname + instance void set_version(string 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance string locale() + { + .get instance string availableVersionAndLocale::get_locale() + .set instance void availableVersionAndLocale::set_locale(string) + } + .property instance string version() + { + .get instance string availableVersionAndLocale::get_version() + .set instance void availableVersionAndLocale::set_version(string) + } +} +.class public serializable common + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class [System.Xml]System.Xml.XmlElement[] get_Any() + { + ret + } + .method public hidebysig specialname + instance void set_Any(class [System.Xml]System.Xml.XmlElement[] 'value') + { + ret + } + .method public hidebysig specialname + instance string get_commonFormat() + { + ret + } + .method public hidebysig specialname + instance void set_commonFormat(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_location() + { + ret + } + .method public hidebysig specialname + instance void set_location(string 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class [System.Xml]System.Xml.XmlElement[] Any() + { + .get instance class [System.Xml]System.Xml.XmlElement[] common::get_Any() + .set instance void common::set_Any(class [System.Xml]System.Xml.XmlElement[]) + } + .property instance string commonFormat() + { + .get instance string common::get_commonFormat() + .set instance void common::set_commonFormat(string) + } + .property instance string location() + { + .get instance string common::get_location() + .set instance void common::set_location(string) + } +} +.class public sealed serializable documentTypes + extends [mscorlib]System.Enum +{ + .field public rtspecialname specialname int32 value__ + .field static public literal valuetype documentTypes common = int32(0x00000001) + .field static public literal valuetype documentTypes feature = int32(0x00000003) + .field static public literal valuetype documentTypes image = int32(0x00000002) + .field static public literal valuetype documentTypes primary = int32(0x00000000) +} +.class public serializable feature + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class [System.Xml]System.Xml.XmlElement[] get_Any() + { + ret + } + .method public hidebysig specialname + instance void set_Any(class [System.Xml]System.Xml.XmlElement[] 'value') + { + ret + } + .method public hidebysig specialname + instance string get_featureFormat() + { + ret + } + .method public hidebysig specialname + instance void set_featureFormat(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_location() + { + ret + } + .method public hidebysig specialname + instance void set_location(string 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class [System.Xml]System.Xml.XmlElement[] Any() + { + .get instance class [System.Xml]System.Xml.XmlElement[] feature::get_Any() + .set instance void feature::set_Any(class [System.Xml]System.Xml.XmlElement[]) + } + .property instance string featureFormat() + { + .get instance string feature::get_featureFormat() + .set instance void feature::set_featureFormat(string) + } + .property instance string location() + { + .get instance string feature::get_location() + .set instance void feature::set_location(string) + } +} +.class public serializable getContentRequest + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_contentIdentifier() + { + ret + } + .method public hidebysig specialname + instance void set_contentIdentifier(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_locale() + { + ret + } + .method public hidebysig specialname + instance void set_locale(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_version() + { + ret + } + .method public hidebysig specialname + instance void set_version(string 'value') + { + ret + } + .method public hidebysig specialname + instance class requestedDocument[] get_requestedDocuments() + { + ret + } + .method public hidebysig specialname + instance void set_requestedDocuments(class requestedDocument[] 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance string contentIdentifier() + { + .get instance string getContentRequest::get_contentIdentifier() + .set instance void getContentRequest::set_contentIdentifier(string) + } + .property instance string locale() + { + .get instance string getContentRequest::get_locale() + .set instance void getContentRequest::set_locale(string) + } + .property instance class requestedDocument[] requestedDocuments() + { + .get instance class requestedDocument[] getContentRequest::get_requestedDocuments() + .set instance void getContentRequest::set_requestedDocuments(class requestedDocument[]) + } + .property instance string version() + { + .get instance string getContentRequest::get_version() + .set instance void getContentRequest::set_version(string) + } +} +.class public serializable getContentResponse + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_contentId() + { + ret + } + .method public hidebysig specialname + instance void set_contentId(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_contentGuid() + { + ret + } + .method public hidebysig specialname + instance void set_contentGuid(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_contentAlias() + { + ret + } + .method public hidebysig specialname + instance void set_contentAlias(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_locale() + { + ret + } + .method public hidebysig specialname + instance void set_locale(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_version() + { + ret + } + .method public hidebysig specialname + instance void set_version(string 'value') + { + ret + } + .method public hidebysig specialname + instance class availableVersionAndLocale[] get_availableVersionsAndLocales() + { + ret + } + .method public hidebysig specialname + instance void set_availableVersionsAndLocales(class availableVersionAndLocale[] 'value') + { + ret + } + .method public hidebysig specialname + instance class primary[] get_primaryDocuments() + { + ret + } + .method public hidebysig specialname + instance void set_primaryDocuments(class primary[] 'value') + { + ret + } + .method public hidebysig specialname + instance class image[] get_imageDocuments() + { + ret + } + .method public hidebysig specialname + instance void set_imageDocuments(class image[] 'value') + { + ret + } + .method public hidebysig specialname + instance class common[] get_commonDocuments() + { + ret + } + .method public hidebysig specialname + instance void set_commonDocuments(class common[] 'value') + { + ret + } + .method public hidebysig specialname + instance class feature[] get_featureDocuments() + { + ret + } + .method public hidebysig specialname + instance void set_featureDocuments(class feature[] 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class availableVersionAndLocale[] availableVersionsAndLocales() + { + .get instance class availableVersionAndLocale[] getContentResponse::get_availableVersionsAndLocales() + .set instance void getContentResponse::set_availableVersionsAndLocales(class availableVersionAndLocale[]) + } + .property instance class common[] commonDocuments() + { + .get instance class common[] getContentResponse::get_commonDocuments() + .set instance void getContentResponse::set_commonDocuments(class common[]) + } + .property instance string contentAlias() + { + .get instance string getContentResponse::get_contentAlias() + .set instance void getContentResponse::set_contentAlias(string) + } + .property instance string contentGuid() + { + .get instance string getContentResponse::get_contentGuid() + .set instance void getContentResponse::set_contentGuid(string) + } + .property instance string contentId() + { + .get instance string getContentResponse::get_contentId() + .set instance void getContentResponse::set_contentId(string) + } + .property instance class feature[] featureDocuments() + { + .get instance class feature[] getContentResponse::get_featureDocuments() + .set instance void getContentResponse::set_featureDocuments(class feature[]) + } + .property instance class image[] imageDocuments() + { + .get instance class image[] getContentResponse::get_imageDocuments() + .set instance void getContentResponse::set_imageDocuments(class image[]) + } + .property instance string locale() + { + .get instance string getContentResponse::get_locale() + .set instance void getContentResponse::set_locale(string) + } + .property instance class primary[] primaryDocuments() + { + .get instance class primary[] getContentResponse::get_primaryDocuments() + .set instance void getContentResponse::set_primaryDocuments(class primary[]) + } + .property instance string version() + { + .get instance string getContentResponse::get_version() + .set instance void getContentResponse::set_version(string) + } +} +.class public serializable getNavigationPathsRequest + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class navigationKey get_root() + { + ret + } + .method public hidebysig specialname + instance void set_root(class navigationKey 'value') + { + ret + } + .method public hidebysig specialname + instance class navigationKey get_target() + { + ret + } + .method public hidebysig specialname + instance void set_target(class navigationKey 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class navigationKey root() + { + .get instance class navigationKey getNavigationPathsRequest::get_root() + .set instance void getNavigationPathsRequest::set_root(class navigationKey) + } + .property instance class navigationKey target() + { + .get instance class navigationKey getNavigationPathsRequest::get_target() + .set instance void getNavigationPathsRequest::set_target(class navigationKey) + } +} +.class public serializable getNavigationPathsResponse + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class navigationPath[] get_navigationPaths() + { + ret + } + .method public hidebysig specialname + instance void set_navigationPaths(class navigationPath[] 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class navigationPath[] navigationPaths() + { + .get instance class navigationPath[] getNavigationPathsResponse::get_navigationPaths() + .set instance void getNavigationPathsResponse::set_navigationPaths(class navigationPath[]) + } +} +.class public serializable image + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_name() + { + ret + } + .method public hidebysig specialname + instance void set_name(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_imageFormat() + { + ret + } + .method public hidebysig specialname + instance void set_imageFormat(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_location() + { + ret + } + .method public hidebysig specialname + instance void set_location(string 'value') + { + ret + } + .method public hidebysig specialname + instance unsigned int8[] get_Value() + { + ret + } + .method public hidebysig specialname + instance void set_Value(unsigned int8[] 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance unsigned int8[] Value() + { + .get instance unsigned int8[] image::get_Value() + .set instance void image::set_Value(unsigned int8[]) + } + .property instance string imageFormat() + { + .get instance string image::get_imageFormat() + .set instance void image::set_imageFormat(string) + } + .property instance string location() + { + .get instance string image::get_location() + .set instance void image::set_location(string) + } + .property instance string name() + { + .get instance string image::get_name() + .set instance void image::set_name(string) + } +} +.class public serializable navigationKey + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_contentId() + { + ret + } + .method public hidebysig specialname + instance void set_contentId(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_locale() + { + ret + } + .method public hidebysig specialname + instance void set_locale(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_version() + { + ret + } + .method public hidebysig specialname + instance void set_version(string 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance string contentId() + { + .get instance string navigationKey::get_contentId() + .set instance void navigationKey::set_contentId(string) + } + .property instance string locale() + { + .get instance string navigationKey::get_locale() + .set instance void navigationKey::set_locale(string) + } + .property instance string version() + { + .get instance string navigationKey::get_version() + .set instance void navigationKey::set_version(string) + } +} +.class public serializable navigationPath + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class navigationPathNode[] get_navigationPathNodes() + { + ret + } + .method public hidebysig specialname + instance void set_navigationPathNodes(class navigationPathNode[] 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class navigationPathNode[] navigationPathNodes() + { + .get instance class navigationPathNode[] navigationPath::get_navigationPathNodes() + .set instance void navigationPath::set_navigationPathNodes(class navigationPathNode[]) + } +} +.class public serializable navigationPathNode + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_title() + { + ret + } + .method public hidebysig specialname + instance void set_title(string 'value') + { + ret + } + .method public hidebysig specialname + instance class navigationKey get_navigationNodeKey() + { + ret + } + .method public hidebysig specialname + instance void set_navigationNodeKey(class navigationKey 'value') + { + ret + } + .method public hidebysig specialname + instance class navigationKey get_contentNodeKey() + { + ret + } + .method public hidebysig specialname + instance void set_contentNodeKey(class navigationKey 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class navigationKey contentNodeKey() + { + .get instance class navigationKey navigationPathNode::get_contentNodeKey() + .set instance void navigationPathNode::set_contentNodeKey(class navigationKey) + } + .property instance class navigationKey navigationNodeKey() + { + .get instance class navigationKey navigationPathNode::get_navigationNodeKey() + .set instance void navigationPathNode::set_navigationNodeKey(class navigationKey) + } + .property instance string title() + { + .get instance string navigationPathNode::get_title() + .set instance void navigationPathNode::set_title(string) + } +} +.class public serializable primary + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance class [System.Xml]System.Xml.XmlElement get_Any() + { + ret + } + .method public hidebysig specialname + instance void set_Any(class [System.Xml]System.Xml.XmlElement 'value') + { + ret + } + .method public hidebysig specialname + instance string get_primaryFormat() + { + ret + } + .method public hidebysig specialname + instance void set_primaryFormat(string 'value') + { + ret + } + .method public hidebysig specialname + instance string get_location() + { + ret + } + .method public hidebysig specialname + instance void set_location(string 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance class [System.Xml]System.Xml.XmlElement Any() + { + .get instance class [System.Xml]System.Xml.XmlElement primary::get_Any() + .set instance void primary::set_Any(class [System.Xml]System.Xml.XmlElement) + } + .property instance string location() + { + .get instance string primary::get_location() + .set instance void primary::set_location(string) + } + .property instance string primaryFormat() + { + .get instance string primary::get_primaryFormat() + .set instance void primary::set_primaryFormat(string) + } +} +.class public serializable requestedDocument + extends [mscorlib]System.Object +{ + .custom instance void [System]System.ComponentModel.DesignerCategoryAttribute::.ctor(string) = { string('code') } + .method public hidebysig specialname + instance string get_selector() + { + ret + } + .method public hidebysig specialname + instance void set_selector(string 'value') + { + ret + } + .method public hidebysig specialname + instance valuetype documentTypes get_type() + { + ret + } + .method public hidebysig specialname + instance void set_type(valuetype documentTypes 'value') + { + ret + } + .method public hidebysig specialname + instance void .ctor() + { + ret + } + .property instance string selector() + { + .get instance string requestedDocument::get_selector() + .set instance void requestedDocument::set_selector(string) + } + .property instance valuetype documentTypes 'type'() + { + .get instance valuetype documentTypes requestedDocument::get_type() + .set instance void requestedDocument::set_type(valuetype documentTypes) + } +} +.namespace Microsoft.Ddue.Tools +{ + .class public CloneComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public CopyFromFileComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public CopyFromIndexComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public DisplayComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public ForEachComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public IfThenComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public ManagedSyntaxWriter + extends Microsoft.Ddue.Tools.SyntaxWriter + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator location) + { + ret + } + .method public virtual hidebysig + instance void WriteStartBlock(string language) + { + ret + } + .method public virtual hidebysig + instance void WriteString(string text) + { + ret + } + .method public virtual hidebysig + instance void WriteStringWithStyle(string text, string style) + { + ret + } + .method public virtual hidebysig + instance void WriteReferenceLink(string reference) + { + ret + } + .method public virtual hidebysig + instance void WriteReferenceLink(string reference, string text) + { + ret + } + .method public virtual hidebysig + instance void WriteEndBlock() + { + ret + } + } + .class public ResolveConceptualLinksComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public ResolveReferenceLinksComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public SaveComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public SharedContentComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public SyntaxComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public abstract SyntaxGenerator + extends [mscorlib]System.Object + { + .method public virtual hidebysig newslot abstract + instance void WriteSyntax(class [System.Xml]System.Xml.XPath.XPathNavigator reflection, class Microsoft.Ddue.Tools.SyntaxWriter writer) + { + } + .method family hidebysig specialname + instance void .ctor() + { + ret + } + } + .class public abstract SyntaxWriter + extends [mscorlib]System.Object + { + .method family hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator location) + { + ret + } + .method public virtual hidebysig newslot abstract + instance void WriteStartBlock(string language) + { + } + .method public virtual hidebysig newslot abstract + instance void WriteString(string text) + { + } + .method public virtual hidebysig newslot abstract + instance void WriteStringWithStyle(string text, string style) + { + } + .method public virtual hidebysig newslot abstract + instance void WriteReferenceLink(string reference) + { + } + .method public virtual hidebysig newslot abstract + instance void WriteReferenceLink(string reference, string text) + { + } + .method public virtual hidebysig newslot + instance void WriteLine() + { + ret + } + .method public virtual hidebysig newslot + instance void WriteKeyword(string keyword) + { + ret + } + .method public virtual hidebysig newslot + instance void WriteParameter(string parameter) + { + ret + } + .method public virtual hidebysig newslot + instance void WriteIdentifier(string identifier) + { + ret + } + .method public virtual hidebysig newslot + instance void WriteLiteral(string 'literal') + { + ret + } + .method public virtual hidebysig newslot + instance void WriteMessage(string message) + { + ret + } + .method public virtual hidebysig newslot abstract + instance void WriteEndBlock() + { + } + } + .class public TransformComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } + .class public ValidateComponent + extends [Microsoft.Ddue.Tools.BuildAssemblerLibrary]Microsoft.Ddue.Tools.BuildComponent + { + .method public hidebysig specialname + instance void .ctor(class [System.Xml]System.Xml.XPath.XPathNavigator configuration) + { + ret + } + .method public virtual hidebysig + instance void Apply(class [System.Xml]System.Xml.XmlDocument document, string key) + { + ret + } + } +} + diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnResolver.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnResolver.cs new file mode 100644 index 0000000..95ad854 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnResolver.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Net; +using System.Web.Services.Protocols; + +namespace Microsoft.Ddue.Tools { + + public class MsdnResolver { + + public MsdnResolver () { + msdnService = new ContentService(); + msdnService.appIdValue = new appId(); + msdnService.appIdValue.value = "Sandcastle"; + msdnService.SoapVersion = SoapProtocolVersion.Soap11; + } + + public bool IsDisabled { + get { + return ((msdnService == null)); + } + } + + public string Locale { + get { + return (locale); + } + set { + locale = value; + } + } + + public string GetMsdnUrl (string id) { + + if (cachedMsdnUrls.ContainsKey(id)) return (String.Format(urlFormat, locale, cachedMsdnUrls[id])); + + if (msdnService == null) return(null); + + getContentRequest msdnRequest = new getContentRequest(); + msdnRequest.contentIdentifier = "AssetId:" + id; + msdnRequest.locale = locale; + + string endpoint = null; + try { + getContentResponse msdnResponse = msdnService.GetContent(msdnRequest); + endpoint = msdnResponse.contentId; + } catch (WebException e) { + msdnService = null; + } catch (SoapException) { + // lookup failed + } + + cachedMsdnUrls[id] = endpoint; + + if (String.IsNullOrEmpty(endpoint)) { + return (null); + } else { + return (String.Format(urlFormat, locale, endpoint)); + } + } + + private ContentService msdnService; + + private string locale = "en-us"; + + private static string urlFormat = "http://msdn2.microsoft.com/{0}/library/{1}"; + + private Dictionary<string, string> cachedMsdnUrls = new Dictionary<string, string>(); + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnService.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnService.cs new file mode 100644 index 0000000..e935faf --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnService.cs @@ -0,0 +1,913 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.832 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Web.Services; +using System.Web.Services.Protocols; +using System.Xml.Serialization; + +// +// This source code was auto-generated by wsdl, Version=2.0.50727.42. +// + + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Web.Services.WebServiceBindingAttribute(Name="ContentServiceBinding", Namespace="urn:msdn-com:public-content-syndication")] +[System.Xml.Serialization.XmlIncludeAttribute(typeof(requestedDocument[]))] +[System.Xml.Serialization.XmlIncludeAttribute(typeof(primary[]))] +[System.Xml.Serialization.XmlIncludeAttribute(typeof(image[]))] +[System.Xml.Serialization.XmlIncludeAttribute(typeof(common[]))] +[System.Xml.Serialization.XmlIncludeAttribute(typeof(feature[]))] +public partial class ContentService : System.Web.Services.Protocols.SoapHttpClientProtocol { + + private appId appIdValueField; + + private System.Threading.SendOrPostCallback GetContentOperationCompleted; + + private System.Threading.SendOrPostCallback GetNavigationPathsOperationCompleted; + + /// <remarks/> + public ContentService() { + this.Url = "http://services.msdn.microsoft.com/ContentServices/ContentService.asmx"; + } + + public appId appIdValue { + get { + return this.appIdValueField; + } + set { + this.appIdValueField = value; + } + } + + /// <remarks/> + public event GetContentCompletedEventHandler GetContentCompleted; + + /// <remarks/> + public event GetNavigationPathsCompletedEventHandler GetNavigationPathsCompleted; + + /// <remarks/> + [System.Web.Services.Protocols.SoapHeaderAttribute("appIdValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:msdn-com:public-content-syndication/GetContent", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)] + [return: System.Xml.Serialization.XmlElementAttribute("getContentResponse", Namespace="urn:msdn-com:public-content-syndication")] + public getContentResponse GetContent([System.Xml.Serialization.XmlElementAttribute(Namespace="urn:msdn-com:public-content-syndication")] getContentRequest getContentRequest) { + object[] results = this.Invoke("GetContent", new object[] { + getContentRequest}); + return ((getContentResponse)(results[0])); + } + + /// <remarks/> + public System.IAsyncResult BeginGetContent(getContentRequest getContentRequest, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetContent", new object[] { + getContentRequest}, callback, asyncState); + } + + /// <remarks/> + public getContentResponse EndGetContent(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((getContentResponse)(results[0])); + } + + /// <remarks/> + public void GetContentAsync(getContentRequest getContentRequest) { + this.GetContentAsync(getContentRequest, null); + } + + /// <remarks/> + public void GetContentAsync(getContentRequest getContentRequest, object userState) { + if ((this.GetContentOperationCompleted == null)) { + this.GetContentOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetContentOperationCompleted); + } + this.InvokeAsync("GetContent", new object[] { + getContentRequest}, this.GetContentOperationCompleted, userState); + } + + private void OnGetContentOperationCompleted(object arg) { + if ((this.GetContentCompleted != null)) { + System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); + this.GetContentCompleted(this, new GetContentCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); + } + } + + /// <remarks/> + [System.Web.Services.Protocols.SoapHeaderAttribute("appIdValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:msdn-com:public-content-syndication/GetNavigationPaths", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)] + [return: System.Xml.Serialization.XmlElementAttribute("getNavigationPathsResponse", Namespace="urn:msdn-com:public-content-syndication")] + public getNavigationPathsResponse GetNavigationPaths([System.Xml.Serialization.XmlElementAttribute(Namespace="urn:msdn-com:public-content-syndication")] getNavigationPathsRequest getNavigationPathsRequest) { + object[] results = this.Invoke("GetNavigationPaths", new object[] { + getNavigationPathsRequest}); + return ((getNavigationPathsResponse)(results[0])); + } + + /// <remarks/> + public System.IAsyncResult BeginGetNavigationPaths(getNavigationPathsRequest getNavigationPathsRequest, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetNavigationPaths", new object[] { + getNavigationPathsRequest}, callback, asyncState); + } + + /// <remarks/> + public getNavigationPathsResponse EndGetNavigationPaths(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((getNavigationPathsResponse)(results[0])); + } + + /// <remarks/> + public void GetNavigationPathsAsync(getNavigationPathsRequest getNavigationPathsRequest) { + this.GetNavigationPathsAsync(getNavigationPathsRequest, null); + } + + /// <remarks/> + public void GetNavigationPathsAsync(getNavigationPathsRequest getNavigationPathsRequest, object userState) { + if ((this.GetNavigationPathsOperationCompleted == null)) { + this.GetNavigationPathsOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetNavigationPathsOperationCompleted); + } + this.InvokeAsync("GetNavigationPaths", new object[] { + getNavigationPathsRequest}, this.GetNavigationPathsOperationCompleted, userState); + } + + private void OnGetNavigationPathsOperationCompleted(object arg) { + if ((this.GetNavigationPathsCompleted != null)) { + System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); + this.GetNavigationPathsCompleted(this, new GetNavigationPathsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); + } + } + + /// <remarks/> + public new void CancelAsync(object userState) { + base.CancelAsync(userState); + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication/2006/09/common")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="urn:msdn-com:public-content-syndication/2006/09/common", IsNullable=false)] +public partial class appId : System.Web.Services.Protocols.SoapHeader { + + private string valueField; + + /// <remarks/> + public string value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public partial class navigationPathNode { + + private string titleField; + + private bool isPhantomField; + + private bool isPhantomFieldSpecified; + + private navigationKey navigationNodeKeyField; + + private navigationKey contentNodeKeyField; + + /// <remarks/> + public string title { + get { + return this.titleField; + } + set { + this.titleField = value; + } + } + + /// <remarks/> + public bool isPhantom { + get { + return this.isPhantomField; + } + set { + this.isPhantomField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool isPhantomSpecified { + get { + return this.isPhantomFieldSpecified; + } + set { + this.isPhantomFieldSpecified = value; + } + } + + /// <remarks/> + public navigationKey navigationNodeKey { + get { + return this.navigationNodeKeyField; + } + set { + this.navigationNodeKeyField = value; + } + } + + /// <remarks/> + public navigationKey contentNodeKey { + get { + return this.contentNodeKeyField; + } + set { + this.contentNodeKeyField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public partial class navigationKey { + + private string contentIdField; + + private string localeField; + + private string versionField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string contentId { + get { + return this.contentIdField; + } + set { + this.contentIdField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string locale { + get { + return this.localeField; + } + set { + this.localeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string version { + get { + return this.versionField; + } + set { + this.versionField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public partial class navigationPath { + + private navigationPathNode[] navigationPathNodesField; + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)] + public navigationPathNode[] navigationPathNodes { + get { + return this.navigationPathNodesField; + } + set { + this.navigationPathNodesField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public partial class availableVersionAndLocale { + + private string localeField; + + private string versionField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string locale { + get { + return this.localeField; + } + set { + this.localeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string version { + get { + return this.versionField; + } + set { + this.versionField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public partial class requestedDocument { + + private string selectorField; + + private documentTypes typeField; + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string selector { + get { + return this.selectorField; + } + set { + this.selectorField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public documentTypes type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:msdn-com:public-content-syndication")] +public enum documentTypes { + + /// <remarks/> + primary, + + /// <remarks/> + common, + + /// <remarks/> + image, + + /// <remarks/> + feature, +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:mtpg-com:mtps/2004/1/primary")] +public partial class primary { + + private System.Xml.XmlElement anyField; + + private string primaryFormatField; + + private string locationField; + + /// <remarks/> + [System.Xml.Serialization.XmlAnyElementAttribute()] + public System.Xml.XmlElement Any { + get { + return this.anyField; + } + set { + this.anyField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="urn:mtpg-com:mtps/2004/1/primary/category")] + public string primaryFormat { + get { + return this.primaryFormatField; + } + set { + this.primaryFormatField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string location { + get { + return this.locationField; + } + set { + this.locationField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:mtpg-com:mtps/2004/1/image")] +public partial class image { + + private string nameField; + + private string imageFormatField; + + private string locationField; + + private byte[] valueField; + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="urn:mtpg-com:mtps/2004/1/image/category")] + public string imageFormat { + get { + return this.imageFormatField; + } + set { + this.imageFormatField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string location { + get { + return this.locationField; + } + set { + this.locationField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlTextAttribute(DataType="base64Binary")] + public byte[] Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:mtpg-com:mtps/2004/1/common")] +public partial class common { + + private System.Xml.XmlElement[] anyField; + + private string commonFormatField; + + private string locationField; + + /// <remarks/> + [System.Xml.Serialization.XmlAnyElementAttribute()] + public System.Xml.XmlElement[] Any { + get { + return this.anyField; + } + set { + this.anyField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="urn:mtpg-com:mtps/2004/1/common/category")] + public string commonFormat { + get { + return this.commonFormatField; + } + set { + this.commonFormatField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string location { + get { + return this.locationField; + } + set { + this.locationField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:mtpg-com:mtps/2004/1/feature")] +public partial class feature { + + private System.Xml.XmlElement[] anyField; + + private string featureFormatField; + + private string locationField; + + /// <remarks/> + [System.Xml.Serialization.XmlAnyElementAttribute()] + public System.Xml.XmlElement[] Any { + get { + return this.anyField; + } + set { + this.anyField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute(Form=System.Xml.Schema.XmlSchemaForm.Qualified, Namespace="urn:mtpg-com:mtps/2004/1/feature/category")] + public string featureFormat { + get { + return this.featureFormatField; + } + set { + this.featureFormatField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlAttributeAttribute()] + public string location { + get { + return this.locationField; + } + set { + this.locationField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:msdn-com:public-content-syndication")] +public partial class getContentRequest { + + private string contentIdentifierField; + + private string localeField; + + private string versionField; + + private requestedDocument[] requestedDocumentsField; + + /// <remarks/> + public string contentIdentifier { + get { + return this.contentIdentifierField; + } + set { + this.contentIdentifierField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string locale { + get { + return this.localeField; + } + set { + this.localeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string version { + get { + return this.versionField; + } + set { + this.versionField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)] + public requestedDocument[] requestedDocuments { + get { + return this.requestedDocumentsField; + } + set { + this.requestedDocumentsField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:msdn-com:public-content-syndication")] +public partial class getContentResponse { + + private string contentIdField; + + private string contentGuidField; + + private string contentAliasField; + + private string localeField; + + private string versionField; + + private availableVersionAndLocale[] availableVersionsAndLocalesField; + + private primary[] primaryDocumentsField; + + private image[] imageDocumentsField; + + private common[] commonDocumentsField; + + private feature[] featureDocumentsField; + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string contentId { + get { + return this.contentIdField; + } + set { + this.contentIdField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string contentGuid { + get { + return this.contentGuidField; + } + set { + this.contentGuidField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string contentAlias { + get { + return this.contentAliasField; + } + set { + this.contentAliasField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string locale { + get { + return this.localeField; + } + set { + this.localeField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:mtpg-com:mtps/2004/1/key")] + public string version { + get { + return this.versionField; + } + set { + this.versionField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)] + public availableVersionAndLocale[] availableVersionsAndLocales { + get { + return this.availableVersionsAndLocalesField; + } + set { + this.availableVersionsAndLocalesField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute("primary", Namespace="urn:mtpg-com:mtps/2004/1/primary", IsNullable=false)] + public primary[] primaryDocuments { + get { + return this.primaryDocumentsField; + } + set { + this.primaryDocumentsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute("image", Namespace="urn:mtpg-com:mtps/2004/1/image", IsNullable=false)] + public image[] imageDocuments { + get { + return this.imageDocumentsField; + } + set { + this.imageDocumentsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute("common", Namespace="urn:mtpg-com:mtps/2004/1/common", IsNullable=false)] + public common[] commonDocuments { + get { + return this.commonDocumentsField; + } + set { + this.commonDocumentsField = value; + } + } + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute("feature", Namespace="urn:mtpg-com:mtps/2004/1/feature", IsNullable=false)] + public feature[] featureDocuments { + get { + return this.featureDocumentsField; + } + set { + this.featureDocumentsField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:msdn-com:public-content-syndication")] +public partial class getNavigationPathsRequest { + + private navigationKey rootField; + + private navigationKey targetField; + + /// <remarks/> + public navigationKey root { + get { + return this.rootField; + } + set { + this.rootField = value; + } + } + + /// <remarks/> + public navigationKey target { + get { + return this.targetField; + } + set { + this.targetField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:msdn-com:public-content-syndication")] +public partial class getNavigationPathsResponse { + + private navigationPath[] navigationPathsField; + + /// <remarks/> + [System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)] + public navigationPath[] navigationPaths { + get { + return this.navigationPathsField; + } + set { + this.navigationPathsField = value; + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +public delegate void GetContentCompletedEventHandler(object sender, GetContentCompletedEventArgs e); + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class GetContentCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { + + private object[] results; + + internal GetContentCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : + base(exception, cancelled, userState) { + this.results = results; + } + + /// <remarks/> + public getContentResponse Result { + get { + this.RaiseExceptionIfNecessary(); + return ((getContentResponse)(this.results[0])); + } + } +} + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +public delegate void GetNavigationPathsCompletedEventHandler(object sender, GetNavigationPathsCompletedEventArgs e); + +/// <remarks/> +[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class GetNavigationPathsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { + + private object[] results; + + internal GetNavigationPathsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : + base(exception, cancelled, userState) { + this.results = results; + } + + /// <remarks/> + public getNavigationPathsResponse Result { + get { + this.RaiseExceptionIfNecessary(); + return ((getNavigationPathsResponse)(this.results[0])); + } + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/PlatformsComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/PlatformsComponent.cs new file mode 100644 index 0000000..7aaa64d --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/PlatformsComponent.cs @@ -0,0 +1,679 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; +using System.IO; +using System.Text; + +// still have problems with spaces + +namespace Microsoft.Ddue.Tools +{ + + public class PlatformsComponent : BuildComponent + { + + private Dictionary<string, Dictionary<string, VersionFilter>> versionFilters = new Dictionary<string, Dictionary<string, VersionFilter>>(); + + private XPathExpression platformQuery = XPathExpression.Compile("/platforms/platform"); + private XPathExpression referenceExpression = XPathExpression.Compile("/document/reference"); + private XPathExpression versionNodesExpression = XPathExpression.Compile("versions//version"); + private XPathExpression apiGroupExpression = XPathExpression.Compile("string(apidata/@group)"); + private XPathExpression topicdataGroupExpression = XPathExpression.Compile("string(topicdata/@group)"); + private XPathExpression topicdataSubgroupExpression = XPathExpression.Compile("string(topicdata/@subgroup)"); + private XPathExpression listTypeNameExpression = XPathExpression.Compile("string(apidata/@name)"); + private XPathExpression apiNamespaceNameExpression = XPathExpression.Compile("string(containers/namespace/apidata/@name)"); + private XPathExpression memberTypeNameExpression = XPathExpression.Compile("string(containers/type/apidata/@name)"); + + private XPathExpression listTopicElementNodesExpression = XPathExpression.Compile("elements//element"); + private XPathExpression elementIdExpression = XPathExpression.Compile("string(@api)"); + + public PlatformsComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) + { + // get the filter files + XPathNodeIterator filterNodes = configuration.Select("filter"); + foreach (XPathNavigator filterNode in filterNodes) + { + string filterFiles = filterNode.GetAttribute("files", String.Empty); + if ((filterFiles == null) || (filterFiles.Length == 0)) + throw new ConfigurationErrorsException("The filter/@files attribute must specify a path."); + ParseDocuments(filterFiles); + } + //WriteMessage(MessageLevel.Info, String.Format("Indexed {0} elements.", index.Count)); + } + + public void ParseDocuments(string wildcardPath) + { + string filterFiles = Environment.ExpandEnvironmentVariables(wildcardPath); + if ((filterFiles == null) || (filterFiles.Length == 0)) + throw new ConfigurationErrorsException("The filter path is an empty string."); + + WriteMessage(MessageLevel.Info, String.Format("Searching for files that match '{0}'.", filterFiles)); + string directoryPart = Path.GetDirectoryName(filterFiles); + if (String.IsNullOrEmpty(directoryPart)) + directoryPart = Environment.CurrentDirectory; + directoryPart = Path.GetFullPath(directoryPart); + string filePart = Path.GetFileName(filterFiles); + string[] files = Directory.GetFiles(directoryPart, filePart); + foreach (string file in files) + ParseDocument(file); + WriteMessage(MessageLevel.Info, String.Format("Found {0} files in {1}.", files.Length, filterFiles)); + } + + private void AddPlatformVersionFilter(string platformId, string versionId, XPathNavigator platformNode, string file) + { + Dictionary<string, VersionFilter> platformFrameworks; + if (!versionFilters.TryGetValue(platformId, out platformFrameworks)) + { + platformFrameworks = new Dictionary<string, VersionFilter>(); + versionFilters.Add(platformId, platformFrameworks); + } + + try + { + VersionFilter filter; + XmlReader platformReader = platformNode.ReadSubtree(); + platformReader.MoveToContent(); + if (!platformFrameworks.TryGetValue(versionId, out filter)) + { + filter = new VersionFilter(platformReader, versionId, file); + } + else + { + // if the platform already has a filter for this version, add the data from the current platform node + filter.AddPlatformNode(platformReader, file); + } + platformReader.Close(); + + platformFrameworks.Remove(versionId); + platformFrameworks.Add(versionId, filter); + } + catch (Exception e) + { + WriteMessage(MessageLevel.Error, e.Message); + } + } + + private void ParseDocument(string file) + { + try + { + XPathDocument document = new XPathDocument(file); + + XPathNodeIterator platformNodes = document.CreateNavigator().Select(platformQuery); + foreach (XPathNavigator platformNode in platformNodes) + { + string platformId = platformNode.GetAttribute("name", String.Empty); + string[] platformIds = platformId.Split(','); + + string version = platformNode.GetAttribute("version", String.Empty); + string[] versionIds = version.Split(','); + for (int i = 0; i < versionIds.Length; i++) + { + for (int j = 0; j < platformIds.Length; j++) + { + XPathNavigator platformNodeClone = platformNode.Clone(); + AddPlatformVersionFilter(platformIds[j], versionIds[i], platformNodeClone, file); + } + } + } + } + catch (Exception e) + { + WriteMessage(MessageLevel.Error, e.Message); + } + } + + public override void Apply(XmlDocument document, string key) + { + XPathNavigator targetDoc = document.CreateNavigator(); + XPathNavigator referenceNode = targetDoc.SelectSingleNode(referenceExpression); + string apiGroup = (string)referenceNode.Evaluate(apiGroupExpression); + string topicdataGroup = (string)referenceNode.Evaluate(topicdataGroupExpression); + string topicdataSubgroup = (string)referenceNode.Evaluate(topicdataSubgroupExpression); + + // get the namespace and typename of the current type to locate the filter information that applies to the current topic + // For filtering inherited members, the platform filters use the namespace and typename of the inheriting type, not the declaring type, + string topicNamespaceName = (string)referenceNode.Evaluate(apiNamespaceNameExpression); + string topicTypeName = (string)referenceNode.Evaluate(memberTypeNameExpression); + + // write platforms info for normal api topics (excluding memberlist and overload list topics + if (topicdataGroup != "list" && topicdataSubgroup != "DerivedTypeList" && (apiGroup == "type" || apiGroup == "member") && versionFilters.Count > 0) + { + WriteApiPlatforms(referenceNode, apiGroup, key, topicTypeName, topicNamespaceName); + } + + // write platforms for elements//element nodes (member list and overload topics; not root or namespace) + if ((topicdataGroup == "list" && topicdataSubgroup != "DerivedTypeList") && apiGroup != "root" && versionFilters.Count > 0) + { + if (topicdataSubgroup != "overload") + topicTypeName = (string)referenceNode.Evaluate(listTypeNameExpression); + + XPathNodeIterator elementNodes = referenceNode.Select(listTopicElementNodesExpression); + foreach (XPathNavigator elementNode in elementNodes) + { + string elementId = (string)elementNode.Evaluate(elementIdExpression); + WriteApiPlatforms(elementNode, "member", elementId, topicTypeName, topicNamespaceName); + } + } + } + + private void WriteApiPlatforms(XPathNavigator referenceNode, string apiGroup, string key, string topicTypeName, string topicNamespaceName) + { + XPathNodeIterator versionNodes = referenceNode.Select(versionNodesExpression); + List<string> supportedPlatforms = new List<string>(); + XmlWriter platformsWriter = referenceNode.AppendChild(); + + foreach (string platformId in versionFilters.Keys) + { + Dictionary<string, VersionFilter> filters = versionFilters[platformId]; + bool included = false; + foreach (XPathNavigator versionNode in versionNodes) + { + string versionId = versionNode.GetAttribute("name", string.Empty); + VersionFilter filter; + if (filters.TryGetValue(versionId, out filter)) + { + switch (apiGroup) + { + case "type": + included = filter.IsIncludedType(referenceNode, key, topicNamespaceName); + break; + case "member": + included = filter.IsIncludedMember(referenceNode, key, topicTypeName, topicNamespaceName); + break; + } + } + if (included) + break; + } + + if (included) + supportedPlatforms.Add(platformId); + } + platformsWriter.WriteStartElement("platforms"); + foreach (string platformId in supportedPlatforms) + { + platformsWriter.WriteElementString("platform", platformId); + } + platformsWriter.WriteEndElement(); + platformsWriter.Close(); + } + + } + + public abstract class InclusionFilter + { + public InclusionFilter(string file) + { + sourceFiles.Add(file); + } + + protected List<string> sourceFiles = new List<string>(); + + protected static XPathExpression apiNameExpression = XPathExpression.Compile("string(apidata/@name)"); + protected static XPathExpression apiParameterNodesExpression = XPathExpression.Compile("parameters/parameter"); + protected static XPathExpression apiParameterTypeNameExpression = XPathExpression.Compile("string(.//type/@api)"); + protected static XPathExpression apiParameterTemplateNameExpression = XPathExpression.Compile("string(.//template/@name)"); + } + + public class VersionFilter : InclusionFilter + { + + public VersionFilter(XmlReader platformReader, string id, string file) + : base(file) + { + // platform/@version can only list included framework versions; excluding versions is not supported + included = true; + versionId = id; + AddPlatformNode(platformReader, file); + } + + public void AddPlatformNode(XmlReader platformReader, string file) + { + XmlReader subtree = platformReader.ReadSubtree(); + while (subtree.Read()) + { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "namespace")) + { + string namespaceName = subtree.GetAttribute("name"); + + NamespaceFilter namespaceFilter; + if (!namespaceFilters.TryGetValue(namespaceName, out namespaceFilter)) + { + namespaceFilter = new NamespaceFilter(subtree, file); + } + else + { + // if the version already has a filter for this namespace, add the data from the current namespace node + // unless the namespace node has a different @include value, in which case log a warning + string nsIncludeAttr = subtree.GetAttribute("include"); + bool nsIncluded = Convert.ToBoolean(string.IsNullOrEmpty(nsIncludeAttr) ? "true" : nsIncludeAttr); + if (nsIncluded != namespaceFilter.Included) + { + // write warning message about conflicting filters + // ISSUE: how to invoke WriteMessage from here + Console.Write(""); + } + else + { + namespaceFilter.AddNamespaceNode(subtree, file); + } + + } + namespaceFilters.Remove(namespaceName); + namespaceFilters.Add(namespaceName, namespaceFilter); + } + } + subtree.Close(); + } + + private string versionId; + + // platform/@version can only list included framework versions; excluding versions is not supported + private bool included; + + private Dictionary<string, NamespaceFilter> namespaceFilters = new Dictionary<string, NamespaceFilter>(); + + public string VersionId + { + get + { + return (versionId); + } + } + + public Dictionary<string, NamespaceFilter> NamespaceFilters + { + get + { + return (namespaceFilters); + } + } + + /// <summary> + /// If we get here, we know that the platform supports this version, and the api is included in this version. + /// So returns true unless the type or its namespace are explicitly excluded by this version filter. + /// </summary> + /// <param name="referenceNode">The type's reflection data.</param> + /// <returns></returns> + public bool IsIncludedType(XPathNavigator referenceNode, string key, string topicNamespaceName) + { + // if we have a filter for the topic's namespace, check it + NamespaceFilter namespaceFilter; + if (namespaceFilters.TryGetValue(topicNamespaceName, out namespaceFilter)) + { + return namespaceFilter.IsIncludedType(referenceNode, key); + } + return included; + } + + public bool IsIncludedMember(XPathNavigator referenceNode, string key, string topicTypeName, string topicNamespaceName) + { + // if we have a filter for the topic's namespace, check it + NamespaceFilter namespaceFilter; + if (namespaceFilters.TryGetValue(topicNamespaceName, out namespaceFilter)) + { + return namespaceFilter.IsIncludedMember(referenceNode, key, topicTypeName); + } + return included; + } + } + + + public class NamespaceFilter : InclusionFilter + { + + public NamespaceFilter(XmlReader namespaceReader, string file) + : base(file) + { + name = namespaceReader.GetAttribute("name"); + string includeAttr = namespaceReader.GetAttribute("include"); + included = Convert.ToBoolean(string.IsNullOrEmpty(includeAttr) ? "true" : includeAttr); + AddNamespaceNode(namespaceReader, file); + } + + public void AddNamespaceNode(XmlReader namespaceReader, string file) + { + XmlReader subtree = namespaceReader.ReadSubtree(); + while (subtree.Read()) + { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "type")) + { + string typeName = subtree.GetAttribute("name"); + + TypeFilter typeFilter; + if (!typeFilters.TryGetValue(typeName, out typeFilter)) + { + typeFilter = new TypeFilter(subtree, file); + } + else + { + // if the namespace already has a filter for this type, add the data from the current type node + // unless the type node has a different @include value, in which case log a warning + string typeIncludeAttr = subtree.GetAttribute("include"); + bool typeIncluded = Convert.ToBoolean(string.IsNullOrEmpty(typeIncludeAttr) ? "true" : typeIncludeAttr); + if (typeIncluded != typeFilter.Included) + { + // write warning message about conflicting filters + // ISSUE: how to invoke WriteMessage from here + Console.Write(""); + } + else + { + typeFilter.AddTypeNode(subtree, file); + } + } + typeFilters.Remove(typeName); + typeFilters.Add(typeName, typeFilter); + } + } + subtree.Close(); + } + + private string name; + + private bool included; + public bool Included + { + get + { + return (included); + } + } + + private Dictionary<string, TypeFilter> typeFilters = new Dictionary<string, TypeFilter>(); + public Dictionary<string, TypeFilter> TypeFilters + { + get + { + return (typeFilters); + } + } + + public bool IsIncludedType(XPathNavigator referenceNode, string key) + { + // get the type's name + string typeName = (string)referenceNode.Evaluate(apiNameExpression); + + // if we have a filter for that type, check it + TypeFilter typeFilter; + if (typeFilters.TryGetValue(typeName, out typeFilter)) + { + return typeFilter.Included; + } + return included; + } + + public bool IsIncludedMember(XPathNavigator referenceNode, string key, string topicTypeName) + { + // if we have a filter for the type, check it + TypeFilter typeFilter; + if (typeFilters.TryGetValue(topicTypeName, out typeFilter)) + { + return typeFilter.IsIncludedMember(referenceNode, key); + } + return included; + } + } + + public class TypeFilter : InclusionFilter + { + + public TypeFilter(XmlReader typeReader, string file) + : base(file) + { + name = typeReader.GetAttribute("name"); + string includeAttr = typeReader.GetAttribute("include"); + included = Convert.ToBoolean(string.IsNullOrEmpty(includeAttr) ? "true" : includeAttr); + AddTypeNode(typeReader, file); + } + + public void AddTypeNode(XmlReader typeReader, string file) + { + XmlReader subtree = typeReader.ReadSubtree(); + while (subtree.Read()) + { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "member")) + { + string memberName = subtree.GetAttribute("name"); + + MemberFilter memberFilter; + if (!memberFilters.TryGetValue(memberName, out memberFilter)) + { + memberFilter = new MemberFilter(subtree, file); + } + else + { + // if the type already has a filter for this member, add the data from the current member node + // unless the member node has a different @include value, in which case log a warning + string memberIncludeAttr = subtree.GetAttribute("include"); + bool memberIncluded = Convert.ToBoolean(string.IsNullOrEmpty(memberIncludeAttr) ? "true" : memberIncludeAttr); + if (memberIncluded != memberFilter.Included) + { + // write warning message about conflicting filters + // ISSUE: how to invoke WriteMessage from here + Console.Write(""); + } + else + { + memberFilter.AddMemberNode(subtree, file); + } + } + memberFilters.Remove(memberName); + memberFilters.Add(memberName, memberFilter); + } + } + subtree.Close(); + } + + private string name; + + private bool included; + public bool Included + { + get + { + return (included); + } + } + + private Dictionary<string, MemberFilter> memberFilters = new Dictionary<string, MemberFilter>(); + public Dictionary<string, MemberFilter> MemberFilters + { + get + { + return (memberFilters); + } + } + + public bool IsIncludedMember(XPathNavigator referenceNode, string key) + { + // get the member's name + string memberName = (string)referenceNode.Evaluate(apiNameExpression); + + // if we have a filter for that member, check it + MemberFilter memberFilter; + if (memberFilters.TryGetValue(memberName, out memberFilter)) + { + return memberFilter.IsIncludedMember(referenceNode, key); + } + return included; + } + + } + + public class MemberFilter : InclusionFilter + { + + public MemberFilter(XmlReader memberReader, string file) + : base(file) + { + name = memberReader.GetAttribute("name"); + + string includeAttr = memberReader.GetAttribute("include"); + included = Convert.ToBoolean(string.IsNullOrEmpty(includeAttr) ? "true" : includeAttr); + AddMemberNode(memberReader, file); + } + + public void AddMemberNode(XmlReader memberReader, string file) + { + XmlReader subtree = memberReader.ReadSubtree(); + while (subtree.Read()) + { + if ((subtree.NodeType == XmlNodeType.Element) && (subtree.Name == "overload")) + { + string overloadId = subtree.GetAttribute("api"); + string paramTypes = subtree.GetAttribute("types"); + string paramNames = subtree.GetAttribute("names"); + string overloadIncludeAttr = subtree.GetAttribute("include"); + bool overloadIncluded = Convert.ToBoolean(string.IsNullOrEmpty(overloadIncludeAttr) ? "true" : overloadIncludeAttr); + // check for existing overload filters that identify the same overload + bool alreadyFiltered = false; + foreach (OverloadFilter overloadFilter in overloadFilters) + { + if (!string.IsNullOrEmpty(overloadId) && overloadId == overloadFilter.OverloadId) + alreadyFiltered = true; + if (!string.IsNullOrEmpty(paramTypes) && paramTypes == overloadFilter.ParamTypes) + alreadyFiltered = true; + if (!string.IsNullOrEmpty(paramNames) && paramNames == overloadFilter.ParamNames) + alreadyFiltered = true; + if (alreadyFiltered && (overloadIncluded != overloadFilter.Included)) + { + // write warning message about conflicting filters + // ISSUE: how to invoke WriteMessage from here + Console.Write(""); + } + } + if (!alreadyFiltered) + { + OverloadFilter overloadFilter = new OverloadFilter(subtree, file); + overloadFilters.Add(overloadFilter); + } + } + } + subtree.Close(); + } + + private string name; + + private bool included; + public bool Included + { + get + { + return (included); + } + } + + private List<OverloadFilter> overloadFilters = new List<OverloadFilter>(); + + public bool IsIncludedMember(XPathNavigator referenceNode, string key) + { + if (overloadFilters.Count == 0) + return included; + + // get the member's paramTypes string + + + // get the member's paramNames string + XPathNodeIterator parameterNodes = referenceNode.Select(apiParameterNodesExpression); + + StringBuilder paramNames = new StringBuilder(); + StringBuilder paramTypes = new StringBuilder(); + int i = 0; + foreach (XPathNavigator parameterNode in parameterNodes) + { + i++; + paramNames.Append(parameterNode.GetAttribute("name", string.Empty)); + if (i < parameterNodes.Count) + paramNames.Append(","); + + // BUGBUG: code here and in the psx conversion transform is a quick hack; make it better + string arrayOf = (parameterNode.SelectSingleNode("arrayOf") == null) ? "" : "[]"; + string typeName = (string)parameterNode.Evaluate(apiParameterTypeNameExpression); + if (string.IsNullOrEmpty(typeName)) + typeName = (string)parameterNode.Evaluate(apiParameterTemplateNameExpression); + + int basenameStart = typeName.LastIndexOf('.') + 1; + if (basenameStart > 0 && basenameStart < typeName.Length) + typeName = typeName.Substring(basenameStart); + + paramTypes.Append(typeName + arrayOf); + if (i < parameterNodes.Count) + paramTypes.Append(","); + } + + foreach (OverloadFilter overloadFilter in overloadFilters) + { + if (key == overloadFilter.OverloadId) + return overloadFilter.Included; + if (paramNames.ToString() == overloadFilter.ParamNames) + return overloadFilter.Included; + if (paramTypes.ToString() == overloadFilter.ParamTypes) + return overloadFilter.Included; + } + + return included; + } + + } + + public class OverloadFilter : InclusionFilter + { + + public OverloadFilter(XmlReader overloadReader, string file) + : base(file) + { + name = overloadReader.GetAttribute("name"); + string includeAttr = overloadReader.GetAttribute("include"); + included = Convert.ToBoolean(string.IsNullOrEmpty(includeAttr) ? "true" : includeAttr); + overloadId = overloadReader.GetAttribute("api"); + paramTypes = overloadReader.GetAttribute("types"); + paramNames = overloadReader.GetAttribute("names"); + } + + private string name; + + private bool included; + public bool Included + { + get + { + return (included); + } + } + + private string paramTypes; + public string ParamTypes + { + get + { + return (paramTypes); + } + } + + private string paramNames; + public string ParamNames + { + get + { + return (paramNames); + } + } + private string overloadId; + public string OverloadId + { + get + { + return (overloadId); + } + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/AssemblyInfo.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1701d1d --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BuildComponents")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("BuildComponents")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("146f3298-2c7f-4588-88f2-520438232a23")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("2.4.10522.00")] +[assembly: AssemblyFileVersion("2.4.10522.00")] diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.Designer.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.Designer.cs new file mode 100644 index 0000000..930f32a --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace BuildComponents.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.settings b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.settings new file mode 100644 index 0000000..8e615f2 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.settings @@ -0,0 +1,5 @@ +<?xml version='1.0' encoding='utf-8'?> +<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)"> + <Profiles /> + <Settings /> +</SettingsFile>
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/References.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/References.cs new file mode 100644 index 0000000..ae11ad5 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/References.cs @@ -0,0 +1,345 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.XPath; + +namespace BuildComponents { + + public interface ISimpleReference { + + string Id { get; } + + } + + public abstract partial class Reference { } + + // Namespace + + public partial class NamespaceReference : Reference, ISimpleReference { + + internal NamespaceReference (string id) { + this.namespaceId = id; + } + + private string namespaceId; + + public string Id { + get { + return (namespaceId); + } + } + + } + + // Type + + public abstract partial class TypeReference : Reference { } + + public partial class SimpleTypeReference : TypeReference { + + internal SimpleTypeReference (string id) { + this.typeId = id; + } + + private string typeId; + + public string Id { + get { + return (typeId); + } + } + + + } + + public partial class ReferenceTypeReference : TypeReference { + + private TypeReference referedToType; + + public TypeReference ReferedToType { + get { + return (referedToType); + } + } + + internal ReferenceTypeReference (TypeReference referedToType) { + if (referedToType == null) throw new ArgumentNullException("referedToType"); + this.referedToType = referedToType; + } + } + + public partial class PointerTypeReference : TypeReference { + + internal PointerTypeReference (TypeReference pointedToType) { + if (pointedToType == null) throw new ArgumentNullException("pointedToType"); + this.pointedToType = pointedToType; + } + + private TypeReference pointedToType; + + public TypeReference PointedToType { + get { + return (pointedToType); + } + } + + } + + public partial class ArrayTypeReference : TypeReference { + + internal ArrayTypeReference (TypeReference elementType, int rank) { + if (elementType == null) throw new ArgumentNullException("elementType"); + if (rank <= 0) throw new ArgumentOutOfRangeException("rank"); + this.elementType = elementType; + this.rank = rank; + } + + private int rank; + + private TypeReference elementType; + + public int Rank { + get { + return (rank); + } + } + + public TypeReference ElementType { + get { + return (elementType); + } + } + + } + + public class TemplateTypeReference : TypeReference { + + internal TemplateTypeReference (string templateId, int position) { + if (template == null) throw new ArgumentNullException("template"); + if (position < 0) throw new ArgumentOutOfRangeException("position"); + this.template = null; // fix this: create a reference + this.position = position; + } + + internal TemplateTypeReference (ISimpleReference template, int position) { + if (template == null) throw new ArgumentNullException("template"); + if (position < 0) throw new ArgumentOutOfRangeException("position"); + this.template = template; + this.position = position; + } + + private ISimpleReference template; + + private int position; + } + + public abstract partial class MemberReference : Reference {} + + public partial class SimpleMemberReference : MemberReference, ISimpleReference { + + internal SimpleMemberReference (string id) { + if (id == null) throw new ArgumentNullException("id"); + this.memberId = id; + } + + private string memberId; + + public string Id { + get { + return(memberId); + } + } + + + + } + + + + + // ***** XML PARSING **** + + public partial class Reference { + + public static Reference Create (XmlReader element) { + if (element == null) throw new ArgumentNullException("element"); + switch (element.LocalName) { + case "namespace": + return (NamespaceReference.Create(element)); + break; + case "member": + return (MemberReference.Create(element)); + break; + default: + return (TypeReference.Create(element)); + break; + } + return (null); + } + + public static Reference Create (XPathNavigator node) { + if (node == null) throw new ArgumentNullException("node"); + if (node.NodeType == XPathNodeType.Element) { + string tag = node.LocalName; + if (tag == "namespace") return (NamespaceReference.Create(node)); + //if (tag == "member") return (MemberReference.Create(node)); + return (TypeReference.Create(node)); + } else { + return (null); + } + } + + protected static XPathExpression referenceApiExpression = XPathExpression.Compile("string(@api)"); + + + } + + public partial class NamespaceReference { + + public static new NamespaceReference Create (XmlReader space) { + if (space == null) throw new ArgumentNullException("space"); + + string api = space.GetAttribute("api"); + + return(new NamespaceReference(api)); + + } + + public static new NamespaceReference Create (XPathNavigator space) { + if (space == null) throw new ArgumentNullException("space"); + string api = (string) space.Evaluate(referenceApiExpression); + return(new NamespaceReference(api)); + } + + } + + public partial class TypeReference { + + public static new TypeReference Create (XmlReader element) { + if (element == null) throw new ArgumentNullException("element"); + switch (element.LocalName) { + case "type": + // also handle specialization! + return(SimpleTypeReference.Create(element)); + break; + case "referenceTo": + return(ReferenceTypeReference.Create(element)); + break; + case "pointerTo": + return(PointerTypeReference.Create(element)); + break; + case "arrayOf": + return(ArrayTypeReference.Create(element)); + break; + + } + return (null); + } + + public static new TypeReference Create (XPathNavigator element) { + if (element == null) throw new ArgumentNullException("reference"); + string tag = element.LocalName; + if (tag == "type") { + bool isSpecialized = (bool)element.Evaluate("boolean(.//specialization)"); + if (isSpecialized) { + // deal with specialization! + // return (CreateSpecializedTypeReference(element)); + return (SimpleTypeReference.Create(element)); + } else { + return (SimpleTypeReference.Create(element)); + } + } else if (tag == "arrayOf") { + string rankValue = element.GetAttribute("rank", String.Empty); + XPathNavigator elementNode = element.SelectSingleNode("*[1]"); + return (new ArrayTypeReference(TypeReference.Create(elementNode), Convert.ToInt32(rankValue))); + } else if (tag == "referenceTo") { + XPathNavigator referedToNode = element.SelectSingleNode("*[1]"); + return (new ReferenceTypeReference(TypeReference.Create(referedToNode))); + } else if (tag == "pointerTo") { + XPathNavigator pointedToNode = element.SelectSingleNode("*[1]"); + return (new PointerTypeReference(TypeReference.Create(pointedToNode))); + } else if (tag == "template") { + string nameValue = element.GetAttribute("name", String.Empty); + string indexValue = element.GetAttribute("index", String.Empty); + string apiValue = element.GetAttribute("api", String.Empty); + if ((!String.IsNullOrEmpty(apiValue)) && (!String.IsNullOrEmpty(indexValue))) { + return (new TemplateTypeReference(apiValue, Convert.ToInt32(indexValue))); + // return (new IndexedTemplateTypeReference(apiValue, Convert.ToInt32(indexValue))); + } else { + throw new InvalidOperationException(); + // return (new NamedTemplateTypeReference(nameValue)); + } + } + + throw new InvalidOperationException(String.Format("INVALID '{0}'", tag)); + } + + } + + public partial class SimpleTypeReference { + + public static new SimpleTypeReference Create (XmlReader type) { + if (type == null) throw new ArgumentNullException("type"); + string api = type.GetAttribute("api"); + return (new SimpleTypeReference(api)); + } + + public static new SimpleTypeReference Create (XPathNavigator type) { + if (type == null) throw new ArgumentNullException("type"); + string api = (string)type.Evaluate(referenceApiExpression); + return(new SimpleTypeReference(api)); + } + + } + + public partial class ReferenceTypeReference { + + public static new ReferenceTypeReference Create (XmlReader referenceTo) { + if (referenceTo == null) throw new ArgumentException("refernceTo"); + referenceTo.Read(); + TypeReference referedToType = TypeReference.Create(referenceTo); + return (new ReferenceTypeReference(referedToType)); + } + + public static new ReferenceTypeReference Create (XPathNavigator referenceTo) { + XPathNavigator referedToNode = referenceTo.SelectSingleNode("*[1]"); + TypeReference referedToType = TypeReference.Create(referedToNode); + return (new ReferenceTypeReference(referedToType)); + } + } + + public partial class PointerTypeReference { + + public static new PointerTypeReference Create (XmlReader pointerTo) { + if (pointerTo == null) throw new ArgumentException("pointerTo"); + pointerTo.Read(); + TypeReference pointedToType = TypeReference.Create(pointerTo); + return (new PointerTypeReference(pointedToType)); + } + + public static new PointerTypeReference Create (XPathNavigator pointerTo) { + XPathNavigator pointedToNode = pointerTo.SelectSingleNode("*[1]"); + TypeReference pointedToType = TypeReference.Create(pointedToNode); + return (new PointerTypeReference(pointedToType)); + } + } + + public partial class ArrayTypeReference { + + public static new ArrayTypeReference Create (XmlReader arrayOf) { + if (arrayOf == null) throw new ArgumentNullException("arrayOf"); + + int rank = 1; + string rankText = arrayOf.GetAttribute("rank"); + if (!String.IsNullOrEmpty(rankText)) rank = Convert.ToInt32(rankText); + + arrayOf.Read(); + TypeReference elementType = TypeReference.Create(arrayOf); + + return (new ArrayTypeReference(elementType, rank)); + } + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveArtLinksComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveArtLinksComponent.cs new file mode 100644 index 0000000..3f13a7b --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveArtLinksComponent.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class ResolveArtLinksComponent : BuildComponent { + + public ResolveArtLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNodeIterator targets_nodes = configuration.Select("targets"); + foreach (XPathNavigator targets_node in targets_nodes) { + + string input = targets_node.GetAttribute("input", String.Empty); + if (String.IsNullOrEmpty(input)) WriteMessage(MessageLevel.Error, "Each targets element must have an input attribute specifying a directory containing art files."); + input = Environment.ExpandEnvironmentVariables(input); + if (!Directory.Exists(input)) WriteMessage(MessageLevel.Error, String.Format("The art input directory '{0}' does not exist.", input)); + + string baseOutputPath = targets_node.GetAttribute("baseOutput", String.Empty); + if (!String.IsNullOrEmpty(baseOutputPath)) { + baseOutputPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(baseOutputPath)); + } + + string outputPath_value = targets_node.GetAttribute("outputPath", string.Empty); + if (string.IsNullOrEmpty(outputPath_value)) WriteMessage(MessageLevel.Error, "Each targets element must have an output attribute specifying a directory in which to place referenced art files."); + XPathExpression output_XPath = XPathExpression.Compile(outputPath_value); + + string linkValue = targets_node.GetAttribute("link", String.Empty); + if (String.IsNullOrEmpty(linkValue)) linkValue = "../art"; + //linkValue = Environment.ExpandEnvironmentVariables(linkValue); + + string map = targets_node.GetAttribute("map", String.Empty); + if (String.IsNullOrEmpty(map)) WriteMessage(MessageLevel.Error, "Each targets element must have a map attribute specifying a file that maps art ids to files in the input directory."); + map = Environment.ExpandEnvironmentVariables(map); + if (!File.Exists(map)) WriteMessage(MessageLevel.Error, String.Format("The art map file '{0}' does not exist.", map)); + + string format = targets_node.GetAttribute("format", String.Empty); + XPathExpression format_xpath = String.IsNullOrEmpty(format) ? null : XPathExpression.Compile(format); + + string relative_to = targets_node.GetAttribute("relative-to", String.Empty); + XPathExpression relative_to_xpath = String.IsNullOrEmpty(relative_to) ? null : XPathExpression.Compile(relative_to); + + AddTargets(map, input, baseOutputPath, output_XPath, linkValue, format_xpath, relative_to_xpath); + + } + + WriteMessage(MessageLevel.Info, String.Format("Indexed {0} art targets.", targets.Count)); + + } + + private void AddTargets (string map, string input, string baseOutputPath, XPathExpression outputXPath, string link, XPathExpression formatXPath, XPathExpression relativeToXPath) { + + XPathDocument document = new XPathDocument(map); + + XPathNodeIterator items = document.CreateNavigator().Select("/*/item"); + foreach (XPathNavigator item in items) { + + string id = (string) item.Evaluate(artIdExpression); + string file = (string) item.Evaluate(artFileExpression); + string text = (string) item.Evaluate(artTextExpression); + + id = id.ToLower(); + string name = Path.GetFileName(file); + + ArtTarget target = new ArtTarget(); + target.Id = id; + target.InputPath = Path.Combine(input, file); + target.baseOutputPath = baseOutputPath; + target.OutputXPath = outputXPath; + + if (string.IsNullOrEmpty(name)) target.LinkPath = link; + else target.LinkPath = string.Format("{0}/{1}", link, name); + + target.Text = text; + target.Name = name; + target.FormatXPath = formatXPath; + target.RelativeToXPath = relativeToXPath; + + targets[id] = target; + // targets.Add(id, target); + } + + } + + private XPathExpression artIdExpression = XPathExpression.Compile("string(@id)"); + private XPathExpression artFileExpression = XPathExpression.Compile("string(image/@file)"); + private XPathExpression artTextExpression = XPathExpression.Compile("string(image/altText)"); + + private Dictionary<string,ArtTarget> targets = new Dictionary<string,ArtTarget>(); + + public override void Apply (XmlDocument document, string id) { + + XPathNodeIterator artLinkIterator = document.CreateNavigator().Select(artLinkExpression); + XPathNavigator[] artLinks = BuildComponentUtilities.ConvertNodeIteratorToArray(artLinkIterator); + foreach (XPathNavigator artLink in artLinks) { + + string name = artLink.GetAttribute("target", String.Empty).ToLower(); + + if (targets.ContainsKey(name)) { + ArtTarget target = targets[name]; + + // evaluate the path + string path = document.CreateNavigator().Evaluate(target.OutputXPath).ToString(); + + if (target.baseOutputPath != null) path = Path.Combine(target.baseOutputPath, path); + string outputPath = Path.Combine(path, target.Name); + + string targetDirectory = Path.GetDirectoryName(outputPath); + + if (!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory); + + if (File.Exists(target.InputPath)) { + + if (File.Exists(outputPath)) { + File.SetAttributes(outputPath, FileAttributes.Normal); + } + + File.Copy(target.InputPath, outputPath, true); + } else { + WriteMessage(MessageLevel.Warn, String.Format("The file '{0}' for the art target '{1}' was not found.", target.InputPath, name)); + } + + // Get the relative art path for HXF generation. + int index = target.LinkPath.IndexOf('/'); + string artPath = target.LinkPath.Substring(index+1, target.LinkPath.Length - (index+1)); + + FileCreatedEventArgs fe = new FileCreatedEventArgs(artPath, Path.GetDirectoryName(path)); + OnComponentEvent(fe); + + XmlWriter writer = artLink.InsertAfter(); + + writer.WriteStartElement("img"); + if (!String.IsNullOrEmpty(target.Text)) writer.WriteAttributeString("alt", target.Text); + + if (target.FormatXPath == null) { + writer.WriteAttributeString("src", target.LinkPath); + } + else { + // WebDocs way, which uses the 'format' xpath expression to calculate the target path + // and then makes it relative to the current page if the 'relative-to' attribute is + // used. + string src = BuildComponentUtilities.EvalXPathExpr(document, target.FormatXPath, "key", Path.GetFileName(outputPath)); + if (target.RelativeToXPath != null) + src = BuildComponentUtilities.GetRelativePath(src, BuildComponentUtilities.EvalXPathExpr(document, target.RelativeToXPath, "key", id)); + writer.WriteAttributeString("src", src); + } + + writer.WriteEndElement(); + + writer.Close(); + + artLink.DeleteSelf(); + + } else { + WriteMessage(MessageLevel.Warn, String.Format("Unknown art target '{0}'", name)); + } + + } + + } + + private static XPathExpression artLinkExpression = XPathExpression.Compile("//artLink"); + + } + + internal class ArtTarget { + + public string Id; + + public string InputPath; + + public string baseOutputPath; + + public XPathExpression OutputXPath; + + public string LinkPath; + + public string Text; + + public string Name; + + public XPathExpression FormatXPath; + + public XPathExpression RelativeToXPath; + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveConceptualLinksComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveConceptualLinksComponent.cs new file mode 100644 index 0000000..02ed150 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveConceptualLinksComponent.cs @@ -0,0 +1,445 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using System.Text.RegularExpressions; +using System.Web; + + +namespace Microsoft.Ddue.Tools { + + public class ResolveConceptualLinksComponent : BuildComponent { + + // Instantiation logic + + public ResolveConceptualLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + string showBrokenLinkTextValue = configuration.GetAttribute("showBrokenLinkText", String.Empty); + if (!String.IsNullOrEmpty(showBrokenLinkTextValue)) showBrokenLinkText = Convert.ToBoolean(showBrokenLinkTextValue); + + XPathNodeIterator targetsNodes = configuration.Select("targets"); + foreach (XPathNavigator targetsNode in targetsNodes) { + + // the base directory containing target; required + string baseValue = targetsNode.GetAttribute("base", String.Empty); + if (String.IsNullOrEmpty(baseValue)) WriteMessage(MessageLevel.Error, "Every targets element must have a base attribute that specifies the path to a directory of target metadata files."); + baseValue = Environment.ExpandEnvironmentVariables(baseValue); + if (!Directory.Exists(baseValue)) WriteMessage(MessageLevel.Error, String.Format("The specified target metadata directory '{0}' does not exist.", baseValue)); + + // an xpath expression to construct a file name + // (not currently used; pattern is hard-coded to $target.cmp.xml + string filesValue = targetsNode.GetAttribute("files", String.Empty); + + // an xpath expression to construct a url + string urlValue = targetsNode.GetAttribute("url", String.Empty); + XPathExpression urlExpression; + if (String.IsNullOrEmpty(urlValue)) { + urlExpression = XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')"); + } else { + urlExpression = CompileXPathExpression(urlValue); + } + + // an xpath expression to construct link text + string textValue = targetsNode.GetAttribute("text", String.Empty); + XPathExpression textExpression; + if (String.IsNullOrEmpty(textValue)) { + textExpression = XPathExpression.Compile("string(/metadata/topic/title)"); + } else { + textExpression = CompileXPathExpression(textValue); + } + + // the type of link to create to targets found in the directory; required + string typeValue = targetsNode.GetAttribute("type", String.Empty); + if (String.IsNullOrEmpty(typeValue)) WriteMessage(MessageLevel.Error, "Every targets element must have a type attribute that specifies what kind of link to create to targets found in that directory."); + + // convert the link type to an enumeration member + LinkType type = LinkType.None; + try { + type = (LinkType) Enum.Parse(typeof(LinkType), typeValue, true); + } catch (ArgumentException) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid link type.", typeValue)); + } + + // we have all the required information; create a TargetDirectory and add it to our collection + TargetDirectory targetDirectory = new TargetDirectory(baseValue, urlExpression, textExpression, type); + targetDirectories.Add(targetDirectory); + + } + + WriteMessage(MessageLevel.Info, String.Format("Collected {0} targets directories.", targetDirectories.Count)); + + } + + private XPathExpression CompileXPathExpression (string xpath) { + XPathExpression expression = null; + try { + expression = XPathExpression.Compile(xpath); + } catch (ArgumentException e) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message)); + } catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message)); + } + return (expression); + } + + // Conceptual link resolution logic + + public override void Apply (XmlDocument document, string key) { + ResolveConceptualLinks(document, key); + } + + private bool showBrokenLinkText = false; + + private string BrokenLinkDisplayText (string target, string text) { + if (showBrokenLinkText) { + return(String.Format("{0}", text)); + } else { + return(String.Format("[{0}]", target)); + } + } + + private TargetDirectoryCollection targetDirectories = new TargetDirectoryCollection(); + + private static XPathExpression conceptualLinks = XPathExpression.Compile("//conceptualLink"); + + private static Regex validGuid = new Regex(@"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", RegexOptions.Compiled); + + private void ResolveConceptualLinks (XmlDocument document, string key) { + + // find links + XPathNodeIterator linkIterator = document.CreateNavigator().Select(conceptualLinks); + + // copy them to an array, because enumerating through an XPathNodeIterator + // fails when the nodes in it are altered + XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(linkIterator); + + foreach (XPathNavigator linkNode in linkNodes) { + + ConceptualLinkInfo link = ConceptualLinkInfo.Create(linkNode); + + // determine url, text, and link type + string url = null; + string text = null; + LinkType type = LinkType.None; + bool isValidLink = validGuid.IsMatch(link.Target); + if (isValidLink) { + // a valid link; try to fetch target info + TargetInfo target = GetTargetInfoFromCache(link.Target.ToLower()); + if (target == null) { + // no target found; issue warning, set link style to none, and text to in-source fall-back + //type = LinkType.None; + type = LinkType.Index; + text = BrokenLinkDisplayText(link.Target, link.Text); + WriteMessage(MessageLevel.Warn, String.Format("Unknown conceptual link target '{0}'.", link.Target)); + } else { + // found target; get url, text, and type from stored info + url = target.Url; + text = target.Text; + type = target.Type; + } + } else { + // not a valid link; issue warning, set link style to none, and text to invalid target + //type = LinkType.None; + type = LinkType.Index; + text = BrokenLinkDisplayText(link.Target, link.Text); + WriteMessage(MessageLevel.Warn, String.Format("Invalid conceptual link target '{0}'.", link.Target)); + } + + // write opening link tag and target info + XmlWriter writer = linkNode.InsertAfter(); + switch (type) { + case LinkType.None: + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nolink"); + break; + case LinkType.Local: + writer.WriteStartElement("a"); + writer.WriteAttributeString("href", url); + break; + case LinkType.Index: + writer.WriteStartElement("mshelp", "link", "http://msdn.microsoft.com/mshelp"); + writer.WriteAttributeString("keywords", link.Target.ToLower()); + writer.WriteAttributeString("tabindex", "0"); + break; + } + + // write the link text + writer.WriteString(text); + + // write the closing link tag + writer.WriteEndElement(); + writer.Close(); + + // delete the original tag + linkNode.DeleteSelf(); + } + } + + + + + // a simple caching system for target names + + private TargetInfo GetTargetInfoFromCache (string target) { + + TargetInfo info; + if (!cache.TryGetValue(target, out info)) { + info = targetDirectories.GetTargetInfo(target + ".cmp.xml"); + + if (cache.Count >= cacheSize) cache.Clear(); + + cache.Add(target, info); + } + + return(info); + + } + + private static int cacheSize = 1000; + + private Dictionary<string,TargetInfo> cache = new Dictionary<string,TargetInfo>(cacheSize); + + //private CustomContext context = new CustomContext(); + + } + + // different types of links + + internal enum LinkType { + None, // not active + Local, // a href + Index // mshelp:link keyword + //Regex // regular expression with match/replace + } + + // a representation of a targets directory, along with all the assoicated expressions used to + // find target metadat files in it, and extract urls and link text from those files + + internal class TargetDirectory { + + private string directory; + + private XPathExpression fileExpression = XPathExpression.Compile("concat($target,'.cmp.htm')"); + + private XPathExpression urlExpression = XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')"); + + private XPathExpression textExpression = XPathExpression.Compile("string(/metadata/topic/title)"); + + private LinkType type; + + public string Directory { + get { + return (directory); + } + } + + public XPathExpression UrlExpression { + get { + return (urlExpression); + } + } + + public XPathExpression TextExpression { + get { + return (textExpression); + } + } + + + public LinkType LinkType { + get { + return (type); + } + } + + public TargetDirectory (string directory, LinkType type) { + if (directory == null) throw new ArgumentNullException("directory"); + this.directory = directory; + this.type = type; + } + + public TargetDirectory (string directory, XPathExpression urlExpression, XPathExpression textExpression, LinkType type) { + if (directory == null) throw new ArgumentNullException("directory"); + if (urlExpression == null) throw new ArgumentNullException("urlExpression"); + if (textExpression == null) throw new ArgumentNullException("textExpression"); + this.directory = directory; + this.urlExpression = urlExpression; + this.textExpression = textExpression; + this.type = type; + } + + private XPathDocument GetDocument (string file) { + string path = Path.Combine(directory, file); + if (File.Exists(path)) { + XPathDocument document = new XPathDocument(path); + return (document); + } else { + return (null); + } + } + + public TargetInfo GetTargetInfo (string file) { + XPathDocument document = GetDocument(file); + if (document == null) { + return(null); + } else { + XPathNavigator context = document.CreateNavigator(); + + string url = context.Evaluate(urlExpression).ToString(); + string text = context.Evaluate(textExpression).ToString(); + TargetInfo info = new TargetInfo(url, text, type); + return(info); + } + } + + public TargetInfo GetTargetInfo (XPathNavigator linkNode, CustomContext context) { + + // compute the metadata file name to open + XPathExpression localFileExpression = fileExpression.Clone(); + localFileExpression.SetContext(context); + string file = linkNode.Evaluate(localFileExpression).ToString(); + if (String.IsNullOrEmpty(file)) return (null); + + // load the metadata file + XPathDocument metadataDocument = GetDocument(file); + if (metadataDocument == null) return (null); + + // querry the metadata file for the target url and link text + XPathNavigator metadataNode = metadataDocument.CreateNavigator(); + XPathExpression localUrlExpression = urlExpression.Clone(); + localUrlExpression.SetContext(context); + string url = metadataNode.Evaluate(localUrlExpression).ToString(); + XPathExpression localTextExpression = textExpression.Clone(); + localTextExpression.SetContext(context); + string text = metadataNode.Evaluate(localTextExpression).ToString(); + + // return this information + TargetInfo info = new TargetInfo(url, text, type); + return (info); + } + + + + } + + // our collection of targets directories + + internal class TargetDirectoryCollection { + + public TargetDirectoryCollection() {} + + private List<TargetDirectory> targetDirectories = new List<TargetDirectory>(); + + public int Count { + get { + return(targetDirectories.Count); + } + } + + public void Add (TargetDirectory targetDirectory) { + targetDirectories.Add(targetDirectory); + } + + public TargetInfo GetTargetInfo (string file) { + foreach (TargetDirectory targetDirectory in targetDirectories) { + TargetInfo info = targetDirectory.GetTargetInfo(file); + if (info != null) return (info); + } + return (null); + } + + public TargetInfo GetTargetInfo (XPathNavigator linkNode, CustomContext context) { + foreach (TargetDirectory targetDirectory in targetDirectories) { + TargetInfo info = targetDirectory.GetTargetInfo(linkNode, context); + if (info != null) return (info); + } + return (null); + } + + } + + // a representation of a resolved target, containing all the information necessary to actually write out the link + + internal class TargetInfo { + + private string url; + + private string text; + + private LinkType type; + + public string Url { + get { + return(url); + } + } + + public string Text { + get { + return(text); + } + } + + public LinkType Type { + get { + return(type); + } + } + + internal TargetInfo (string url, string text, LinkType type) { + if (url == null) throw new ArgumentNullException("url"); + if (text == null) throw new ArgumentNullException("url"); + this.url = url; + this.text = text; + this.type = type; + } + } + + // a representation of a conceptual link + + internal class ConceptualLinkInfo { + + private string target; + + private string text; + + private bool showText = false; + + public string Target { + get { + return (target); + } + } + + public string Text { + get { + return (text); + } + } + + public bool ShowText { + get { + return (showText); + } + } + + private ConceptualLinkInfo () { } + + public static ConceptualLinkInfo Create (XPathNavigator node) { + if (node == null) throw new ArgumentNullException("node"); + + ConceptualLinkInfo info = new ConceptualLinkInfo(); + + info.target = node.GetAttribute("target", String.Empty); + info.text = node.ToString(); + + return(info); + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs new file mode 100644 index 0000000..31e4349 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs @@ -0,0 +1,473 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.XPath; + + +namespace Microsoft.Ddue.Tools { + + // replace the old component with the new one + public class ResolveReferenceLinksComponent : ResolveReferenceLinksComponent2 { + + public ResolveReferenceLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { } + + } + + public class ResolveReferenceLinksComponent2 : BuildComponent { + + // instantiation logic + + public ResolveReferenceLinksComponent2 (BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) { + + // base-url is an xpath expression applied against the current document to pick up the save location of the + // document. If specified, local links will be made relative to the base-url. + string baseUrlValue = configuration.GetAttribute("base-url", String.Empty); + if (!String.IsNullOrEmpty(baseUrlValue)) + baseUrl = XPathExpression.Compile(baseUrlValue); + + // url-format is a string format that is used to format the value of local href attributes. The default is + // "{0}.htm" for backwards compatibility. + string hrefFormatValue = configuration.GetAttribute("href-format", String.Empty); + if (!String.IsNullOrEmpty(hrefFormatValue)) + hrefFormat = hrefFormatValue; + + // the container XPath can be replaced; this is useful + string containerValue = configuration.GetAttribute("container", String.Empty); + if (!String.IsNullOrEmpty(containerValue)) XmlTargetCollectionUtilities.ContainerExpression = containerValue; + + targets = new TargetCollection(); + resolver = new LinkTextResolver(targets); + + XPathNodeIterator targets_nodes = configuration.Select("targets"); + foreach (XPathNavigator targets_node in targets_nodes) { + + // get target type + string typeValue = targets_node.GetAttribute("type", String.Empty); + if (String.IsNullOrEmpty(typeValue)) WriteMessage(MessageLevel.Error, "Each targets element must have a type attribute that specifies which type of links to create."); + + LinkType2 type = LinkType2.None; + try { + type = (LinkType2)Enum.Parse(typeof(LinkType2), typeValue, true); + if ((type == LinkType2.Msdn) && (msdn == null)) { + WriteMessage(MessageLevel.Info, "Creating MSDN URL resolver."); + msdn = new MsdnResolver(); + } + } catch (ArgumentException) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a supported reference link type.", typeValue)); + } + + // get base directory + string baseValue = targets_node.GetAttribute("base", String.Empty); + + // get file pattern + string filesValue = targets_node.GetAttribute("files", String.Empty); + if (String.IsNullOrEmpty(filesValue)) WriteMessage(MessageLevel.Error, "Each targets element must have a files attribute specifying which target files to load."); + + // determine whether to search recursively + bool recurse = false; + string recurseValue = targets_node.GetAttribute("recurse", String.Empty); + if (!String.IsNullOrEmpty(recurseValue)) { + if (String.Compare(recurseValue, Boolean.TrueString, true) == 0) { + recurse = true; + } else if (String.Compare(recurseValue, Boolean.FalseString, true) == 0) { + recurse = false; + } else { + WriteMessage(MessageLevel.Error, String.Format("On the targets element, recurse='{0}' is not an allowed value.", recurseValue)); + } + } + + // turn baseValue and filesValue into directoryPath and filePattern + string fullPath; + if (String.IsNullOrEmpty(baseValue)) { + fullPath = filesValue; + } else { + fullPath = Path.Combine(baseValue, filesValue); + } + fullPath = Environment.ExpandEnvironmentVariables(fullPath); + string directoryPath = Path.GetDirectoryName(fullPath); + if (String.IsNullOrEmpty(directoryPath)) directoryPath = Environment.CurrentDirectory; + string filePattern = Path.GetFileName(fullPath); + + // verify that directory exists + if (!Directory.Exists(directoryPath)) WriteMessage(MessageLevel.Error, String.Format("The targets directory '{0}' does not exist.", directoryPath)); + + // add the specified targets from the directory + WriteMessage(MessageLevel.Info, String.Format("Searching directory '{0}' for targets files of the form '{1}'.", directoryPath, filePattern)); + AddTargets(directoryPath, filePattern, recurse, type); + + } + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} reference targets.", targets.Count)); + + string locale_value = configuration.GetAttribute("locale", String.Empty); + if (!String.IsNullOrEmpty(locale_value) && msdn != null) msdn.Locale = locale_value; + + string target_value = configuration.GetAttribute("linkTarget", String.Empty); + if (!String.IsNullOrEmpty(target_value)) linkTarget = target_value; + } + + private void AddTargets (string directory, string filePattern, bool recurse, LinkType2 type) { + string[] files = Directory.GetFiles(directory, filePattern); + foreach (string file in files) { + AddTargets(file, type); + } + if (recurse) { + string[] subdirectories = Directory.GetDirectories(directory); + foreach (string subdirectory in subdirectories) { + AddTargets(subdirectory, filePattern, recurse, type); + } + } + } + + private void AddTargets (string file, LinkType2 type) { + try { + XPathDocument document = new XPathDocument(file); + XmlTargetCollectionUtilities.AddTargets(targets, document.CreateNavigator(), type); + } catch (XmlSchemaException e) { + WriteMessage(MessageLevel.Error, String.Format("The reference targets file '{0}' is not valid. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("The reference targets file '{0}' is not well-formed XML. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while opening the reference targets file '{0}'. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } + } + + private string linkTarget = "_blank"; + + // target information storage + + private TargetCollection targets; + + private LinkTextResolver resolver; + + private static XPathExpression referenceLinkExpression = XPathExpression.Compile("//referenceLink"); + + // WebDocs target url formatting + + private XPathExpression baseUrl; + + private string hrefFormat = "{0}.htm"; + + // component logic + + public override void Apply (XmlDocument document, string key) { + + // XmlNodeList link_nodes = document.SelectNodes("//referenceLink"); + XPathNodeIterator linkIterator = document.CreateNavigator().Select(referenceLinkExpression); + + XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(linkIterator); + + foreach (XPathNavigator linkNode in linkNodes) { + + // extract link information + ReferenceLinkInfo2 link = ReferenceLinkInfo2.Create(linkNode); + + if (link == null) { + WriteMessage(MessageLevel.Warn, "Invalid referenceLink element."); + } else { + + // determine target, link type, and display options + string targetId = link.Target; + DisplayOptions options = link.DisplayOptions; + LinkType2 type = LinkType2.None; + + Target target = targets[targetId]; + if (target == null) { + // no such target known; set link type to none and warn + type = LinkType2.None; + WriteMessage(MessageLevel.Warn, String.Format("Unknown reference link target '{0}'.", targetId)); + } else { + // if overload is prefered and found, change targetId and make link options hide parameters + if (link.PreferOverload) { + MemberTarget member = target as MemberTarget; + if ((member != null) && (!String.IsNullOrEmpty(member.OverloadId))) { + Target overloadTarget = targets[member.OverloadId]; + if (overloadTarget != null) { + target = overloadTarget; + targetId = overloadTarget.Id; + } + } + options = options & ~DisplayOptions.ShowParameters; + } + + // get stored link type + type = target.DefaultLinkType; + + // if link type is local or index, determine which + if (type == LinkType2.LocalOrIndex) { + if ((key != null) && targets.Contains(key) && (target.Container == targets[key].Container)) { + type = LinkType2.Local; + } else { + type = LinkType2.Index; + } + } + } + + // links to this page are not live + if (targetId == key) { + type = LinkType2.Self; + } else if ((target != null) && (key != null) && targets.Contains(key) && (target.File == targets[key].File)) { + type = LinkType2.Self; + } + + // get msdn endpoint, if needed + string msdnUrl = null; + if (type == LinkType2.Msdn) { + if ((msdn == null) || (msdn.IsDisabled)) { + // no msdn resolver + } else { + msdnUrl = msdn.GetMsdnUrl(targetId); + if (String.IsNullOrEmpty(msdnUrl)) { + WriteMessage(MessageLevel.Warn, String.Format("MSDN URL not found for target '{0}'.", targetId)); + } + } + if (String.IsNullOrEmpty(msdnUrl)) type = LinkType2.None; + } + + // write opening link tag and target info + XmlWriter writer = linkNode.InsertAfter(); + switch (type) { + case LinkType2.None: + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nolink"); + break; + case LinkType2.Self: + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "selflink"); + break; + case LinkType2.Local: + // format link with prefix and/or postfix + string href = String.Format(hrefFormat, target.File); + + // make link relative, if we have a baseUrl + if (baseUrl != null) + href = BuildComponentUtilities.GetRelativePath(href, BuildComponentUtilities.EvalXPathExpr(document, baseUrl, "key", key)); + + writer.WriteStartElement("a"); + writer.WriteAttributeString("href", href); + break; + case LinkType2.Index: + writer.WriteStartElement("mshelp", "link", "http://msdn.microsoft.com/mshelp"); + writer.WriteAttributeString("keywords", targetId); + writer.WriteAttributeString("tabindex", "0"); + break; + case LinkType2.Msdn: + writer.WriteStartElement("a"); + writer.WriteAttributeString("href", msdnUrl); + writer.WriteAttributeString("target", linkTarget); + break; + } + + // write the link text + if (String.IsNullOrEmpty(link.DisplayTarget)) { + if (link.Contents == null) { + if (target != null) { + resolver.WriteTarget(target, options, writer); + } else { + //Console.WriteLine("Attemting to create reference"); + Reference reference = TextReferenceUtilities.CreateReference(targetId); + //Console.WriteLine("Returned"); + if (reference is InvalidReference) WriteMessage(MessageLevel.Warn, String.Format("Invalid reference link target '{0}'.", targetId)); + resolver.WriteReference(reference, options, writer); + } + } else { + // write contents to writer + link.Contents.WriteSubtree(writer); + } + } else { + //Console.WriteLine("Display target = {0}", link.DisplayTarget); + if ((String.Compare(link.DisplayTarget, "content", true) == 0) && (link.Contents != null)) { + // Use the contents as an XML representation of the display target + + //Console.WriteLine(link.Contents.NodeType); + Reference reference = XmlTargetCollectionUtilities.CreateReference(link.Contents); + //Console.WriteLine(reference.GetType().FullName); + resolver.WriteReference(reference, options, writer); + } if ((String.Compare(link.DisplayTarget, "format", true) == 0) && (link.Contents != null)) { + // Use the contents as a format string for the display target + + string format = link.Contents.OuterXml; + //Console.WriteLine("format = {0}", format); + + string input = null; + StringWriter textStore = new StringWriter(); + try { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.ConformanceLevel = ConformanceLevel.Fragment; + + XmlWriter xmlStore = XmlWriter.Create(textStore, settings); + try { + if (target != null) { + resolver.WriteTarget(target, options, xmlStore); + } else { + Reference reference = TextReferenceUtilities.CreateReference(targetId); + resolver.WriteReference(reference, options, xmlStore); + } + } finally { + xmlStore.Close(); + } + input = textStore.ToString(); + } finally { + textStore.Close(); + } + //Console.WriteLine("input = {0}", input); + + string output = String.Format(format, input); + //Console.WriteLine("output = {0}", output); + + XmlDocumentFragment fragment = document.CreateDocumentFragment(); + fragment.InnerXml = output; + fragment.WriteTo(writer); + + //writer.WriteRaw(output); + } else { + // Use the display target value as a CER for the display target + + TextReferenceUtilities.SetGenericContext(key); + Reference reference = TextReferenceUtilities.CreateReference(link.DisplayTarget); + //Console.WriteLine("Reference is {0}", reference.GetType().FullName); + resolver.WriteReference(reference, options, writer); + } + } + + // write the closing link tag + writer.WriteEndElement(); + writer.Close(); + } + + // delete the original tag + linkNode.DeleteSelf(); + + } + + } + + // msdn resolver + + private MsdnResolver msdn = null; + + + } + + + internal class ReferenceLinkInfo2 { + + // stored data + + private string target; + + private string displayTarget; + + private DisplayOptions options = DisplayOptions.Default; + + private bool preferOverload = false; + + private XPathNavigator contents; + + // data accessors + + public string Target { + get { + return(target); + } + } + + public string DisplayTarget { + get { + return(displayTarget); + } + } + + public DisplayOptions DisplayOptions { + get { + return(options); + } + } + + public bool PreferOverload { + get { + return(preferOverload); + } + } + + public XPathNavigator Contents { + get { + return(contents); + } + } + + // creation logic + + private ReferenceLinkInfo2 () {} + + public static ReferenceLinkInfo2 Create (XPathNavigator element) { + if (element == null) throw new ArgumentNullException("element"); + + ReferenceLinkInfo2 info = new ReferenceLinkInfo2(); + + info.target = element.GetAttribute("target", String.Empty); + if (String.IsNullOrEmpty(info.target)) return(null); + + info.displayTarget = element.GetAttribute("display-target", String.Empty); + + string showContainer = element.GetAttribute("show-container", String.Empty); + if (String.IsNullOrEmpty(showContainer)) showContainer = element.GetAttribute("qualified", String.Empty); + if (!String.IsNullOrEmpty(showContainer)) { + if (String.Compare(showContainer, Boolean.TrueString, true) == 0) { + info.options = info.options | DisplayOptions.ShowContainer; + } else if (String.Compare(showContainer, Boolean.FalseString, true) == 0) { + info.options = info.options & ~DisplayOptions.ShowContainer; + } else { + return(null); + } + } + + string showTemplates = element.GetAttribute("show-templates", String.Empty); + if (!String.IsNullOrEmpty(showTemplates)) { + if (String.Compare(showTemplates, Boolean.TrueString, true) == 0) { + info.options = info.options | DisplayOptions.ShowTemplates; + } else if (String.Compare(showTemplates, Boolean.FalseString, true) == 0) { + info.options = info.options & ~DisplayOptions.ShowTemplates; + } else { + return(null); + } + } + + string showParameters = element.GetAttribute("show-parameters", String.Empty); + if (!String.IsNullOrEmpty(showParameters)) { + if (String.Compare(showParameters, Boolean.TrueString, true) == 0) { + info.options = info.options | DisplayOptions.ShowParameters; + } else if (String.Compare(showParameters, Boolean.FalseString, true) == 0) { + info.options = info.options & ~DisplayOptions.ShowParameters; + } else { + return(null); + } + } + + + string preferOverload = element.GetAttribute("prefer-overload", String.Empty); + if (String.IsNullOrEmpty(preferOverload)) preferOverload = element.GetAttribute("auto-upgrade", String.Empty); + if (!String.IsNullOrEmpty(preferOverload)) { + if (String.Compare(preferOverload, Boolean.TrueString, true) == 0) { + info.preferOverload = true; + } else if (String.Compare(preferOverload, Boolean.FalseString, true) == 0) { + info.preferOverload = false; + } else { + return(null); + } + } + + info.contents = element.Clone(); + if (!info.contents.MoveToFirstChild()) info.contents = null; + + return(info); + } + + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs new file mode 100644 index 0000000..bacb26c --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Configuration; +using System.Text; +using System.Xml; +using System.Xml.XPath; + +using System.IO; + +namespace Microsoft.Ddue.Tools { + + public class SaveComponent : BuildComponent { + + private CustomContext context = new CustomContext(); + + private XPathExpression path_expression; + + private XPathExpression select_expression; + + private XmlWriterSettings settings = new XmlWriterSettings(); + + public SaveComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // load the target path format + XPathNavigator save_node = configuration.SelectSingleNode("save"); + if (save_node == null) throw new ConfigurationErrorsException("When instantiating a save component, you must specify a the target file using the <save> element."); + + string base_value = save_node.GetAttribute("base", String.Empty); + if (!String.IsNullOrEmpty(base_value)) { + basePath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(base_value)); + } + + string path_value = save_node.GetAttribute("path", String.Empty); + if (String.IsNullOrEmpty(path_value)) WriteMessage(MessageLevel.Error, "Each save element must have a path attribute specifying an XPath that evaluates to the location to save the file."); + path_expression = XPathExpression.Compile(path_value); + + string select_value = save_node.GetAttribute("select", String.Empty); + if (!String.IsNullOrEmpty(select_value)) + select_expression = XPathExpression.Compile(select_value); + + settings.Encoding = Encoding.UTF8; + + string indent_value = save_node.GetAttribute("indent", String.Empty); + if (!String.IsNullOrEmpty(indent_value)) settings.Indent = Convert.ToBoolean(indent_value); + + string omit_value = save_node.GetAttribute("omit-xml-declaration", String.Empty); + if (!String.IsNullOrEmpty(omit_value)) settings.OmitXmlDeclaration = Convert.ToBoolean(omit_value); + + linkPath = save_node.GetAttribute("link", String.Empty); + if (String.IsNullOrEmpty(linkPath)) linkPath = "../html"; + + // encoding + + settings.CloseOutput = true; + + } + + private string basePath = null; + + private string linkPath = null; + + public override void Apply (XmlDocument document, string key) { + + // set the evaluation context + context["key"] = key; + + XPathExpression path_xpath = path_expression.Clone(); + path_xpath.SetContext(context); + + // evaluate the path + string path = document.CreateNavigator().Evaluate(path_xpath).ToString(); + string file = Path.GetFileName(path); + + string fileLinkPath = Path.Combine(linkPath, file); + if (basePath != null) path = Path.Combine(basePath, path); + + + // *SECURIY* The path name may be derived from user entered meta data in Doc Studio and as such + // it is not trustworthy. To pass, the path must be inside the directory tree base_directory + // (which is the current directory if a path is not specified). + + // This test is causing problems + /* + string absoluteBasePath = (basePath == null) ? Directory.GetCurrentDirectory() : Path.GetFullPath(basePath); + string targetPath = Path.GetDirectoryName(path); + if (!targetPath.StartsWith(absoluteBasePath, StringComparison.CurrentCultureIgnoreCase)) { + WriteMessage(MessageLevel.Error, string.Format("Cannot save document outside of base directory: {0}", targetPath)); + return; + } + */ + string targetDirectory = Path.GetDirectoryName(path); + if (!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory); + + // save the document + // select_expression determines which nodes get saved. If there is no select_expression + // we simply save the root node as before. If there is a select_expression, we evaluate the + // xpath expression and save the resulting node set. The select expression also enables the + // "literal-text" processing instruction, which outputs its content as unescaped text. + if (select_expression == null) { + XmlNode doctype = document.DocumentType; + try { + //Console.WriteLine("path = '{0}'", path); + //document.Save(path); + + using (XmlWriter writer = XmlWriter.Create(path, settings)) { + document.Save(writer); + } + + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to save to the file '{0}'. The error message is: {1}", path, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("Invalid XML was written to the output file '{0}'. The error message is: '{1}'", path, BuildComponentUtilities.GetExceptionMessage(e))); + } + + // Get the relative html path for HXF generation. + int index = fileLinkPath.IndexOf('/'); + string htmlPath = fileLinkPath.Substring(index + 1, fileLinkPath.Length - (index + 1)); + + FileCreatedEventArgs fe = new FileCreatedEventArgs(htmlPath, Path.GetDirectoryName(targetDirectory)); + OnComponentEvent(fe); + } + else { + // IMPLEMENTATION NOTE: The separate StreamWriter is used to maintain XML indenting. + // Without it the XmlWriter won't honor our indent settings after plain text nodes have been + // written. + settings.ConformanceLevel = ConformanceLevel.Auto; + using (StreamWriter output = File.CreateText(path)) { + using (XmlWriter writer = XmlWriter.Create(output, settings)) { + XPathExpression select_xpath = select_expression.Clone(); + select_xpath.SetContext(context); + XPathNodeIterator ni = document.CreateNavigator().Select(select_expression); + while (ni.MoveNext()) { + if (ni.Current.NodeType == XPathNodeType.ProcessingInstruction && ni.Current.Name.Equals("literal-text")) { + writer.Flush(); + output.Write(ni.Current.Value); + } + else + ni.Current.WriteSubtree(writer); + } + } + } + } + } + } +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SharedContentComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SharedContentComponent.cs new file mode 100644 index 0000000..f339800 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SharedContentComponent.cs @@ -0,0 +1,377 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Schema; + +// still have problems with spaces + +namespace Microsoft.Ddue.Tools { + + public class SharedContentComponent : BuildComponent { + + public SharedContentComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // get context + context = GetContext(configuration); + + // get the tags to be resolved + XPathNodeIterator resolve_nodes = configuration.Select("replace"); + foreach (XPathNavigator resolve_node in resolve_nodes) { + string path = resolve_node.GetAttribute("elements", String.Empty); + if (String.IsNullOrEmpty(path)) path = "//(include|includeAttribute)"; + // if (String.IsNullOrEmpty(path)) WriteMessage(MessageLevel.Error, "Each resolve element must contain a path attribute specifying an XPath expression for shared content elements."); + try { + XPathExpression path_expresion = XPathExpression.Compile(path, context); + } catch (XPathException) { + WriteMessage(MessageLevel.Error, String.Format("The elements expression '{0}' is not a valid XPath.", path)); + } + + string item = resolve_node.GetAttribute("item", String.Empty); + if (String.IsNullOrEmpty(item)) item = "string(@item)"; + try { + XPathExpression item_expression = XPathExpression.Compile(item, context); + } catch (XPathException) { + WriteMessage(MessageLevel.Error, String.Format("The item expression '{0}' is not a valid XPath.", item)); + } + + string parameters = resolve_node.GetAttribute("parameters", String.Empty); + if (String.IsNullOrEmpty(parameters)) parameters = "parameter"; + + string attribute = resolve_node.GetAttribute("attribute", String.Empty); + if (String.IsNullOrEmpty(attribute)) attribute = "string(@name)"; + + elements.Add( new SharedContentElement(path, item, parameters, attribute, context) ); + } + + // Console.WriteLine("{0} elements explicitly defined", elements.Count); + + if (elements.Count == 0) elements.Add( new SharedContentElement(@"//include | //includeAttribute", "string(@item)", "parameter", "string(@name)", context) ); + + // get the source and target formats + XPathNodeIterator content_nodes = configuration.Select("content"); + foreach (XPathNavigator content_node in content_nodes) + { + // get the files + string sharedContentFiles = content_node.GetAttribute("file", String.Empty); + if (String.IsNullOrEmpty(sharedContentFiles)) + WriteMessage(MessageLevel.Error, "The content/@file attribute must specify a path."); + ParseDocuments(sharedContentFiles); + } + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} shared content items.", content.Count)); + } + + public void ParseDocuments(string wildcardPath) + { + string sharedContentFiles = Environment.ExpandEnvironmentVariables(wildcardPath); + if (String.IsNullOrEmpty(sharedContentFiles)) + WriteMessage(MessageLevel.Error, "The content/@file attribute specifies an empty string."); + + WriteMessage(MessageLevel.Info, String.Format("Searching for files that match '{0}'.", sharedContentFiles)); + string directoryPart = Path.GetDirectoryName(sharedContentFiles); + if (String.IsNullOrEmpty(directoryPart)) + directoryPart = Environment.CurrentDirectory; + directoryPart = Path.GetFullPath(directoryPart); + string filePart = Path.GetFileName(sharedContentFiles); + string[] files = Directory.GetFiles(directoryPart, filePart); + foreach (string file in files) + LoadContent(file); + WriteMessage(MessageLevel.Info, String.Format("Found {0} files in {1}.", files.Length, sharedContentFiles)); + } + + private void LoadContent(string file) + { + + WriteMessage(MessageLevel.Info, String.Format("Loading shared content file '{0}'.", file) ); + + try { + XmlReader reader = XmlReader.Create(file); + + try { + reader.MoveToContent(); + while (!reader.EOF) { + + if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "item")) { + + string key = reader.GetAttribute("id").ToLower(); + string value = reader.ReadInnerXml(); + + if (content.ContainsKey(key)) WriteMessage(MessageLevel.Info, String.Format("Overriding shared content item '{0}' with value in file '{1}'.", key, file)); + content[key] = value; + // content.Add(key, value); + } else { + reader.Read(); + } + + } + } finally { + reader.Close(); + } + + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("The shared content file '{0}' could not be opened. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("The shared content file '{0}' is not well-formed. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XmlSchemaException e) { + WriteMessage(MessageLevel.Error, String.Format("The shared content file '{0}' is not valid. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } + + } + + private void DetectLoops () { + + } + + // Stored data + + // The context + private CustomContext context = new CustomContext(); + + // The shared content items + private Dictionary<string,string> content = new Dictionary<string,string>(); + + // THe shared content elements + private List<SharedContentElement> elements = new List<SharedContentElement>(); + + public override void Apply (XmlDocument document, string key) { + ResolveContent(document); + } + + // private XPathExpression expression = XPathExpression.Compile("//include | //includeAttribute"); + + private void ResolveContent (XmlDocument document) { + //Console.WriteLine("doc={0}", document.CreateNavigator().InnerXml); + ResolveContent(document, document.CreateNavigator()); + } + + private void ResolveContent (XmlDocument document, XPathNavigator start) { + + // for each kind of shared content element + foreach (SharedContentElement element in elements) { + + // find all such elements + XPathNodeIterator nodeIterator = start.Select(element.Path); + // Console.WriteLine("Found {0} shared content elements.", nodeIterator.Count); + + // convert to an array so as not to cause an error when manipulating the document + XPathNavigator[] nodes = ConvertIteratorToArray(nodeIterator); + + // process each element + foreach (XPathNavigator node in nodes) { + + // Console.WriteLine(node.LocalName); + + // get the key + string item = node.Evaluate(element.Item).ToString().ToLower(); + + // check for missing key + if (String.IsNullOrEmpty(item)) { + WriteMessage(MessageLevel.Warn, "A shared content element did not specify an item."); + } else { + + // Console.WriteLine("node={0}", node.OuterXml); + + // extract parameters + List<string> parameters = new List<string>(); + XPathNodeIterator parameter_nodes = node.Select(element.Parameters); + foreach (XPathNavigator parameter_node in parameter_nodes) { + string parameter = BuildComponentUtilities.GetInnerXml(parameter_node); + // Console.WriteLine("parameter={0}", parameter); + parameters.Add(parameter); + } + + // get the content + string content = GetContent(item, parameters.ToArray()); + + // check for missing content + if (content == null) { + WriteMessage(MessageLevel.Warn, String.Format("Missing shared content item. Tag:'{0}'; Id:'{1}'.", node.LocalName, item)); + } else { + + // store the content in a document fragment + XmlDocumentFragment fragment = document.CreateDocumentFragment(); + fragment.InnerXml = content; + + // resolve any shared content in the fragment + ResolveContent(document, fragment.CreateNavigator()); + //string resolvedContent = fragment.InnerXml; + //Console.WriteLine("value = '{0}'", resolvedContent); + + // look for an attribute name + string attribute = node.Evaluate(element.Attribute).ToString(); + + // insert the resolved content + if (String.IsNullOrEmpty(attribute)) { + // as mixed content + // node.InsertAfter(resolvedContent); + XmlWriter writer = node.InsertAfter(); + fragment.WriteTo(writer); + writer.Close(); + } else { + // as an attribute + XPathNavigator parent = node.CreateNavigator(); + parent.MoveToParent(); + parent.CreateAttribute(String.Empty, attribute, String.Empty, fragment.InnerText); + } + + } + + + } + + // keep a reference to the parent element + XPathNavigator parentElement = node.CreateNavigator(); + parentElement.MoveToParent(); + + // remove the node + node.DeleteSelf(); + + // if there is no content left in the parent element, make sure it is self-closing + if (!parentElement.HasChildren && !parentElement.IsEmptyElement) { + + //If 'node' was already the root then we will have a blank node now and + //doing an InsertAfter() will throw an exception. + if (parentElement.Name.Length > 0) + { + // create a new element + XmlWriter attributeWriter = parentElement.InsertAfter(); + attributeWriter.WriteStartElement(parentElement.Prefix, parentElement.LocalName, parentElement.NamespaceURI); + + // copy attributes to it + XmlReader attributeReader = parentElement.ReadSubtree(); + attributeReader.Read(); + attributeWriter.WriteAttributes(attributeReader, false); + attributeReader.Close(); + + // close it + attributeWriter.WriteEndElement(); + attributeWriter.Close(); + + // delete the old element + parentElement.DeleteSelf(); + } + else + { + //if we are inside a tag such as title, removing the content will make it in the + //form of <title /> which is not allowed in html. + //Since this usually means there is a problem with the shared content or the transforms + //leading up to this we will just report the error here. + WriteMessage(MessageLevel.Error, "Error replacing item."); + } + } + + } + + } + + } + + // look up shared content + private string GetContent (string key, string[] parameters) { + + string value; + if (content.TryGetValue(key, out value)) { + try { + value = String.Format(value, parameters); + } catch (FormatException) { + WriteMessage(MessageLevel.Error, String.Format("The shared content item '{0}' could not be formatted with {1} parameters.", key, parameters.Length)); + } + + return(value); + } else { + return(null); + } + + } + + private static XPathNavigator[] ConvertIteratorToArray (XPathNodeIterator iterator) { + XPathNavigator[] result = new XPathNavigator[iterator.Count]; + for (int i=0; i<result.Length; i++) { + iterator.MoveNext(); + result[i] = iterator.Current.Clone(); + } + return(result); + } + + private static CustomContext GetContext (XPathNavigator configuration) { + + CustomContext context = new CustomContext(); + + XPathNodeIterator context_nodes = configuration.Select("context"); + foreach (XPathNavigator context_node in context_nodes) { + string prefix = context_node.GetAttribute("prefix", String.Empty); + string name = context_node.GetAttribute("name", String.Empty); + context.AddNamespace(prefix, name); + } + + return(context); + + } + + } + + internal class SharedContentElement { + + public SharedContentElement (string path, string item, string parameters, string attribute, IXmlNamespaceResolver context) { + this.path = XPathExpression.Compile(path, context); + this.item = XPathExpression.Compile(item, context); + this.parameters = XPathExpression.Compile(parameters, context); + this.attribute = XPathExpression.Compile(attribute, context); + } + + + + public SharedContentElement (string path, string item, string parameters, string attribute) { + this.path = XPathExpression.Compile(path); + this.item = XPathExpression.Compile(item); + this.parameters = XPathExpression.Compile(parameters); + if (attribute != null) { + this.attribute = XPathExpression.Compile(attribute); + } + } + + private XPathExpression path; + + private XPathExpression item; + + private XPathExpression parameters; + + private XPathExpression attribute; + + public XPathExpression Path { + get { + return(path); + } + } + + public XPathExpression Item { + get { + return(item); + } + } + + public XPathExpression Parameters { + get { + return(parameters); + } + } + + public XPathExpression Attribute { + get { + return(attribute); + } + } + + public bool IsAttribute { + get { + return(attribute != null); + } + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SwitchComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SwitchComponent.cs new file mode 100644 index 0000000..47b1d6c --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SwitchComponent.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class SwitchComponent : BuildComponent { + + public SwitchComponent(BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // get the condition + XPathNavigator condition_element = configuration.SelectSingleNode("switch"); + if (condition_element == null) { + throw new ConfigurationErrorsException("You must specifiy a condition using the <switch> statement with a 'value' attribute."); + } + string condition_value = condition_element.GetAttribute("value", String.Empty); + if (String.IsNullOrEmpty(condition_value)) { + throw new ConfigurationErrorsException("The switch statement must have a 'value' attribute, which is an xpath expression."); + } + condition = XPathExpression.Compile(condition_value); + + // load the component stacks for each case + XPathNodeIterator case_elements = configuration.Select("case"); + foreach (XPathNavigator case_element in case_elements) { + string case_value = case_element.GetAttribute("value", String.Empty); + BuildComponent[] components = BuildAssembler.LoadComponents(case_element); + cases.Add(case_value, components); + } + } + + // data held by the component + + private XPathExpression condition; + + private Dictionary<string,IEnumerable<BuildComponent>> cases = new Dictionary<string,IEnumerable<BuildComponent>>(); + + // the action of the component + + public override void Apply(XmlDocument document, string key) { + + // evaluate the condition + string result = document.CreateNavigator().Evaluate(condition).ToString(); + + // get the corresponding component stack + IEnumerable<BuildComponent> components; + if (cases.TryGetValue(result, out components)) { + // apply it + foreach (BuildComponent component in components) { + component.Apply(document, key); + } + } else { + // no such case exists + } + + } + + public override void Dispose () { + foreach (IEnumerable<BuildComponent> components in cases.Values) { + foreach (BuildComponent component in components) { + component.Dispose(); + } + } + base.Dispose(); + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SyntaxComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SyntaxComponent.cs new file mode 100644 index 0000000..39c68f9 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SyntaxComponent.cs @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Reflection; +using System.Xml; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + + public class SyntaxComponent : BuildComponent { + + public SyntaxComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNavigator syntax_node = configuration.SelectSingleNode("syntax"); + string syntax_input_xpath = syntax_node.GetAttribute("input", String.Empty); + if (String.IsNullOrEmpty(syntax_input_xpath)) throw new ConfigurationErrorsException("You must specify an XPath for input in the syntax element."); + syntax_input = XPathExpression.Compile(syntax_input_xpath); + string syntax_output_xpath = syntax_node.GetAttribute("output", String.Empty); + if (String.IsNullOrEmpty(syntax_output_xpath)) throw new ConfigurationErrorsException("You must specify an XPath for output in the syntax element."); + syntax_output = XPathExpression.Compile(syntax_output_xpath); + + writerType = typeof(ManagedSyntaxWriter); + //if (writerType == null) Console.WriteLine("null writer"); + + XPathNodeIterator generator_nodes = configuration.Select("generators/generator"); + foreach (XPathNavigator generator_node in generator_nodes) { + + // get the data to load the generator + string assembly_path = generator_node.GetAttribute("assembly", String.Empty); + if (String.IsNullOrEmpty(assembly_path)) WriteMessage(MessageLevel.Error, "Each generator element must have an assembly attribute."); + string type_name = generator_node.GetAttribute("type", String.Empty); + if (String.IsNullOrEmpty(type_name)) WriteMessage(MessageLevel.Error, "Each generator element must have a type attribute."); + + // expand environment variables in the path + assembly_path = Environment.ExpandEnvironmentVariables(assembly_path); + + //Console.WriteLine("loading {0} from {1}", type_name, assembly_path); + try { + Assembly assembly = Assembly.LoadFrom(assembly_path); + SyntaxGenerator generator = (SyntaxGenerator)assembly.CreateInstance(type_name, false, BindingFlags.Public | BindingFlags.Instance, null, new Object[1] { generator_node.Clone() }, null, null); + + if (generator == null) { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' does not exist in the assembly '{1}'.", type_name, assembly_path)); + } else { + generators.Add(generator); + } + + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("A file access error occured while attempting to load the build component '{0}'. The error message is: {1}", assembly_path, e.Message)); + } catch (BadImageFormatException e) { + WriteMessage(MessageLevel.Error, String.Format("A syntax generator assembly '{0}' is invalid. The error message is: {1}.", assembly_path, e.Message)); + } catch (TypeLoadException e) { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' does not exist in the assembly '{1}'. The error message is: {2}", type_name, assembly_path, e.Message)); + } catch (MissingMethodException e) { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' in the assembly '{1}' does not have an appropriate constructor. The error message is: {2}", type_name, assembly_path, e.Message)); + } catch (TargetInvocationException e) { + WriteMessage(MessageLevel.Error, String.Format("An error occured while attempting to instantiate the type '{0}' in the assembly '{1}'. The error message is: {2}", type_name, assembly_path, e.InnerException.Message)); + } catch (InvalidCastException e) { + WriteMessage(MessageLevel.Error, String.Format("The type '{0}' in the assembly '{1}' is not a SyntaxGenerator.", type_name, assembly_path)); + } + } + + WriteMessage(MessageLevel.Info, String.Format("Loaded {0} syntax generators.", generators.Count)); + + } + + private XPathExpression syntax_input; + + private XPathExpression syntax_output; + + private Type writerType; + + private List<SyntaxGenerator> generators = new List<SyntaxGenerator>(); + + public override void Apply (XmlDocument document, string key) { + + XPathNavigator input = document.CreateNavigator().SelectSingleNode(syntax_input); + if (input == null) Console.WriteLine("null input"); + + XPathNavigator output = document.CreateNavigator().SelectSingleNode(syntax_output); + if (output == null) Console.WriteLine("null output"); + + SyntaxWriter writer = (SyntaxWriter) Activator.CreateInstance(writerType, new Object[1] { output }); + + foreach (SyntaxGenerator generator in generators) { + generator.WriteSyntax(input, writer); + } + + } + + } + + // the writer + + public abstract class SyntaxWriter { + + protected SyntaxWriter (XPathNavigator location) {} + + // Syntax block APIs + + public virtual int Position { + get { + return (-1); + } + } + + public abstract void WriteStartBlock(string language); + + public abstract void WriteStartSubBlock(string classId); + + public abstract void WriteEndSubBlock(); + + public abstract void WriteString(string text); + + public abstract void WriteStringWithStyle (string text, string style); + + public abstract void WriteReferenceLink (string reference); + + public abstract void WriteReferenceLink (string reference, string text); + + public virtual void WriteLine () { + WriteString("\n"); + } + + public virtual void WriteKeyword (string keyword) { + WriteStringWithStyle(keyword, "keyword"); + } + + public virtual void WriteParameter (string parameter) { + WriteStringWithStyle(parameter, "parameter"); + } + + public virtual void WriteIdentifier (string identifier) { + WriteStringWithStyle(identifier, "identifier"); + } + + public virtual void WriteLiteral (string literal) { + WriteStringWithStyle(literal, "literal"); + } + + public virtual void WriteMessage(string message) + { + WriteMessage(message, null); + } + + public abstract void WriteMessage(string message, IEnumerable<string> parameters); + + public abstract void WriteEndBlock(); + + } + + // the concrete writer + // the should really be moved out + + public class ManagedSyntaxWriter : SyntaxWriter { + + public ManagedSyntaxWriter (XPathNavigator location) : base(location) { + if (location == null) Console.WriteLine("null location"); + this.location = location; + } + + XPathNavigator location; + + XmlWriter writer; + + // position along the line + int position = 0; + + public override int Position { + get { + return (position); + } + } + + public override void WriteStartBlock(string language) + { + writer = location.AppendChild(); + writer.WriteStartElement("div"); + writer.WriteAttributeString("codeLanguage", language); + position = 0; + } + + public override void WriteStartSubBlock(string classId) + { + writer.WriteStartElement("div"); + writer.WriteAttributeString("class", classId); + position = 0; + } + + public override void WriteEndSubBlock() + { + writer.WriteEndElement(); + position = 0; + } + + public override void WriteLine () { + base.WriteLine(); + position = 0; + } + + public override void WriteString(string text) + { + writer.WriteString(text); + position += text.Length; + } + + public override void WriteStringWithStyle (string text, string style) { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", style); + WriteString(text); + writer.WriteEndElement(); + position += text.Length; + } + + public override void WriteReferenceLink (string reference) { + writer.WriteStartElement("referenceLink"); + writer.WriteAttributeString("target", reference); + writer.WriteAttributeString("prefer-overload", "false"); + writer.WriteAttributeString("show-container", "false"); + writer.WriteAttributeString("show-templates", "false"); + writer.WriteAttributeString("show-parameters", "false"); + writer.WriteEndElement(); + position += 10; // approximate + } + + public override void WriteReferenceLink (string reference, string text) { + writer.WriteStartElement("referenceLink"); + writer.WriteAttributeString("target", reference); + writer.WriteAttributeString("prefer-overload", "false"); + writer.WriteAttributeString("show-container", "false"); + writer.WriteAttributeString("show-templates", "false"); + writer.WriteAttributeString("show-parameters", "false"); + writer.WriteString(text); + writer.WriteEndElement(); + position += text.Length; + } + + public override void WriteEndBlock () { + writer.WriteEndElement(); + writer.Close(); + position = 0; + } + + public override void WriteMessage(string message, IEnumerable<string> parameters) + { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "message"); + writer.WriteStartElement("include"); + writer.WriteAttributeString("item", message); + if (parameters != null) + { + foreach (string parameter in parameters) + { + writer.WriteStartElement("parameter"); + writer.WriteRaw(parameter); + writer.WriteEndElement(); + } + } + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + } + + // the abstract generator + + public abstract class SyntaxGenerator { + + protected SyntaxGenerator (XPathNavigator configuration) { } + + public abstract void WriteSyntax (XPathNavigator reflection, SyntaxWriter writer); + + } + + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TargetCollection.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TargetCollection.cs new file mode 100644 index 0000000..4366e6f --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TargetCollection.cs @@ -0,0 +1,2420 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Schema; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + // The basic object model here is this: + // * Target objects represent files that can be targeted by a reference link + // * Different child objects of Target represent different sorts of API targets: Namespace, Type, Member, etc. + // * Targets are stored in a TargetCollection + // To indicate relationships between targets (e.g. a Method takes a particular type parameter), we + // introduce another set of classes: + // * Reference objects refer to a specific target + // * Objects like SpecializedTypeReference and ArrayTypeReference that represent decorated types + // There are two ways to construct such objects: + // * XML from a reflection information file defines Target and Reference objects. XmlUtilities does this. + // * Code entity reference strings construct Reference objecs. CerUtilities does this. + // Finally, we need a way to write the link text corresponding to a reference: + // * LinkTextResolver contains routines that, given a reference, writes the corresponding link text + + // all arguments of public methods are verified + + // The fact that the creation methods (via XML or CER strings) for references and their rendering methods + // are seperated from the declarations of the reference types goes against OO principals. (The consequent + // absence of virtual methods also makes for a lot of ugly casting to figure out what method to call.) + // But there is a reason for it: I wanted all the code that intrepreted XML together, all the code that + // intrepreted CER strings together, and all the code that did link text renderig together, and I wanted + // them all seperate from each other. I belive this is extremely important for maintainability. It may + // be possible to leverage partial classes to do this in a more OO fashion. + + public class Test { + + public static void Main (string[] args) { + + TargetCollection targets = new TargetCollection(); + + XPathDocument document = new XPathDocument(args[0]); + XPathNavigator node = document.CreateNavigator(); + XmlTargetCollectionUtilities.AddTargets(targets, node, LinkType2.Local); + Console.WriteLine(targets.Count); + + LinkTextResolver resolver = new LinkTextResolver(targets); + + // test writer + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + XmlWriter writer = XmlWriter.Create(Console.Out, settings); + writer.WriteStartDocument(); + writer.WriteStartElement("types"); + XPathNodeIterator apiNodes = node.Select("/*/apis/api[not(apidata/@subgroup='enumeration')]//*[@display-api]"); + foreach (XPathNavigator apiNode in apiNodes) { + string api = apiNode.GetAttribute("display-api", String.Empty); + if (api[1] != ':') continue; + + string id = (string) apiNode.Evaluate("string(ancestor::api[1]/@id)"); + TextReferenceUtilities.SetGenericContext(id); + + Reference reference = TextReferenceUtilities.CreateReference(api); + + writer.WriteStartElement("test"); + writer.WriteAttributeString("api", api); + writer.WriteAttributeString("context", id); + if (reference == null) { + writer.WriteString("NULL REFERENCE"); + } else { + resolver.WriteReference(reference, DisplayOptions.ShowContainer | DisplayOptions.ShowTemplates | DisplayOptions.ShowParameters, writer); + } + writer.WriteEndElement(); + + } + + writer.WriteEndElement(); + writer.WriteEndDocument(); + writer.Close(); + + } + + } + + // contains a collection of targets + + public class TargetCollection { + + private Dictionary<string, Target> index = new Dictionary<string, Target>(); + + // read the collection + + public Target this[string id] { + get { + Target result; + index.TryGetValue(id, out result); + return (result); + } + } + + public bool Contains (string id) { + return( index.ContainsKey(id) ); + } + + public IEnumerable<string> Ids { + get { + return (index.Keys); + } + } + + public IEnumerable<Target> Targets { + get { + return (index.Values); + } + } + + public int Count { + get { + return(index.Count); + } + } + + // change the collection + + public void Add (Target target) { + index[target.Id] = target; + //index.Add(target.Id, target); + } + + public void Clear () { + index.Clear(); + } + + } + + // targets + + public class Target { + + internal string id; + + internal string container; + + internal string file; + + internal LinkType2 type; + + public string Id { + get { + return (id); + } + } + + public string Container { + get { + return (container); + } + } + + public string File { + get { + return(file); + } + } + + internal LinkType2 DefaultLinkType { + get { + return (type); + } + } + + public virtual void Add (TargetCollection targets) { + targets.Add(this); + } + + } + + public class NamespaceTarget : Target { + + internal string name; + + public string Name { + get { + return(name); + } + } + + } + + public class TypeTarget : Target { + + internal string name; + + internal NamespaceReference containingNamespace; + + internal SimpleTypeReference containingType; + + internal string[] templates; + + public string Name { + get { + return(name); + } + } + + public NamespaceReference Namespace { + get { + return (containingNamespace); + } + } + + public SimpleTypeReference OuterType { + get { + return (containingType); + } + } + + public string[] Templates { + get { + return(templates); + } + } + + } + + public class EnumerationTarget : TypeTarget { + + internal MemberTarget[] elements; + + public override void Add (TargetCollection targets) { + base.Add(targets); + + foreach (MemberTarget element in elements) { + element.Add(targets); + } + } + + } + + public class MemberTarget : Target { + + internal string name; + + internal SimpleTypeReference containingType; + + internal string overload; + + public string Name { + get { + return (name); + } + } + + public TypeReference Type { + get { + return (containingType); + } + } + + public string OverloadId { + get { + return (overload); + } + } + + } + + public class ConstructorTarget : MemberTarget { + + internal Parameter[] parameters; + + public Parameter[] Parameters { + get { + return (parameters); + } + } + + } + + public class ProcedureTarget : MemberTarget { + + internal MemberReference explicitlyImplements = null; + + public MemberReference ExplicitlyImplements { + get { + return (explicitlyImplements); + } + } + + } + + public class EventTarget : ProcedureTarget { + } + + public class PropertyTarget : ProcedureTarget { + + internal Parameter[] parameters; + + internal TypeReference returnType; + + public Parameter[] Parameters { + get { + return (parameters); + } + } + + } + + public class MethodTarget : ProcedureTarget { + + internal Parameter[] parameters; + + internal TypeReference returnType; + + internal string[] templates; + + public Parameter[] Parameters { + get { + return (parameters); + } + } + + public string[] Templates { + get { + return (templates); + } + } + + } + + public class Parameter { + + private string name; + + private TypeReference type; + + public string Name { + get { + return (name); + } + } + + public TypeReference Type { + get { + return (type); + } + } + + + internal Parameter (string name, TypeReference type) { + this.name = name; + this.type = type; + } + + } + + // ***** Reference objects ***** + + public abstract class Reference { } + + public class NamespaceReference : Reference { + + private string namespaceId; + + public string Id { + get { + return(namespaceId); + } + } + + public Target Resolve (TargetCollection targets) { + return(targets[namespaceId]); + } + + internal NamespaceReference (string id) { + this.namespaceId = id; + } + + } + + public abstract class TypeReference : Reference {} + + public class SimpleTypeReference : TypeReference { + + private string typeId; + + public string Id { + get { + return(typeId); + } + } + + public Target Resolve (TargetCollection targets) { + return(targets[typeId]); + } + + internal SimpleTypeReference (string id) { + this.typeId = id; + } + + } + + public class SpecializedTypeReference : TypeReference { + + private Specialization[] specializations; + + public Specialization[] Specializations { + get { + return (specializations); + } + } + + internal SpecializedTypeReference (Specialization[] specializations) { + if (specializations == null) throw new ArgumentNullException("specializations"); + this.specializations = specializations; + } + + public Dictionary<IndexedTemplateTypeReference, TypeReference> GetSpecializationDictionary () { + Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary = new Dictionary<IndexedTemplateTypeReference, TypeReference>(); + foreach (Specialization specialization in specializations) { + for (int index=0; index<specialization.Arguments.Length; index++) { + IndexedTemplateTypeReference template = new IndexedTemplateTypeReference(specialization.TemplateType.Id, index); + dictionary.Add(template, specialization.Arguments[index]); + } + } + return (dictionary); + } + + } + + public class Specialization { + + private SimpleTypeReference template; + + public TypeReference[] arguments; + + public SimpleTypeReference TemplateType { + get { + return (template); + } + } + + public TypeReference[] Arguments { + get { + return (arguments); + } + } + + internal Specialization (SimpleTypeReference template, TypeReference[] arguments) { + if (template == null) throw new ArgumentNullException("template"); + if (arguments == null) throw new ArgumentNullException("arguments"); + this.template = template; + this.arguments = arguments; + } + + } + + public abstract class TemplateTypeReference : TypeReference { } + + public class IndexedTemplateTypeReference : TemplateTypeReference { + + private string templateId; + + private int index; + + public string TemplateId { + get { + return (templateId); + } + } + + public int Index { + get { + return (index); + } + } + + internal IndexedTemplateTypeReference (string templateId, int index) { + if (templateId == null) throw new ArgumentNullException("templateId"); + if (index < 0) throw new ArgumentOutOfRangeException("index"); + this.templateId = templateId; + this.index = index; + } + + public override int GetHashCode () { + return (index ^ templateId.GetHashCode()); + } + + public override bool Equals (object obj) { + IndexedTemplateTypeReference other = obj as IndexedTemplateTypeReference; + if (other == null) return (false); + if ((this.index == other.index) && (this.templateId == other.templateId)) { + return (true); + } else { + return (false); + } + } + } + + public class NamedTemplateTypeReference : TemplateTypeReference { + + private string name; + + public string Name { + get { + return (name); + } + } + + public NamedTemplateTypeReference (string name) { + this.name = name; + } + + } + + public class TypeTemplateTypeReference : TemplateTypeReference { + + private SimpleTypeReference template; + + private int position; + + public SimpleTypeReference TemplateType { + get { + return (template); + } + } + + public int Position { + get { + return (position); + } + } + + internal TypeTemplateTypeReference (SimpleTypeReference template, int position) { + if (template == null) throw new ArgumentNullException("template"); + if (position < 0) throw new ArgumentOutOfRangeException("position"); + this.template = template; + this.position = position; + } + + } + + public class MethodTemplateTypeReference : TemplateTypeReference { + + private MemberReference template; + + private int position; + + public MemberReference TemplateMethod { + get { + return (template); + } + } + + public int Position { + get { + return (position); + } + } + + internal MethodTemplateTypeReference (MemberReference template, int position) { + this.template = template; + this.position = position; + } + + } + + public class ArrayTypeReference : TypeReference { + + private int rank; + + private TypeReference elementType; + + public int Rank { + get { + return(rank); + } + } + + public TypeReference ElementType { + get { + return(elementType); + } + } + + internal ArrayTypeReference (TypeReference elementType, int rank) { + if (elementType == null) throw new ArgumentNullException("elementType"); + if (rank <= 0) throw new ArgumentOutOfRangeException("rank"); + this.elementType = elementType; + this.rank = rank; + } + + } + + public class ReferenceTypeReference : TypeReference { + + private TypeReference referedToType; + + public TypeReference ReferedToType { + get { + return (referedToType); + } + } + + internal ReferenceTypeReference (TypeReference referedToType) { + if (referedToType == null) throw new ArgumentNullException("referedToType"); + this.referedToType = referedToType; + } + } + + public class PointerTypeReference : TypeReference { + + private TypeReference pointedToType; + + public TypeReference PointedToType { + get { + return (pointedToType); + } + } + + internal PointerTypeReference (TypeReference pointedToType) { + if (pointedToType == null) throw new ArgumentNullException("pointedToType"); + this.pointedToType = pointedToType; + } + } + + + public abstract class MemberReference : Reference { } + + public class SimpleMemberReference : MemberReference { + + private string memberId; + + public string Id { + get { + return(memberId); + } + } + + public Target Resolve (TargetCollection targets) { + return(targets[memberId]); + } + + internal SimpleMemberReference (string id) { + if (id == null) throw new ArgumentNullException("id"); + this.memberId = id; + } + + } + + + public class SpecializedMemberReference : MemberReference { + + private SimpleMemberReference member; + + private SpecializedTypeReference type; + + public SimpleMemberReference TemplateMember { + get { + return(member); + } + } + + public SpecializedTypeReference SpecializedType { + get { + return (type); + } + } + + internal SpecializedMemberReference (SimpleMemberReference member, SpecializedTypeReference type) { + if (member == null) throw new ArgumentNullException("member"); + if (type == null) throw new ArgumentNullException("type"); + this.member = member; + this.type = type; + } + + } + + public class SpecializedMemberWithParametersReference : MemberReference { + + private string prefix; + + private SpecializedTypeReference type; + + private string member; + + private TypeReference[] parameters; + + public string Prefix { + get { + return (prefix); + } + } + + public SpecializedTypeReference SpecializedType { + get { + return (type); + } + } + + public string MemberName { + get { + return (member); + } + } + + public TypeReference[] ParameterTypes { + get { + return(parameters); + } + } + + internal SpecializedMemberWithParametersReference (string prefix, SpecializedTypeReference type, string member, TypeReference[] parameters) { + if (type == null) throw new ArgumentNullException("type"); + if (parameters == null) throw new ArgumentNullException("parameters"); + this.prefix = prefix; + this.type = type; + this.member = member; + this.parameters = parameters; + } + + } + + public class InvalidReference : Reference { + + private string id; + + public String Id { + get { + return (id); + } + } + + internal InvalidReference (string id) { + this.id = id; + } + + } + + // ***** Logic to construct Target & Reference objects from XML reflection data ***** + // Anything that depends on specifics of the XML reflection data format lives here + + public static class XmlTargetCollectionUtilities { + + // XPath expressions for extracting data + + // topic data + private static XPathExpression topicIdExpression = XPathExpression.Compile("string(@id)"); + private static XPathExpression topicContainerExpression = XPathExpression.Compile("string(containers/library/@assembly)"); + private static XPathExpression topicFileExpression = XPathExpression.Compile("string(file/@name)"); + private static XPathExpression topicGroupExpression = XPathExpression.Compile("string(topicdata/@group)"); + + // api data + private static XPathExpression apiNameExpression = XPathExpression.Compile("string(apidata/@name)"); + private static XPathExpression apiGroupExpression = XPathExpression.Compile("string(apidata/@group)"); + private static XPathExpression apiSubgroupExpression = XPathExpression.Compile("string(apidata/@subgroup)"); + private static XPathExpression apiSubsubgroupExpression = XPathExpression.Compile("string(apidata/@subsubgroup)"); + + // member data + private static XPathExpression apiOverloadIdExpression = XPathExpression.Compile("string(overload/@api | memberdata/@overload)"); + + // explicit implmentation data + private static XPathExpression apiIsExplicitImplementationExpression = XPathExpression.Compile("boolean(memberdata/@visibility='private' and proceduredata/@virtual='true' and boolean(implements/member))"); + private static XPathExpression apiImplementedMembersExpression = XPathExpression.Compile("implements/member"); + + // container data + private static XPathExpression apiContainingNamespaceExpression = XPathExpression.Compile("(containers/namespace)[1]"); + private static XPathExpression apiContainingTypeExpression = XPathExpression.Compile("(containers/type)[1]"); + + // reference data + private static XPathExpression referenceApiExpression = XPathExpression.Compile("string(@api)"); + + // template data + private static XPathExpression apiTemplatesExpression = XPathExpression.Compile("templates/template"); + private static XPathExpression templateNameExpression = XPathExpression.Compile("string(@name)"); + + // Change the container + + public static string ContainerExpression { + get { + return (topicContainerExpression.Expression); + } + set { + topicContainerExpression = XPathExpression.Compile(value); + } + } + + // super factory method + + public static void AddTargets (TargetCollection targets, XPathNavigator topicsNode, LinkType2 type) { + XPathNodeIterator topicNodes = topicsNode.Select("/*/apis/api[not(topicdata/@notopic)]"); + foreach (XPathNavigator topicNode in topicNodes) { + Target target = CreateTarget(topicNode, type); + if (target != null) target.Add(targets); + } + } + + // Target factory methods + + public static Target CreateTarget (XPathNavigator topic, LinkType2 type) { + if (topic == null) throw new ArgumentNullException("topic"); + + bool isApiTarget = (bool)topic.Evaluate("boolean(apidata)"); + + Target target; + if (isApiTarget) { + target = CreateApiTarget(topic, type); + } else { + target = new Target(); + } + + if (target == null) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); + + target.id = (string)topic.Evaluate(topicIdExpression); + if (String.IsNullOrEmpty(target.id)) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); + + target.container = (string)topic.Evaluate(topicContainerExpression); + + target.file = (string)topic.Evaluate(topicFileExpression); + if (String.IsNullOrEmpty(target.file)) throw new XmlSchemaValidationException(String.Format("The target file '{0}' is not valid.", topic.BaseURI)); + + target.type = type; + + return(target); + } + + private static Target CreateApiTarget (XPathNavigator api, LinkType2 linkType) { + string subGroup = (string) api.Evaluate(apiGroupExpression); + if (subGroup == "namespace") { + return( CreateNamespaceTarget(api) ); + } else if (subGroup == "type") { + return (CreateTypeTarget(api, linkType)); + } else if (subGroup == "member") { + return (CreateMemberTarget(api)); + } else { + return (null); + } + + } + + private static NamespaceTarget CreateNamespaceTarget (XPathNavigator api) { + NamespaceTarget target = new NamespaceTarget(); + target.name = (string) api.Evaluate(apiNameExpression); + if (String.IsNullOrEmpty(target.name)) target.name = "(Default Namespace)"; + return(target); + } + + private static TypeTarget CreateTypeTarget (XPathNavigator api, LinkType2 linkType) { + string subgroup = (string)api.Evaluate(apiSubgroupExpression); + + TypeTarget target; + if (subgroup == "enumeration") { + target = CreateEnumerationTarget(api, linkType); + } else { + target = new TypeTarget(); + } + + target.name = (string) api.Evaluate(apiNameExpression); + + // containing namespace + XPathNavigator namespaceNode = api.SelectSingleNode(apiContainingNamespaceExpression); + target.containingNamespace = CreateNamespaceReference(namespaceNode); + + // containing type, if any + XPathNavigator typeNode = api.SelectSingleNode(apiContainingTypeExpression); + if (typeNode == null) { + target.containingType = null; + } else { + target.containingType = CreateSimpleTypeReference(typeNode); + } + + // templates + target.templates = GetTemplateNames(api); + + return(target); + } + + private static string[] GetTemplateNames (XPathNavigator api) { + List<string> templates = new List<string>(); + XPathNodeIterator templateNodes = api.Select(apiTemplatesExpression); + foreach (XPathNavigator templateNode in templateNodes) { + templates.Add((string)templateNode.Evaluate(templateNameExpression)); + } + return(templates.ToArray()); + } + + private static EnumerationTarget CreateEnumerationTarget (XPathNavigator api, LinkType2 linkType) { + + EnumerationTarget enumeration = new EnumerationTarget(); + + string typeId = (string) api.Evaluate(topicIdExpression); + string file = (string) api.Evaluate(topicFileExpression); + + // Create tar + List<MemberTarget> members = new List<MemberTarget>(); + XPathNodeIterator elementNodes = api.Select("elements/element"); + foreach (XPathNavigator elementNode in elementNodes) { + string memberId = elementNode.GetAttribute("api", String.Empty); + + // try to get name from attribute on element node + string memberName = elementNode.GetAttribute("name", String.Empty); + if (String.IsNullOrEmpty(memberName)) { + // if we can't do that, try to get the name by searching the file for the <api> element of that member + XPathNavigator memberApi = api.SelectSingleNode(String.Format("following-sibling::api[@id='{0}']", memberId)); + if (memberApi != null) { + memberName = (string) memberApi.Evaluate(apiNameExpression); + } else { + // if all else fails, get the name by parsing the identifier + string arguments; + string type; + TextReferenceUtilities.DecomposeMemberIdentifier(memberId, out type, out memberName, out arguments); + } + } + + MemberTarget member = new MemberTarget(); + member.id = memberId; // get Id from element + member.file = file; // get file from type file + member.type = linkType; + member.name = memberName; // get name from element + member.containingType = new SimpleTypeReference(typeId); // get containing type from this type + members.Add(member); + } + + enumeration.elements = members.ToArray(); + + return (enumeration); + } + + public static MemberTarget CreateMemberTarget (XPathNavigator api) { + + string subgroup = (string)api.Evaluate(apiSubgroupExpression); + + MemberTarget target; + if (subgroup == "method") { + target = CreateMethodTarget(api); + } else if (subgroup == "property") { + target = CreatePropertyTarget(api); + } else if (subgroup == "constructor") { + target = CreateConstructorTarget(api); + } else if (subgroup == "event") { + target = CreateEventTarget(api); + } else { + target = new MemberTarget(); + } + + target.name = (string) api.Evaluate(apiNameExpression); + target.containingType = CreateSimpleTypeReference( api.SelectSingleNode(apiContainingTypeExpression) ); + target.overload = (string) api.Evaluate(apiOverloadIdExpression); + + return (target); + } + + private static MethodTarget CreateMethodTarget (XPathNavigator api) { + MethodTarget target = new MethodTarget(); + target.parameters = CreateParameterList(api); + target.returnType = CreateReturnType(api); + + if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { + target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); + } + + target.templates = GetTemplateNames(api); + + return (target); + } + + private static PropertyTarget CreatePropertyTarget (XPathNavigator api) { + PropertyTarget target = new PropertyTarget(); + target.parameters = CreateParameterList(api); + target.returnType = CreateReturnType(api); + + if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { + target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); + } + + return (target); + } + + private static EventTarget CreateEventTarget (XPathNavigator api) { + EventTarget target = new EventTarget(); + if ((bool)api.Evaluate(apiIsExplicitImplementationExpression)) { + target.explicitlyImplements = CreateMemberReference(api.SelectSingleNode(apiImplementedMembersExpression)); + } + return (target); + } + + private static ConstructorTarget CreateConstructorTarget (XPathNavigator api) { + ConstructorTarget target = new ConstructorTarget(); + target.parameters = CreateParameterList(api); + return (target); + } + + private static Parameter[] CreateParameterList (XPathNavigator api) { + List<Parameter> parameters = new List<Parameter>(); + XPathNodeIterator parameterNodes = api.Select("parameters/parameter"); + foreach (XPathNavigator parameterNode in parameterNodes) { + string name = parameterNode.GetAttribute("name", String.Empty); + XPathNavigator type = parameterNode.SelectSingleNode("*[1]"); + Parameter parameter = new Parameter(name, CreateTypeReference(type)); + parameters.Add(parameter); + } + return (parameters.ToArray()); + } + + private static TypeReference CreateReturnType (XPathNavigator api) { + XPathNavigator returnTypeNode = api.SelectSingleNode("returns/*[1]"); + if (returnTypeNode == null) { + return (null); + } else { + return (CreateTypeReference(returnTypeNode)); + } + } + + // reference factory + + public static Reference CreateReference (XPathNavigator node) { + if (node == null) throw new ArgumentNullException("node"); + if (node.NodeType == XPathNodeType.Element) { + string tag = node.LocalName; + if (tag == "namespace") return(CreateNamespaceReference(node)); + if (tag == "member") return (CreateMemberReference(node)); + return (CreateTypeReference(node)); + } else { + return (null); + } + } + + public static NamespaceReference CreateNamespaceReference (XPathNavigator namespaceElement) { + if (namespaceElement == null) throw new ArgumentNullException("namespaceElement"); + string api = (string) namespaceElement.Evaluate(referenceApiExpression); + NamespaceReference reference = new NamespaceReference(api); + return(reference); + } + + public static TypeReference CreateTypeReference (XPathNavigator node) { + if (node == null) throw new ArgumentNullException("reference"); + string tag = node.LocalName; + if (tag == "type") { + bool isSpecialized = (bool)node.Evaluate("boolean(.//specialization)"); + if (isSpecialized) { + return (CreateSpecializedTypeReference(node)); + } else { + return (CreateSimpleTypeReference(node)); + } + } else if (tag == "arrayOf") { + string rankValue = node.GetAttribute("rank", String.Empty); + XPathNavigator elementNode = node.SelectSingleNode("*[1]"); + return (new ArrayTypeReference(CreateTypeReference(elementNode), Convert.ToInt32(rankValue))); + } else if (tag == "referenceTo") { + XPathNavigator referedToNode = node.SelectSingleNode("*[1]"); + return (new ReferenceTypeReference(CreateTypeReference(referedToNode))); + } else if (tag == "pointerTo") { + XPathNavigator pointedToNode = node.SelectSingleNode("*[1]"); + return (new PointerTypeReference(CreateTypeReference(pointedToNode))); + } else if (tag == "template") { + string nameValue = node.GetAttribute("name", String.Empty); + string indexValue = node.GetAttribute("index", String.Empty); + string apiValue = node.GetAttribute("api", String.Empty); + if ((!String.IsNullOrEmpty(apiValue)) && (!String.IsNullOrEmpty(indexValue))) { + return (new IndexedTemplateTypeReference(apiValue, Convert.ToInt32(indexValue))); + } else { + return (new NamedTemplateTypeReference(nameValue)); + } + } + + throw new InvalidOperationException(String.Format("INVALID '{0}'", tag)); + + } + + public static SimpleTypeReference CreateSimpleTypeReference (XPathNavigator node) { + if (node == null) throw new ArgumentNullException("node"); + string api = node.GetAttribute("api", String.Empty); + SimpleTypeReference reference = new SimpleTypeReference(api); + return(reference); + } + + + private static SpecializedTypeReference CreateSpecializedTypeReference (XPathNavigator node) { + Stack<Specialization> specializations = new Stack<Specialization>(); + XPathNavigator typeNode = node.Clone(); + while (typeNode != null) { + specializations.Push(CreateSpecialization(typeNode)); + typeNode = typeNode.SelectSingleNode("type"); + } + SpecializedTypeReference reference = new SpecializedTypeReference(specializations.ToArray()); + return (reference); + } + + private static Specialization CreateSpecialization (XPathNavigator node) { + SimpleTypeReference template = CreateSimpleTypeReference(node); + + List<TypeReference> arguments = new List<TypeReference>(); + XPathNodeIterator specializationNodes = node.Select("specialization/*"); + foreach (XPathNavigator specializationNode in specializationNodes) { + arguments.Add(CreateTypeReference(specializationNode)); + } + + Specialization specialization = new Specialization(template, arguments.ToArray()); + return(specialization); + } + + + public static MemberReference CreateMemberReference (XPathNavigator node) { + string api = node.GetAttribute("api", String.Empty); + SimpleMemberReference member = new SimpleMemberReference(api); + + bool isSpecialized = (bool)node.Evaluate("boolean(./type//specialization)"); + if (isSpecialized) { + XPathNavigator typeNode = node.SelectSingleNode("type"); + SpecializedTypeReference type = CreateSpecializedTypeReference(typeNode); + return( new SpecializedMemberReference(member, type) ); + } else { + return(member); + } + + } + + } + + // ***** Logic for constructing references from code entity reference strings ***** + // Anything that depends on the specific form the ID strings lives here + + public static class TextReferenceUtilities { + + public static Reference CreateReference (string api) { + if (String.IsNullOrEmpty(api)) throw new ArgumentException("api"); + + Reference reference = null; + + char start = api[0]; + if (start == 'N') { + reference = CreateNamespaceReference(api); + } else if (start == 'T') { + reference = CreateTypeReference(api); + } else { + //Console.WriteLine("Creating member reference"); + reference = CreateMemberReference(api); + } + + if (reference == null) { + return (new InvalidReference(api)); + } else { + return (reference); + } + + } + + public static NamespaceReference CreateNamespaceReference (string api) { + if (ValidNamespace.IsMatch(api)) { + return( new NamespaceReference(api) ); + } else { + return(null); + } + } + + public static TypeReference CreateTypeReference (string api) { + if (ValidSimpleType.IsMatch(api)) { + // this is a reference to a "normal" simple type + return (CreateSimpleTypeReference(api)); + } else if (ValidSpecializedType.IsMatch(api)) { + // this is a reference to a specialized type + return (CreateSpecializedTypeReference(api)); + } else if (ValidDecoratedType.IsMatch(api)) { + // this is a reference to a type that is decorated or is a template + // process array, reference, and pointer decorations + char lastCharacter = api[api.Length-1]; + if (lastCharacter == ']') { + // arrays + int lastBracketPosition = api.LastIndexOf('['); + int rank = api.Length - lastBracketPosition - 1; + string elementApi = api.Substring(0, lastBracketPosition); + TypeReference elementReference = CreateTypeReference(elementApi); + return( new ArrayTypeReference(elementReference, rank) ); + } else if (lastCharacter == '@') { + // references + string referedApi = api.Substring(0, api.Length - 1); + TypeReference referedReference = CreateTypeReference(referedApi); + return (new ReferenceTypeReference(referedReference)); + } else if (lastCharacter == '*') { + // pointers + string pointedApi = api.Substring(0, api.Length - 1); + TypeReference pointedReference = CreateTypeReference(pointedApi); + return (new PointerTypeReference(pointedReference)); + } + + // process templates + if (api.StartsWith("T:``")) { + int position = Convert.ToInt32(api.Substring(4)); + return (new NamedTemplateTypeReference("UMP")); + } else if (api.StartsWith("T:`")) { + int position = Convert.ToInt32(api.Substring(3)); + if (genericTypeContext == null) { + return (new NamedTemplateTypeReference("UTP")); + } else { + return (new IndexedTemplateTypeReference(genericTypeContext.Id, position)); + } + } + + // we shouldn't get here, because one of those test should have been satisfied if the regex matched + throw new InvalidOperationException("Could not parse valid type expression"); + + } else { + return (null); + } + } + + private static SimpleTypeReference CreateSimpleTypeReference (string api) { + return (new SimpleTypeReference(api)); + } + + private static SpecializedTypeReference CreateSpecializedTypeReference (string api) { + + List<Specialization> specializations = new List<Specialization>(); + + string text = String.Copy(api); + + // at the moment we are only handling one specialization; need to iterate + + int specializationStart = text.IndexOf('{'); + int specializationEnd = FindMatchingEndBracket(text, specializationStart); + string list = text.Substring(specializationStart + 1, specializationEnd - specializationStart - 1); + string[] types = SeperateTypes(list); + string template = text.Substring(0, specializationStart) + String.Format("`{0}", types.Length); + + + SimpleTypeReference templateReference = CreateSimpleTypeReference(template); + TypeReference[] argumentReferences = new TypeReference[types.Length]; + for (int i = 0; i < types.Length; i++) { + argumentReferences[i] = CreateTypeReference(types[i]); + } + Specialization specialization = new Specialization(templateReference, argumentReferences); + + specializations.Add(specialization); + + // end iteration + + return (new SpecializedTypeReference(specializations.ToArray())); + } + + private static Regex tr = new Regex(@"^(M:([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+\.[_a-zA-Z0-9]+(``\d+)?(\((((([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+)|(`\d+)|(``\d+))(@|\*|(\[\]))*,)*((([_a-zA-Z0-9]+\.)*[_a-zA-Z0-9]+)|(`\d+)|(``\d+))(@|\*|(\[\]))*\))?)$", RegexOptions.Compiled); + + public static MemberReference CreateMemberReference (string api) { + //Console.WriteLine("Testing"); + //Console.WriteLine(tr.ToString()); + //Console.WriteLine(api); + //Console.WriteLine(tr.IsMatch(api)); + //Console.WriteLine("Tested"); + if (ValidSimpleMember.IsMatch(api)) { + //Console.WriteLine("Is valid simple member"); + // this is just a normal member of a simple type + return (new SimpleMemberReference(api)); + } else if (ValidSpecializedMember.IsMatch(api)) { + //Console.WriteLine("Is valid specialized member"); + //Console.WriteLine("cer = {0}", api); + // this is a member of a specialized type; we need to extract: + // (1) the underlying specialized type, (2) the member name, (3) the arguments + //Console.WriteLine("Extracting data"); + + // seperate the member prefix + int colonPosition = api.IndexOf(':'); + string prefix = api.Substring(0, colonPosition); + string text = api.Substring(colonPosition + 1); + + // get the arguments + string arguments = String.Empty; + int startParenthesisPosition = text.IndexOf('('); + if (startParenthesisPosition > 0) { + int endParenthesisPosition = text.LastIndexOf(')'); + arguments = text.Substring(startParenthesisPosition + 1, endParenthesisPosition - startParenthesisPosition - 1); + text = text.Substring(0, startParenthesisPosition); + } + + // seperate the type and member name + int lastDotPosition; + int firstHashPosition = text.IndexOf('#'); + if (firstHashPosition > 0) { + // if this is an EII, the boundry is at the last dot before the hash + lastDotPosition = text.LastIndexOf('.', firstHashPosition); + } else { + // otherwise, the boundry is at the last dot + lastDotPosition = text.LastIndexOf('.'); + } + string name = text.Substring(lastDotPosition+1); + text = text.Substring(0, lastDotPosition); + //Console.WriteLine("type = {0}", "T:" + text); + + // text now contains a specialized generic type; use it to create a reference + SpecializedTypeReference type = CreateSpecializedTypeReference("T:" + text); + + // If there are no arguments... + // we simply create a reference to a member whoose identifier we construct in the specialized type + if (String.IsNullOrEmpty(arguments)) { + string typeId = type.Specializations[type.Specializations.Length - 1].TemplateType.Id; + string memberId = String.Format("{0}:{1}.{2}", prefix, typeId.Substring(2), name); + SimpleMemberReference member = new SimpleMemberReference(memberId); + return (new SpecializedMemberReference(member, type)); + } + + // If there are arguments... life is not so simple. We can't be sure we can identify the + // corresponding member of the template type because any particular type that appears in + // the argument might have come from the template or it might have come from the specialization. + // We need to create a special kind of reference to handle this situation. + //Console.WriteLine("Specialized member with arguments '{0}'", api); + string[] parameterTypeCers = SeperateTypes(arguments); + TypeReference[] parameterTypes = new TypeReference[parameterTypeCers.Length]; + for (int i = 0; i < parameterTypeCers.Length; i++) { + parameterTypes[i] = CreateTypeReference(parameterTypeCers[i]); + } + return (new SpecializedMemberWithParametersReference(prefix, type, name, parameterTypes)); + + } else { + //Console.WriteLine("No match"); + return (null); + //throw new InvalidOperationException(String.Format("Invalid member '{0}'", api)); + } + + } + + // Template context logic + + private static SimpleTypeReference genericTypeContext = null; + + private static SimpleMemberReference genericMemberContext = null; + + public static void SetGenericContext (string cer) { + //Console.WriteLine("context = {0}", cer); + + // re-set the context + genericTypeContext = null; + genericMemberContext = null; + + // get the new context + Reference context = CreateReference(cer); + if (context == null) return; + + // if it is a type context, set it to be the type context + SimpleTypeReference typeContext = context as SimpleTypeReference; + if (typeContext != null) { + genericTypeContext = typeContext; + return; + } + + // if it is a member context, set it to be the member context and use it to obtain a type context, too + SimpleMemberReference memberContext = context as SimpleMemberReference; + if (memberContext != null) { + genericMemberContext = memberContext; + + string typeId, memberName, arguments; + DecomposeMemberIdentifier(memberContext.Id, out typeId, out memberName, out arguments); + genericTypeContext = CreateSimpleTypeReference(typeId); + return; + } + + } + + public static SimpleTypeReference GenericContext { + get { + return (genericTypeContext); + } + } + + // Code entity reference validation logic + + // iterate -> specializedTypePattern -> decoratedTypePattern -> decoratedTypeListPattern + // to get a patterns that enforce the contents of specialization brackets + + static TextReferenceUtilities () { + + string namePattern = @"[_a-zA-Z0-9]+"; + + // namespace patterns + + string namespacePattern = String.Format(@"({0}\.)*({0})?", namePattern); + + string optionalNamespacePattern = String.Format(@"({0}\.)*", namePattern); + + // type patterns + + string simpleTypePattern = String.Format(@"{0}({1}(`\d+)?\.)*{1}(`\d+)?", optionalNamespacePattern, namePattern); + + string specializedTypePattern = String.Format(@"({0}(\{{.+\}})?\.)*{0}(\{{.+\}})?", namePattern); + + string baseTypePattern = String.Format(@"({0})|({1})", simpleTypePattern, specializedTypePattern); + + string decoratedTypePattern = String.Format(@"(({0})|(`\d+)|(``\d+))(@|\*|(\[\]))*", specializedTypePattern); + + string decoratedTypeListPattern = String.Format(@"({0},)*{0}", decoratedTypePattern); + + string explicitInterfacePattern = String.Format(@"({0}(\{{[^\.]+\}})?#)*", namePattern); + + // members of non-specialized types + + string simpleFieldPattern = String.Format(@"{0}\.{1}", simpleTypePattern, namePattern); + + string simpleEventPattern = String.Format(@"{0}\.{1}{2}", simpleTypePattern, explicitInterfacePattern, namePattern); + + string simplePropertyPattern = String.Format(@"{0}\.{1}{2}(\({3}\))?", simpleTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern); + + string simpleMethodPattern = String.Format(@"{0}\.{1}{2}(``\d+)?(\({3}\))?(~{4})?", simpleTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern, decoratedTypePattern); + + string simpleConstructorPattern = String.Format(@"{0}\.#ctor(\({1}\))?", simpleTypePattern, decoratedTypeListPattern); + + string simpleOverloadPattern = String.Format(@"{0}\.{1}{2}", simpleTypePattern, explicitInterfacePattern, namePattern); + + string simpleConstructorOverloadPattern = String.Format(@"{0}\.#ctor", simpleTypePattern); + + // members of specialized types + + string specializedFieldPattern = String.Format(@"{0}\.{1}", specializedTypePattern, namePattern); + + string specializedEventPattern = String.Format(@"{0}\.{1}{2}", specializedTypePattern, explicitInterfacePattern, namePattern); + + string specializedPropertyPattern = String.Format(@"{0}\.{1}{2}(\({3}\))?", specializedTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern); + + string specializedMethodPattern = String.Format(@"{0}\.{1}{2}(``\d+)?(\({3}\))?(~{4})?", specializedTypePattern, explicitInterfacePattern, namePattern, decoratedTypeListPattern, decoratedTypePattern); + + string specializedOverloadPattern = String.Format(@"{0}\.{1}{2}", specializedTypePattern, explicitInterfacePattern, namePattern); + + // create regexes using this patterns + + ValidNamespace = new Regex( String.Format(@"^N:{0}$", namespacePattern), RegexOptions.Compiled ); + + ValidSimpleType = new Regex(String.Format(@"^T:{0}$", simpleTypePattern), RegexOptions.Compiled); + + ValidDecoratedType = new Regex(String.Format(@"^T:{0}$", decoratedTypePattern), RegexOptions.Compiled); + + ValidSpecializedType = new Regex(String.Format(@"^T:{0}$", specializedTypePattern), RegexOptions.Compiled); + + ValidSimpleMember = new Regex(String.Format(@"^((M:{0})|(M:{1})|(P:{2})|(F:{3})|(E:{4})|(Overload:{5})|(Overload:{6}))$", simpleMethodPattern, simpleConstructorPattern, simplePropertyPattern, simpleFieldPattern, simpleEventPattern, simpleOverloadPattern, simpleConstructorOverloadPattern)); + + ValidSpecializedMember = new Regex(String.Format(@"^((M:{0})|(P:{1})|(F:{2})|(E:{3})|(Overload:{4}))$", specializedMethodPattern, specializedPropertyPattern, specializedFieldPattern, specializedEventPattern, specializedOverloadPattern)); + + ValidTest = new Regex(String.Format(@"^M:{0}\.{1}$", simpleTypePattern, namePattern)); + + } + + private static Regex ValidNamespace; + + private static Regex ValidSimpleType; + + private static Regex ValidDecoratedType; + + private static Regex ValidSpecializedType; + + private static Regex ValidSimpleMember; + + private static Regex ValidSpecializedMember; + + private static Regex ValidTest; + + // Code entity reference string manipulation utilities + + internal static string[] SeperateTypes (string typelist) { + List<string> types = new List<string>(); + + int start = 0; + int specializationCount = 0; + for (int index = 0; index < typelist.Length; index++) { + switch (typelist[index]) { + case '{': + case '[': + specializationCount++; + break; + case '}': + case ']': + specializationCount--; + break; + case ',': + if (specializationCount == 0) { + types.Add("T:" + typelist.Substring(start, index - start)); + start = index + 1; + } + break; + } + } + types.Add("T:" + typelist.Substring(start)); + return (types.ToArray()); + } + + internal static void DecomposeMemberIdentifier (string memberCer, out string typeCer, out string memberName, out string arguments) { + + // drop the member prefix + int colonPosition = memberCer.IndexOf(':'); + string text = memberCer.Substring(colonPosition + 1); + + // get the arguments + arguments = String.Empty; + int startParenthesisPosition = text.IndexOf('('); + if (startParenthesisPosition > 0) { + int endParenthesisPosition = text.LastIndexOf(')'); + arguments = text.Substring(startParenthesisPosition + 1, endParenthesisPosition - startParenthesisPosition - 1); + text = text.Substring(0, startParenthesisPosition); + } + + // seperate the type and member name + int lastDotPosition; + int firstHashPosition = text.IndexOf('#'); + if (firstHashPosition > 0) { + // if this is an EII, the boundry is at the last dot before the hash + lastDotPosition = text.LastIndexOf('.', firstHashPosition); + } else { + // otherwise, the boundry is at the last dot + lastDotPosition = text.LastIndexOf('.'); + } + + memberName = text.Substring(lastDotPosition + 1); + typeCer = "T:" + text.Substring(0, lastDotPosition); + } + + private static int FindMatchingEndBracket (string text, int position) { + + if (text == null) throw new ArgumentNullException("text"); + if ((position < 0) || (position >= text.Length)) throw new ArgumentOutOfRangeException("position", String.Format("The position {0} is not within the given text string.", position)); + if (text[position] != '{') throw new InvalidOperationException(String.Format("Position {0} of the string '{1}' does not contain and ending curly bracket.", position, text)); + + int count = 1; + for (int index = position + 1; index < text.Length; index++) { + if (text[index] == '{') { + count++; + } else if (text[index] == '}') { + count--; + } + + if (count == 0) return (index); + } + + throw new FormatException("No opening brace matches the closing brace."); + + } + + // Writing link text for unresolved simple references + + internal static void WriteNamespaceReference (NamespaceReference space, DisplayOptions options, XmlWriter writer) { + writer.WriteString(space.Id.Substring(2)); + } + + internal static void WriteSimpleTypeReference (SimpleTypeReference type, DisplayOptions options, XmlWriter writer) { + + // this logic won't correctly deal with nested types, but type cer strings simply don't include that + // infomation, so this is out best guess under the assumption of a non-nested type + + string cer = type.Id; + + // get the name + string name; + int lastDotPosition = cer.LastIndexOf('.'); + if (lastDotPosition > 0) { + // usually, the name will start after the last dot + name = cer.Substring(lastDotPosition + 1); + } else { + // but if there is no dot, this is a type in the default namespace and the name is everything after the colon + name = cer.Substring(2); + } + + // remove any generic tics from the name + int tickPosition = name.IndexOf('`'); + if (tickPosition > 0) name = name.Substring(0, tickPosition); + + if ((options & DisplayOptions.ShowContainer) > 0) { + // work out namespace + } + + writer.WriteString(name); + + if ((options & DisplayOptions.ShowTemplates) > 0) { + // work out templates + } + + } + + internal static void WriteSimpleMemberReference (SimpleMemberReference member, DisplayOptions options, XmlWriter writer, LinkTextResolver resolver) { + + string cer = member.Id; + + string typeCer, memberName, arguments; + DecomposeMemberIdentifier(cer, out typeCer, out memberName, out arguments); + + if ((options & DisplayOptions.ShowContainer) > 0) { + SimpleTypeReference type = CreateSimpleTypeReference(typeCer); + WriteSimpleTypeReference(type, options & ~DisplayOptions.ShowContainer, writer); + } + + // change this so that we deal with EII names correctly, too + writer.WriteString(memberName); + + if ((options & DisplayOptions.ShowParameters) > 0) { + string[] parameterTypeCers; + if (String.IsNullOrEmpty(arguments)) { + Parameter[] parameters = new Parameter[0]; + resolver.WriteMethodParameters(parameters, writer); + } else { + parameterTypeCers = SeperateTypes(arguments); + Parameter[] parameters = new Parameter[parameterTypeCers.Length]; + for (int i = 0; i < parameterTypeCers.Length; i++) { + TypeReference parameterType = CreateTypeReference(parameterTypeCers[i]); + if (parameterType == null) { + parameterType = new NamedTemplateTypeReference("UAT"); + } + parameters[i] = new Parameter(String.Empty, parameterType); + } + resolver.WriteMethodParameters(parameters, writer); + } + } + + } + + } + + // ***** Link text writing logic ***** + + public class LinkTextResolver { + + public LinkTextResolver (TargetCollection targets) { + this.targets = targets; + } + + private TargetCollection targets; + + public void WriteTarget (Target target, DisplayOptions options, XmlWriter writer) { + if (target == null) throw new ArgumentNullException("target"); + if (writer == null) throw new ArgumentNullException("writer"); + + NamespaceTarget space = target as NamespaceTarget; + if (space != null) { + WriteNamespaceTarget(space, options, writer); + return; + } + + TypeTarget type = target as TypeTarget; + if (type != null) { + WriteTypeTarget(type, options, writer); + return; + } + + MemberTarget member = target as MemberTarget; + if (member != null) { + WriteMemberTarget(member, options, writer); + return; + } + + throw new InvalidOperationException(); + } + + public void WriteNamespaceTarget (NamespaceTarget space, DisplayOptions options, XmlWriter writer) { + if (space == null) throw new ArgumentNullException("target"); + if (writer == null) throw new ArgumentNullException("writer"); + writer.WriteString(space.Name); + } + + public void WriteTypeTarget (TypeTarget type, DisplayOptions options, XmlWriter writer) { + if (type == null) throw new ArgumentNullException("type"); + if (writer == null) throw new ArgumentNullException("writer"); + WriteTypeTarget(type, options, true, writer); + } + + private void WriteTypeTarget (TypeTarget type, DisplayOptions options, bool showOuterType, XmlWriter writer) { + + // write namespace, if containers are requested + if ((options & DisplayOptions.ShowContainer) > 0) { + WriteNamespace(type.Namespace, DisplayOptions.Default, writer); + WriteSeperator(writer); + } + + // write outer type, if one exists + if (showOuterType && (type.OuterType != null)) { + WriteSimpleType(type.OuterType, DisplayOptions.Default, writer); + WriteSeperator(writer); + } + + // write the type name + writer.WriteString(type.Name); + + // write if template parameters, if they exist and we are requested + if ((options & DisplayOptions.ShowTemplates) > 0) { + WriteTemplateParameters(type.Templates, writer); + } + } + + public void WriteMemberTarget (MemberTarget target, DisplayOptions options, XmlWriter writer) { + WriteMemberTarget(target, options, writer, null); + } + + + private void WriteMemberTarget (MemberTarget target, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + if (target == null) throw new ArgumentNullException("target"); + if (writer == null) throw new ArgumentNullException("writer"); + + if ((options & DisplayOptions.ShowContainer) > 0) { + TypeReference type = target.Type; + WriteType(type, options & ~DisplayOptions.ShowContainer, writer); + WriteSeperator(writer); + } + + // special logic for writing methods + MethodTarget method = target as MethodTarget; + if (method != null) { + WriteMethod(method, options, writer, dictionary); + return; + } + + // special logic for writing properties + PropertyTarget property = target as PropertyTarget; + if (property != null) { + WriteProperty(property, options, writer); + return; + } + + // special logic for writing constructors + ConstructorTarget constructor = target as ConstructorTarget; + if (constructor != null) { + WriteConstructor(constructor, options, writer); + return; + } + + // special logic for writing events + EventTarget trigger = target as EventTarget; + if (trigger != null) { + WriteEvent(trigger, options, writer); + return; + } + + // by default, just write name + writer.WriteString(target.Name); + } + + public void WriteReference (Reference reference, DisplayOptions options, XmlWriter writer) { + if (reference == null) throw new ArgumentNullException("reference"); + if (writer == null) throw new ArgumentNullException("writer"); + + NamespaceReference space = reference as NamespaceReference; + if (space != null) { + WriteNamespace(space, options, writer); + return; + } + + TypeReference type = reference as TypeReference; + if (type != null) { + WriteType(type, options, writer); + return; + } + + MemberReference member = reference as MemberReference; + if (member != null) { + WriteMember(member, options, writer); + return; + } + + InvalidReference invalid = reference as InvalidReference; + if (invalid != null) { + WriteInvalid(invalid, options, writer); + return; + } + + throw new InvalidOperationException(); + } + + public void WriteNamespace (NamespaceReference spaceReference, DisplayOptions options, XmlWriter writer) { + if (spaceReference == null) throw new ArgumentNullException("spaceReference"); + if (writer == null) throw new ArgumentNullException("writer"); + + NamespaceTarget spaceTarget = spaceReference.Resolve(targets) as NamespaceTarget; + if (spaceTarget != null) { + WriteNamespaceTarget(spaceTarget, options, writer); + } else { + TextReferenceUtilities.WriteNamespaceReference(spaceReference, options, writer); + } + } + + public void WriteType (TypeReference type, DisplayOptions options, XmlWriter writer) { + WriteType(type, options, writer, null); + } + + private void WriteType (TypeReference type, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + + if (type == null) throw new ArgumentNullException("type"); + if (writer == null) throw new ArgumentNullException("writer"); + + //Console.WriteLine("type {0}", type.GetType().FullName); + + SimpleTypeReference simple = type as SimpleTypeReference; + if (simple != null) { + WriteSimpleType(simple, options, writer); + return; + } + + SpecializedTypeReference specialized = type as SpecializedTypeReference; + if (specialized != null) { + WriteSpecializedType(specialized, options, writer); + return; + } + + ArrayTypeReference array = type as ArrayTypeReference; + if (array != null) { + WriteArrayType(array, options, writer, dictionary); + return; + } + + ReferenceTypeReference reference = type as ReferenceTypeReference; + if (reference != null) { + WriteReferenceType(reference, options, writer, dictionary); + return; + } + + PointerTypeReference pointer = type as PointerTypeReference; + if (pointer != null) { + WritePointerType(pointer, options, writer, dictionary); + return; + } + + TemplateTypeReference template = type as TemplateTypeReference; + if (template != null) { + WriteTemplateType(template, options, writer, dictionary); + return; + } + + throw new InvalidOperationException("Unknown type reference type"); + + } + + public void WriteSimpleType (SimpleTypeReference simple, DisplayOptions options, XmlWriter writer) { + WriteSimpleType(simple, options, true, writer); + } + + private void WriteSimpleType (SimpleTypeReference simple, DisplayOptions options, bool showOuterType, XmlWriter writer) { + TypeTarget type = simple.Resolve(targets) as TypeTarget; + if (type != null) { + WriteTypeTarget(type, options, showOuterType, writer); + } else { + TextReferenceUtilities.WriteSimpleTypeReference(simple, options, writer); + } + } + + private static void WriteTemplateParameters (string[] templates, XmlWriter writer) { + + if (templates.Length == 0) return; + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("<"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString("(Of "); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("<"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("("); + writer.WriteEndElement(); + writer.WriteEndElement(); + + for (int i = 0; i < templates.Length; i++) { + if (i > 0) writer.WriteString(", "); + writer.WriteString(templates[i]); + } + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString(">"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString(")"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString(">"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString(")"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + private void WriteSpecializedType (SpecializedTypeReference special, DisplayOptions options, XmlWriter writer) { + + Specialization[] specializations = special.Specializations; + for (int i = 0; i < specializations.Length; i++) { + if (i == 0) { + WriteSpecialization(specializations[0], options, writer); + } else { + WriteSeperator(writer); + WriteSpecialization(specializations[i], options & ~DisplayOptions.ShowContainer, writer); + } + + } + + } + + private void WriteSpecialization (Specialization specialization, DisplayOptions options, XmlWriter writer) { + // write the type itself (without outer types, because those will be written be other calls to this routine) + WriteSimpleType(specialization.TemplateType, (options & ~DisplayOptions.ShowTemplates), false, writer); + + // then write the template arguments + WriteTemplateArguments(specialization.Arguments, writer); + } + + private void WriteTemplateArguments (TypeReference[] specialization, XmlWriter writer) { + + if (specialization.Length == 0) return; + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("<"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString("(Of "); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("<"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("("); + writer.WriteEndElement(); + writer.WriteEndElement(); + + for (int i = 0; i < specialization.Length; i++) { + if (i > 0) writer.WriteString(", "); + WriteType(specialization[i], DisplayOptions.Default, writer); + } + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString(">"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString(")"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString(">"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString(")"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + private void WriteArrayType (ArrayTypeReference reference, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + // C++ array notation (left) + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("array<"); + writer.WriteEndElement(); + writer.WriteEndElement(); // end of <span class="languageSpecificText"> element + + // the underlying type + WriteType(reference.ElementType, options, writer, dictionary); + + // C++ array notation (right) + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + if (reference.Rank > 1) { + writer.WriteString("," + reference.Rank.ToString()); + } + writer.WriteString(">"); + writer.WriteEndElement(); + + // C# array notation + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("["); + for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } + writer.WriteString("]"); + writer.WriteEndElement(); + + // VB array notation + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString("("); + for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } + writer.WriteString(")"); + writer.WriteEndElement(); + + // neutral array notation + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("["); + for (int i = 1; i < reference.Rank; i++) { writer.WriteString(","); } + writer.WriteString("]"); + writer.WriteEndElement(); + writer.WriteEndElement(); // end of <span class="languageSpecificText"> element + } + + private static void WriteSeperator (XmlWriter writer) { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + // C# seperator + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("."); + writer.WriteEndElement(); + + // VB seperator + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString("."); + writer.WriteEndElement(); + + // C++ seperator + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("::"); + writer.WriteEndElement(); + + // neutral seperator + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("."); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + private void WritePointerType (PointerTypeReference pointer, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + WriteType(pointer.PointedToType, options, writer, dictionary); + writer.WriteString("*"); + } + + private void WriteReferenceType (ReferenceTypeReference reference, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + WriteType(reference.ReferedToType, options, writer, dictionary); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + // add % in C++ + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("%"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + private void WriteTemplateType (TemplateTypeReference template, DisplayOptions options, XmlWriter writer) { + WriteTemplateType(template, options, writer, null); + } + + private void WriteTemplateType (TemplateTypeReference template, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + + /* + Console.WriteLine("template type {0}", template.GetType().FullName); + if (dictionary != null) { + foreach (IndexedTemplateTypeReference it in dictionary.Keys) { + Console.WriteLine("index = {0}, api = {1} ({3}) -> {2}", it.Index, it.TemplateId, dictionary[it].GetType().FullName, it.GetHashCode()); + } + } + */ + + // if we have the name, just write it + NamedTemplateTypeReference namedTemplate = template as NamedTemplateTypeReference; + if (namedTemplate != null) { + writer.WriteString(namedTemplate.Name); + return; + } + + IndexedTemplateTypeReference indexedTemplate = template as IndexedTemplateTypeReference; + if (indexedTemplate != null) { + //Console.WriteLine("index = {0}, api = {1} ({2})", indexedTemplate.Index, indexedTemplate.TemplateId, indexedTemplate.GetHashCode()); + //if ((dictionary != null) && (!dictionary.ContainsKey(indexedTemplate))) Console.WriteLine("not in dictionary"); + if ((dictionary != null) && (dictionary.ContainsKey(indexedTemplate))) { + //Console.WriteLine("Substituted {0}", dictionary[indexedTemplate].GetType().FullName); + WriteType(dictionary[indexedTemplate], options, writer); + } else { + //Console.WriteLine("Getting name for id='{0}' and index={1}", indexedTemplate.TemplateId, indexedTemplate.Index); + writer.WriteString(GetTemplateName(indexedTemplate.TemplateId, indexedTemplate.Index)); + } + return; + } + + TypeTemplateTypeReference typeTemplate = template as TypeTemplateTypeReference; + if (typeTemplate != null) { + + TypeReference value = null; + if (dictionary != null) { + IndexedTemplateTypeReference key = new IndexedTemplateTypeReference(typeTemplate.TemplateType.Id, typeTemplate.Position); + if (dictionary.ContainsKey(key)) value = dictionary[key]; + } + + if (value == null) { + writer.WriteString(GetTypeTemplateName(typeTemplate.TemplateType, typeTemplate.Position)); + } else { + WriteType(value, options, writer); + } + + return; + + } + + throw new InvalidOperationException(); + } + + private string GetTemplateName (string templateId, int position) { + Target target = targets[templateId]; + + if (target == null) { + return ("UTT"); + } else { + + TypeTarget type = target as TypeTarget; + if (type != null) { + string[] templates = type.Templates; + if (templates.Length > position) { + return(templates[position]); + } else { + return ("UTT"); + } + } + + MethodTarget method = target as MethodTarget; + if (method != null) { + string[] templates = method.Templates; + if (templates.Length > position) { + return (templates[position]); + } else { + return ("UTT"); + } + } + + return ("UTT"); + } + } + + private string GetTypeTemplateName (SimpleTypeReference type, int position) { + TypeTarget target = type.Resolve(targets) as TypeTarget; + if (target != null) { + string[] templates = target.Templates; + if (templates.Length > position) { + return (templates[position]); + } else if (target.OuterType != null) { + return (GetTypeTemplateName(target.OuterType, position)); + } else { + return ("UTT"); + } + } else { + throw new InvalidOperationException(String.Format("Unknown type reference '{0}'", type.Id)); + } + } + + public void WriteMember (MemberReference member, DisplayOptions options, XmlWriter writer) { + + if (member == null) throw new ArgumentNullException("member"); + if (writer == null) throw new ArgumentNullException("writer"); + + SimpleMemberReference simple = member as SimpleMemberReference; + if (simple != null) { + WriteSimpleMember(simple, options, writer); + return; + } + + SpecializedMemberReference special = member as SpecializedMemberReference; + if (special != null) { + WriteSpecializedMember(special, options, writer); + return; + } + + SpecializedMemberWithParametersReference ugly = member as SpecializedMemberWithParametersReference; + if (ugly != null) { + WriteSpecializedMemberWithParameters(ugly, options, writer); + return; + } + + throw new InvalidOperationException(); + + } + + private void WriteSpecializedMember (SpecializedMemberReference member, DisplayOptions options, XmlWriter writer) { + + if ((options & DisplayOptions.ShowContainer) > 0) { + WriteType(member.SpecializedType, options & ~DisplayOptions.ShowContainer, writer); + WriteSeperator(writer); + } + + Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary = member.SpecializedType.GetSpecializationDictionary(); + WriteSimpleMember(member.TemplateMember, options & ~DisplayOptions.ShowContainer, writer, dictionary); + + } + + private void WriteSimpleMember (SimpleMemberReference member, DisplayOptions options, XmlWriter writer) { + WriteSimpleMember(member, options, writer, null); + } + + private void WriteSimpleMember (SimpleMemberReference member, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + MemberTarget target = member.Resolve(targets) as MemberTarget; + if (target != null) { + WriteMemberTarget(target, options, writer, dictionary); + } else { + TextReferenceUtilities.WriteSimpleMemberReference(member, options, writer, this); + } + + } + + private void WriteProcedureName (ProcedureTarget target, DisplayOptions options, XmlWriter writer) { + MemberReference implements = target.ExplicitlyImplements; + if (implements == null) { + writer.WriteString(target.Name); + } else { + WriteMember(implements, DisplayOptions.ShowContainer, writer); + } + } + + private void WriteMethod (MethodTarget target, DisplayOptions options, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + + WriteProcedureName(target, options, writer); + + if ((options & DisplayOptions.ShowTemplates) > 0) { + WriteTemplateParameters(target.Templates, writer); + } + + if ((options & DisplayOptions.ShowParameters) > 0) { + Parameter[] parameters = target.Parameters; + WriteMethodParameters(parameters, writer, dictionary); + } + + } + + internal void WriteMethodParameters (Parameter[] parameters, XmlWriter writer) { + WriteMethodParameters(parameters, writer, null); + } + + + private void WriteMethodParameters (Parameter[] parameters, XmlWriter writer, Dictionary<IndexedTemplateTypeReference, TypeReference> dictionary) { + if (parameters.Length > 0) { + writer.WriteString("("); + + // show parameters + // we need to deal with type template substitutions! + for (int i = 0; i < parameters.Length; i++) { + if (i > 0) writer.WriteString(", "); + WriteType(parameters[i].Type, DisplayOptions.Default, writer, dictionary); + } + + writer.WriteString(")"); + } else { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + // when there are no parameters, VB shows no parenthesis + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("()"); + writer.WriteEndElement(); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("()"); + writer.WriteEndElement(); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("()"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } + + private void WriteProperty (PropertyTarget target, DisplayOptions options, XmlWriter writer) { + + WriteProcedureName(target, options, writer); + + if ((options & DisplayOptions.ShowParameters) > 0) { + + Parameter[] parameters = target.Parameters; + + // VB only shows parenthesis when there are parameters + if (parameters.Length > 0) { + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("["); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString("("); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("["); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString("("); + writer.WriteEndElement(); + writer.WriteEndElement(); + + // show parameters + // we need to deal with type template substitutions! + for (int i = 0; i < parameters.Length; i++) { + if (i > 0) writer.WriteString(", "); + WriteType(parameters[i].Type, DisplayOptions.Default, writer); + } + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "languageSpecificText"); + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cs"); + writer.WriteString("]"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "vb"); + writer.WriteString(")"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "cpp"); + writer.WriteString("]"); + writer.WriteEndElement(); + + writer.WriteStartElement("span"); + writer.WriteAttributeString("class", "nu"); + writer.WriteString(")"); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + + } + + } + + private void WriteEvent (EventTarget trigger, DisplayOptions options, XmlWriter writer) { + WriteProcedureName(trigger, options, writer); + } + + private void WriteConstructor (ConstructorTarget constructor, DisplayOptions options, XmlWriter writer) { + + + WriteType(constructor.Type, options & ~DisplayOptions.ShowContainer, writer); + + if ((options & DisplayOptions.ShowParameters) > 0) { + Parameter[] parameters = constructor.Parameters; + WriteMethodParameters(parameters, writer); + } + + } + + private void WriteSpecializedMemberWithParameters (SpecializedMemberWithParametersReference ugly, DisplayOptions options, XmlWriter writer) { + + if ((options & DisplayOptions.ShowContainer) > 0) { + WriteSpecializedType(ugly.SpecializedType, options & ~DisplayOptions.ShowContainer, writer); + WriteSeperator(writer); + } + + writer.WriteString(ugly.MemberName); + + if ((options & DisplayOptions.ShowParameters) > 0) { + + writer.WriteString("("); + + TypeReference[] parameterTypes = ugly.ParameterTypes; + for (int i = 0; i < parameterTypes.Length; i++) { + //Console.WriteLine("i = {0}, type = {1}", i, parameterTypes[i].GetType().FullName); + if (i > 0) writer.WriteString(", "); + WriteType(parameterTypes[i], DisplayOptions.Default, writer); + } + + writer.WriteString(")"); + } + + } + + private static void WriteInvalid (InvalidReference invalid, DisplayOptions options, XmlWriter writer) { + writer.WriteString("[" + invalid.Id + "]"); + } + + } + + [Flags] + public enum DisplayOptions { + + ShowContainer = 1, + ShowTemplates = 2, + ShowParameters = 4, + + Default = 6 + + } + + public enum LinkType2 { + None, + Self, + Local, + Index, + LocalOrIndex, + Msdn + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Targets.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Targets.cs new file mode 100644 index 0000000..00c17f2 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/Targets.cs @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Text; + +using System.Xml; +using System.Xml.XPath; + +namespace BuildComponents { + + public partial class Target { + + internal string id; + + internal string container; + + internal string file; + + public string Id { + get { + return (id); + } + } + + public string Container { + get { + return (container); + } + } + + public string File { + get { + return (file); + } + } + + } + + // Namespace + + public partial class NamespaceTarget : Target { + + internal string name; + + public string Name { + get { + return (name); + } + } + + } + + // Type + + public partial class TypeTarget : Target { + + // apidata + + protected string name; + + protected string subgroup; + + // containers + + protected NamespaceReference containingNamespace; + + protected SimpleTypeReference containingType; + + protected string containingAssembly; + + // templates + + protected string[] templates; + + // typedata + + private object visibility; + + private bool isAbstract; + + private bool isSealed; + + private bool isSerializable; + + // family + + private SimpleTypeReference parentType; + + // other + + public string Name { + get { + return (name); + } + } + + public NamespaceReference Namespace { + get { + return (containingNamespace); + } + } + + public SimpleTypeReference OuterType { + get { + return (containingType); + } + } + + public string[] Templates { + get { + return (templates); + } + } + + } + + // Construction of targets from Xml + + public partial class Target { + + public static Target Create (XmlReader api) { + + string id = api.GetAttribute("id"); + + Target target = null; + api.ReadToFollowing("apidata"); + string group = api.GetAttribute("group"); + switch (group) { + case "namespace": + target = NamespaceTarget.Create(api); + break; + } + + target.id = id; + + return (target); + } + + protected static XPathExpression apiNameExpression = XPathExpression.Compile("string(apidata/@name)"); + + + } + + public partial class NamespaceTarget { + + public static NamespaceTarget Create (XmlReader apidata) { + NamespaceTarget target = new NamespaceTarget(); + string name = apidata.GetAttribute("name"); + + // This is not locale-independent. + if (String.IsNullOrEmpty(target.name)) name = "(Default Namespace)"; + + target.name = name; + + return (target); + } + + public static NamespaceTarget Create (XPathNavigator api) { + + NamespaceTarget target = new NamespaceTarget(); + target.name = (string)api.Evaluate(apiNameExpression); + + // This is not locale-independent. + if (String.IsNullOrEmpty(target.name)) target.name = "(Default Namespace)"; + + return (target); + } + + } + + + public partial class TypeTarget { + + public static TypeTarget Create (XmlReader api) { + + api.ReadToFollowing("apidata"); + string subgroup = api.GetAttribute("subgroup"); + + api.ReadToFollowing("typedata"); + string visibilityValue = api.GetAttribute("visibility"); + string abstractValue = api.GetAttribute("abstract"); + string sealedValue = api.GetAttribute("sealed"); + string serializableValue = api.GetAttribute("serealizable"); + + api.ReadToFollowing("library"); + string containingAssemblyValue = api.GetAttribute("assembly"); + + api.ReadToFollowing("namespace"); + NamespaceReference containingNamespace = NamespaceReference.Create(api); + + TypeTarget target = new TypeTarget(); + target.containingAssembly = containingAssemblyValue; + target.containingNamespace = containingNamespace; + + return (target); + + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs new file mode 100644 index 0000000..81fc811 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.XPath; +using System.IO; + +// still have problems with spaces + +namespace Microsoft.Ddue.Tools { + + public class TaskGrabberComponent : BuildComponent { + + private XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable()); + + private XPathExpression valueQuery = null; + private XPathExpression keyQuery = null; + + private string xpathFromConfig = string.Empty; + + private Dictionary<string, List<string>> bKeywordMap = new Dictionary<string, List<string>>(); + + private Dictionary<string, string> index = new Dictionary<string, string>(); + + // what to copy + private List<CopySet> copySets = new List<CopySet>(); + + public TaskGrabberComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) + { + XPathNavigator keywordsNode = configuration.SelectSingleNode("keywords"); + if (keywordsNode == null) + return; + + string filespec = keywordsNode.GetAttribute("files", String.Empty); + filespec = Environment.ExpandEnvironmentVariables(filespec); + string keywordXPath = keywordsNode.GetAttribute("keyword", String.Empty); + string topicXPath = keywordsNode.GetAttribute("topic", String.Empty); + if (String.IsNullOrEmpty(keywordXPath) || String.IsNullOrEmpty(topicXPath) || String.IsNullOrEmpty(filespec)) + return; + + xpathFromConfig = keywordXPath; + + string[] keywordFiles = null; + if (File.Exists(filespec)) + { + // we're loading a single file + AddBKeywords(filespec, topicXPath, keywordXPath); + } + else + { + // must be loading a set of files + if (Directory.Exists(filespec)) + { + // if they specified a directory, transform all the files in the directory + keywordFiles = Directory.GetFiles(filespec); + } + else + { + // it's not a file or a directory, maybe it's a path with wildcards + string directoryPath = Path.GetDirectoryName(filespec); + string filePath = Path.GetFileName(filespec); + keywordFiles = Directory.GetFiles(directoryPath, filePath); + } + foreach (string file in keywordFiles) + { + // load targets from each file + AddBKeywords(file, topicXPath, keywordXPath); + } + } + + // set up the context + // put in a default entry for ddue + nsManager.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/6"); + // look for context nodes, which could override the default + XPathNodeIterator contextNodes = configuration.Select("context"); + foreach (XPathNavigator contextNode in contextNodes) + { + string prefix = contextNode.GetAttribute("prefix", String.Empty); + string uri = contextNode.GetAttribute("name", String.Empty); + nsManager.AddNamespace(prefix, uri); + } + + + // set up the index of source files + XPathNodeIterator sourceNodes = configuration.Select("source"); + foreach (XPathNavigator sourceNode in sourceNodes) + { + string valueXPath = sourceNode.GetAttribute("value", String.Empty); + string keyXPath = sourceNode.GetAttribute("key", String.Empty); + if (String.IsNullOrEmpty(valueXPath) || String.IsNullOrEmpty(keyXPath)) + { + WriteMessage(MessageLevel.Error, "@key and @value must be set in the 'source' node."); + return; + } + + keyQuery = XPathExpression.Compile(keyXPath); + valueQuery = XPathExpression.Compile(valueXPath); + + // search the data directories for entries + XPathNodeIterator dataNodes = sourceNode.Select("data"); + foreach (XPathNavigator dataNode in dataNodes) + { + string dataFiles = dataNode.GetAttribute("files", String.Empty); + dataFiles = Environment.ExpandEnvironmentVariables(dataFiles); + if ((dataFiles == null) || (dataFiles.Length == 0)) throw new ConfigurationErrorsException("When instantiating a CopyFromDirectory component, you must specify a directory path using the files attribute."); + WriteMessage(MessageLevel.Info, String.Format("Searching for files that match '{0}'.", dataFiles)); + int fileCount = ParseDocuments(dataFiles); + WriteMessage(MessageLevel.Info, String.Format("Found {0} files.", fileCount)); + } + WriteMessage(MessageLevel.Info, String.Format("Indexed {0} elements.", index.Count)); + + } + + // get the copy commands + XPathNodeIterator copyNodes = configuration.Select("copy"); + foreach (XPathNavigator copyNode in copyNodes) + { + string sourceXPath = copyNode.GetAttribute("source", String.Empty); + string targetXPath = copyNode.GetAttribute("target", String.Empty); + if (String.IsNullOrEmpty(sourceXPath) || String.IsNullOrEmpty(targetXPath)) + { + WriteMessage(MessageLevel.Error, "@source and @target must be set in the 'copy' node."); + return; + } + + copySets.Add(new CopySet(sourceXPath, targetXPath, nsManager)); + } + + } + + private string currentKey = string.Empty; + + public override void Apply (XmlDocument document, string key) { + currentKey = key; + + XPathNavigator targetDoc = document.CreateNavigator(); + foreach (CopySet copySet in copySets) + { + XPathExpression targetExpression = copySet.GetTargetExpression(targetDoc, key); + + // get the target nodes in the document + XPathNavigator targetNode = targetDoc.SelectSingleNode(targetExpression); + while (targetNode != null) + { + string targetId = targetNode.Value; + int pound = (string.IsNullOrEmpty(targetId)) ? -1 : targetId.IndexOf("#"); + string bkeyword = (pound == -1) ? "" : targetId.Substring(pound + 1); + if (bkeyword == "") + { + WriteMessage(MessageLevel.Warn, string.Format("Invalid id '{0}' in topic '{1}'.", targetId, currentKey)); + // delete this target and get the next target node + targetNode.DeleteSelf(); + targetNode = targetDoc.SelectSingleNode(targetExpression); + continue; + } + + List<string> idList; + if (!bKeywordMap.TryGetValue(bkeyword, out idList)) + { + WriteMessage(MessageLevel.Warn, string.Format("B-keyword not found '{0}' in topic '{1}'.", targetId, currentKey)); + // delete this target and get the next target node + targetNode.DeleteSelf(); + targetNode = targetDoc.SelectSingleNode(targetExpression); + continue; + } + if (idList.Count > 1) + Console.Write(""); + + // create a 'tasks' node to replace the target + XPathNavigator tasksNode = document.CreateElement("tasks").CreateNavigator(); + tasksNode.CreateAttribute(string.Empty, "bkeyword", string.Empty, bkeyword); + foreach (string topicId in idList) + { + //create a task node for this source topic + XPathNavigator taskNode = document.CreateElement("task").CreateNavigator(); + taskNode.CreateAttribute(string.Empty, "topicId", string.Empty, topicId); + + // get the source document for the topic id + string filepath; + if (!index.TryGetValue(topicId, out filepath)) + { + WriteMessage(MessageLevel.Warn, string.Format("No file found for topicId '{0}' for B-keyword '{1}'. Source topic: '{2}'.", topicId, bkeyword, currentKey)); + continue; + } + + XPathNavigator sourceDoc = new XPathDocument(filepath).CreateNavigator(); + XPathNavigator sourceNode = sourceDoc.SelectSingleNode(valueQuery); + + if (sourceNode == null) continue; + XPathNodeIterator sources = sourceNode.Select(copySet.SourceExpression); + + // append the source nodes to the target node + if (sources.Count > 0) + { + foreach (XPathNavigator source in sources) + taskNode.AppendChild(source); + } + tasksNode.AppendChild(taskNode); + } + targetNode.ReplaceSelf(tasksNode); + // get the next target node + targetNode = targetDoc.SelectSingleNode(targetExpression); + } + } + } + + public int ParseDocuments(string wildcardPath) + { + string directoryPart = Path.GetDirectoryName(wildcardPath); + if (String.IsNullOrEmpty(directoryPart)) directoryPart = Environment.CurrentDirectory; + directoryPart = Path.GetFullPath(directoryPart); + string filePart = Path.GetFileName(wildcardPath); + string[] files = Directory.GetFiles(directoryPart, filePart); + foreach (string file in files) + ParseDocument(file); + return (files.Length); + } + + private void ParseDocument(string file) + { + try + { + XPathDocument document = new XPathDocument(file); + + // set context for the xpath expression + valueQuery.SetContext(nsManager); + keyQuery.SetContext(nsManager); + + XPathNodeIterator valueNodes = document.CreateNavigator().Select(valueQuery); + foreach (XPathNavigator valueNode in valueNodes) + { + XPathNavigator keyNode = valueNode.SelectSingleNode(keyQuery); + if (keyNode == null) continue; + string key = keyNode.Value; + + // log multiple occurences of a single id + if (index.ContainsKey(key)) + { + WriteMessage(MessageLevel.Warn, String.Format("Entries for the key '{0}' occur in both '{1}' and '{2}'. The first entry will be used.", key, index[key], file)); + } + else + { + index[key] = file; + } + + } + } + catch (Exception e) + { + WriteMessage(MessageLevel.Error, e.Message); + } + } + + private XPathDocument GetDocument(string identifier) + { + string file = index[identifier]; + XPathDocument document = new XPathDocument(file); + return (document); + } + + private void AddBKeywords(string file, string topicXPath, string keywordXPath) + { + XPathDocument document = new XPathDocument(file); + XPathNodeIterator targetNodes = document.CreateNavigator().Select(topicXPath); + foreach (XPathNavigator targetNode in targetNodes) + { + string topicId = targetNode.GetAttribute("id", string.Empty); + if (string.IsNullOrEmpty(topicId)) + continue; + foreach (XPathNavigator keywordNode in targetNode.Select(keywordXPath)) + { + string keyword = keywordNode.Value; + if (string.IsNullOrEmpty(keyword)) + continue; + AddValueToListDictionary(bKeywordMap, keyword, topicId); + } + } + } + + public static void AddValueToListDictionary<K, V>(Dictionary<K, List<V>> dict, K key, V value) + { + List<V> list; + try + { + if (!dict.TryGetValue(key, out list)) + { + list = new List<V>(); + dict.Add(key, list); + } + list.Add(value); + } + catch (Exception e) + { + Console.WriteLine("Exception adding to dictionary {0}", key); + Console.WriteLine(e); + throw e; + } + } + + } + + internal class CopySet + { + + public CopySet(string sourceXPath, string targetXPath, XmlNamespaceManager nsMgr) + : this(null, sourceXPath, targetXPath, nsMgr) + { + } + + public CopySet(IndexedFileCache cache, string sourceXPath, string targXPath, XmlNamespaceManager nsMgr) + { + this.fileCache = cache; + this.namespaceMgr = nsMgr; + source = XPathExpression.Compile(sourceXPath); + source.SetContext(nsMgr); + if (targXPath.Contains("{0}")) + { + targetXPath = targXPath; + } + else + { + target = XPathExpression.Compile(targXPath); + target.SetContext(nsMgr); + } + } + + private IndexedFileCache fileCache; + public IndexedFileCache FileCache + { + get + { + return (fileCache); + } + } + + private XPathExpression source; + public XPathExpression SourceExpression + { + get + { + return (source); + } + } + + private XmlNamespaceManager namespaceMgr; + + private string targetXPath = string.Empty; + private XPathExpression target = null; + public XPathExpression GetTargetExpression(XPathNavigator targetDoc, string key) + { + if (target == null) + { + XPathExpression keyedTargetExpression = targetDoc.Compile(string.Format(targetXPath, key)); + keyedTargetExpression.SetContext(namespaceMgr); + return keyedTargetExpression; + } + + return target; + } + + + } + + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TransformComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TransformComponent.cs new file mode 100644 index 0000000..82b3241 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TransformComponent.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Xml; +using System.Xml.XPath; +using System.Xml.Xsl; + +namespace Microsoft.Ddue.Tools { + + public class TransformComponent : BuildComponent { + + public TransformComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + // load the transforms + XPathNodeIterator transform_nodes = configuration.Select("transform"); + foreach (XPathNavigator transform_node in transform_nodes) { + + // load the transform + string file = transform_node.GetAttribute("file", String.Empty); + if (String.IsNullOrEmpty(file)) WriteMessage(MessageLevel.Error, "Each transform element must specify a file attribute."); + file = Environment.ExpandEnvironmentVariables(file); + + Transform transform = null; + try { + transform = new Transform(file); + } catch (IOException e) { + WriteMessage(MessageLevel.Error, String.Format("The transform file '{0}' could not be loaded. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("The transform file '{0}' is not a valid XML file. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } catch (XsltException e) { + WriteMessage(MessageLevel.Error, String.Format("The XSL transform '{0}' contains an error. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); + } + + + transforms.Add(transform); + + + // load any arguments + XPathNodeIterator argument_nodes = transform_node.Select("argument"); + foreach (XPathNavigator argument_node in argument_nodes) { + string key = argument_node.GetAttribute("key", String.Empty); + if ((key == null) || (key.Length == 0)) WriteMessage(MessageLevel.Error, "When creating a transform argument, you must specify a key using the key attribute"); + + // set "expand-value" attribute to true to expand environment variables embedded in "value". + string expand_attr = argument_node.GetAttribute("expand-value", String.Empty); + bool expand_value = String.IsNullOrEmpty(expand_attr) ? false : Convert.ToBoolean(expand_attr); + + string value = argument_node.GetAttribute("value", String.Empty); + if ((value != null) && (value.Length > 0)) { + transform.Arguments.AddParam(key, String.Empty, expand_value ? Environment.ExpandEnvironmentVariables(value) : value); + } + else { + transform.Arguments.AddParam(key, String.Empty, argument_node.Clone()); + } + } + + } + + } + + // the stored transforms + + private List<Transform> transforms = new List<Transform>(); + + // the action of the component + + public override void Apply (XmlDocument document, string key) { + + // iterate over transforms + foreach (Transform transform in transforms) { + + // add the key as a parameter to the arguments + transform.Arguments.RemoveParam("key", String.Empty); + transform.Arguments.AddParam("key", String.Empty, key); + + // create a buffer into which output can be written + using (MemoryStream buffer = new MemoryStream()) { + + + // do the transform, routing output to the buffer + XmlWriterSettings settings = transform.Xslt.OutputSettings; + XmlWriter writer = XmlWriter.Create(buffer, settings); + try { + transform.Xslt.Transform(document, transform.Arguments, writer); + } catch (XsltException e) { + WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message)); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message)); + } finally { + writer.Close(); + } + + // replace the document by the contents of the buffer + buffer.Seek(0, SeekOrigin.Begin); + + // some settings to ensure that we don't try to go get, parse, and validate using any referenced schemas or DTDs + XmlReaderSettings readerSettings = new XmlReaderSettings(); + readerSettings.ProhibitDtd = false; + readerSettings.XmlResolver = null; + + XmlReader reader = XmlReader.Create(buffer, readerSettings); + try { + document.Load(reader); + } catch (XmlException e) { + WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message)); + } finally { + reader.Close(); + } + + } + } + + } + + } + + + // a represenataion of a transform action + + internal class Transform { + + public Transform (string file) { + // The transforms presumably come from a trusted source, so there's no reason + // not to enable scripting and the document function. The latter is used to read topic + // info files for the conceptual WebDocs build. + xslt.Load(file, new XsltSettings(true, true), new XmlUrlResolver()); + } + + private XslCompiledTransform xslt = new XslCompiledTransform(); + + private XsltArgumentList arguments = new XsltArgumentList(); + + public XslCompiledTransform Xslt { + get { + return(xslt); + } + } + + public XsltArgumentList Arguments { + get { + return(arguments); + } + } + + } + +} diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ValidateComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ValidateComponent.cs new file mode 100644 index 0000000..afd6286 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/ValidateComponent.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.XPath; + +namespace Microsoft.Ddue.Tools { + + public class ValidateComponent : BuildComponent { + + private XmlSchemaSet schemas = new XmlSchemaSet(); + + public ValidateComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + + XPathNodeIterator schema_nodes = configuration.Select("schema"); + foreach (XPathNavigator schema_node in schema_nodes) { + string file = schema_node.GetAttribute("file", String.Empty); + schemas.Add(null, file); + } + + } + + public override void Apply (XmlDocument document, string key) { + + // set the validate schema + document.Schemas = schemas; + + // create a validation handler + ValidationEventHandler handler = new ValidationEventHandler(LogValidationError); + + // validate the document + document.Validate(handler); + + } + + private void LogValidationError (Object o, ValidationEventArgs e) { + string message = String.Format("ValidationError: {0}", e.Message); + WriteMessage(MessageLevel.Warn, message); + } + + } + +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/WdxResolveConceptualLinksComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/WdxResolveConceptualLinksComponent.cs new file mode 100644 index 0000000..d404d28 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/WdxResolveConceptualLinksComponent.cs @@ -0,0 +1,296 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.XPath; +using System.Text.RegularExpressions; +using System.Web; + + +namespace Microsoft.Ddue.Tools { + + /// <summary> + /// WdxResolveConceptualLinksComponent handles conceptual links where the target is a GUID. All other kinds + /// of targets are considered invalid. NOTE: This is an experimental version for WebDocs. + /// </summary> + public class WdxResolveConceptualLinksComponent : BuildComponent { + + const int MaxTargetCacheSize = 1000; + + TargetSetList targetSets = new TargetSetList(); + XPathExpression baseUrl; + string invalidLinkFormat = "<span class='nolink'>{1}</span>"; + string brokenLinkFormat = "<a href='http://msdn2.microsoft.com/en-us/library/{0}'>{1}</a>"; + string defaultFormat = "<a href='{0}'>{1}</a>"; + + public WdxResolveConceptualLinksComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) { + //System.Diagnostics.Debugger.Break(); + + string av; // temporary attribute values + + // base-url is an xpath expression that is used to lookup the url that relative links need to be + // relative to. The lookup is done against the current document. This attribute is needed only if + // one of the targets uses relative links that are not in the current directory. If not specified, + // the target uses the url from the meta data unchanged. + av = configuration.GetAttribute("base-url", String.Empty); + if (!String.IsNullOrEmpty(av)) + baseUrl = CompileXPathExpression(av); + + // invalid-link-format specifies a string format to be used for invalid (target is not a valid GUID) + // links. The string formatter is called with parameter {0} set to the target attribute of the link, + // and parameter {1} set to the tag content from the source document. A reasonable default is used + // if the value is not specified. + av = configuration.GetAttribute("invalid-link-format", String.Empty); + if (!String.IsNullOrEmpty(av)) + invalidLinkFormat = av; + + // broken-link-format specifies a string format to be used for broken links (target GUID lookup + // failed in all targets). The string formatter is called with parameter {0} set to the target attribute + // of the link, and parameter {1} set to the tag content from the source document. A reasonable + // default is used if the value is not specified. + av = configuration.GetAttribute("broken-link-format", String.Empty); + if (!String.IsNullOrEmpty(av)) + brokenLinkFormat = av; + + // <targets> specifies a lookup solution for each possible set of link targets. Each target must + // specify either a lookup file or error condition (invalid-link, broken-link). + XPathNodeIterator targetsNodes = configuration.Select("targets"); + foreach (XPathNavigator targetsNode in targetsNodes) { + + // lookup-file specifies the meta data file used for looking up URLs and titles. The value will + // go through environment variable expansion during setup and then through string formatting after + // computing the url, with parameter {0} set to the link target GUID. This attribute is required. + string lookupFile = targetsNode.GetAttribute("lookup-file", String.Empty); + if (string.IsNullOrEmpty(lookupFile)) + WriteMessage(MessageLevel.Error, "Each target must have a lookup-file attribute."); + else + lookupFile = Environment.ExpandEnvironmentVariables(lookupFile); + + // check-file-exists if specified ensures that the link target file exists; if it doesn't exist we + // take the broken link action. + string checkFileExists = targetsNode.GetAttribute("check-file-exists", String.Empty); + if (!String.IsNullOrEmpty(checkFileExists)) + checkFileExists = Environment.ExpandEnvironmentVariables(checkFileExists); + + // url is an xpath expression that is used to lookup the link url in the meta data file. The default + // value can be used to lookup the url in .cmp.xml files. + av = targetsNode.GetAttribute("url", String.Empty); + XPathExpression url = String.IsNullOrEmpty(av) ? + XPathExpression.Compile("concat(/metadata/topic/@id,'.htm')") : + XPathExpression.Compile(av); + + // text is an xpath expression that is used to lookup the link text in the meta data file. The default + // value can be used to lookup the link text in .cmp.xml files. + av = targetsNode.GetAttribute("text", string.Empty); + XPathExpression text = String.IsNullOrEmpty(av) ? + XPathExpression.Compile("string(/metadata/topic/title)") : + XPathExpression.Compile(av); + + // relative-url determines whether the links from this target set are relative to the current page + // and need to be adjusted to the base directory. + av = targetsNode.GetAttribute("relative-url", String.Empty); + bool relativeUrl = String.IsNullOrEmpty(av) ? false : Convert.ToBoolean(av);; + + // format is a format string that is used to generate the link. Parameter {0} is the url; + // parameter {1} is the text. The default creates a standard HTML link. + string format = targetsNode.GetAttribute("format", String.Empty); + if (String.IsNullOrEmpty(format)) + format = defaultFormat; + + // target looks OK + targetSets.Add(new TargetSet(lookupFile, checkFileExists, url, text, relativeUrl, format)); + } + + WriteMessage(MessageLevel.Info, String.Format("Collected {0} targets directories.", targetSets.Count)); + } + + public override void Apply(XmlDocument document, string key) { + // Run through all conceptual nodes, make sure the target is a valid GUID, attempt to resolve the target + // to a link using one of the TargetSet definitions, and then replace the node with the result. Errors + // will be dealt with as follows: 1) bad target (GUID) -> output invalidLinkFormat; 2) broken link (cannot + // resolve target or the URL is empty) -> output brokenLinkFormat; 3) missing text -> just delete the node + // and don't output anything (presumably there's no point in creating a link that nobody can see). In all + // three cases we'll log the problem as a warning. + string docBaseUrl = baseUrl == null ? null : BuildComponentUtilities.EvalXPathExpr(document, baseUrl, "key", key); + XPathNavigator[] linkNodes = BuildComponentUtilities.ConvertNodeIteratorToArray(document.CreateNavigator().Select(conceptualLinks)); + foreach (XPathNavigator node in linkNodes) { + string targetGuid = node.GetAttribute("target", String.Empty); + string url = targetGuid; + string text = node.ToString(); + string format; + if (validGuid.IsMatch(url)) { + format = brokenLinkFormat; + Target t = targetSets.Lookup(targetGuid); + if (t == null) { + WriteMessage(MessageLevel.Warn, String.Format("Conceptual link not found in target sets; target={0}", targetGuid)); + } + else { + if (!String.IsNullOrEmpty(t.Url)) { + format = t.TargetSet.Format; + url = (docBaseUrl != null && t.TargetSet.RelativeUrl) ? + BuildComponentUtilities.GetRelativePath(t.Url, docBaseUrl) : t.Url; + if (!String.IsNullOrEmpty(t.Text)) + text = t.Text; + } + else + WriteMessage(MessageLevel.Warn, String.Format("Conceptual link found in target set, but meta data does not specify a url; target={0}", targetGuid)); + } + } + else + format = invalidLinkFormat; + + if (String.IsNullOrEmpty(text)) { + node.DeleteSelf(); + WriteMessage(MessageLevel.Warn, String.Format("Skipping conceptual link without text; target={0}", url)); + } + else { + node.OuterXml = String.Format(format, url, text); + } + } + } + + private static XPathExpression conceptualLinks = XPathExpression.Compile("//conceptualLink"); + private static Regex validGuid = new Regex(@"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", RegexOptions.Compiled); + + + #region HelperFunctions + + public XPathExpression CompileXPathExpression(string xpath) { + XPathExpression expression = null; + try { + expression = XPathExpression.Compile(xpath); + } + catch (ArgumentException e) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message)); + } + catch (XPathException e) { + WriteMessage(MessageLevel.Error, String.Format("'{0}' is not a valid XPath expression. The error message is: {1}", xpath, e.Message)); + } + return (expression); + } + + #endregion HelperFunctions + + + #region DataStructures + + // + // Internal data structures to support WdxResolveConceptualLinksComponent + // + + class TargetSet { + string lookupFile; + string checkFileExists; + XPathExpression url; + XPathExpression text; + + bool relativeUrl; + public bool RelativeUrl { + get { return relativeUrl; } + } + + string format; + public string Format { + get { return format; } + } + + public TargetSet(string lookupFile, string checkFileExists, XPathExpression url, XPathExpression text, bool relativeUrl, string format) { + if (lookupFile == null) + throw new ArgumentNullException("lookupFile"); + this.lookupFile = lookupFile; + + this.checkFileExists = checkFileExists; + + if (url == null) + throw new ArgumentNullException("url"); + this.url = url; + + if (text == null) + throw new ArgumentNullException("text"); + this.text = text; + + this.relativeUrl = relativeUrl; + + if (format == null) + throw new ArgumentNullException("format"); + this.format = format; + } + + public Target Lookup(string targetGuid) { + string lookupFilePathName = String.Format(lookupFile, targetGuid); + if (File.Exists(lookupFilePathName) && + (checkFileExists == null || File.Exists(String.Format(checkFileExists, targetGuid)))) { + XPathDocument document = new XPathDocument(lookupFilePathName); + return new Target(this, + (string)document.CreateNavigator().Evaluate(url), + (string)document.CreateNavigator().Evaluate(text)); + } + return null; + } + } + + class TargetSetList { + List<TargetSet> targetSets = new List<TargetSet>(); + Dictionary<string, Target> targetCache = new Dictionary<string, Target>(); + + public TargetSetList() { + } + + public void Add(TargetSet targetSet) { + if (targetSet == null) + throw new ArgumentNullException("targetSet"); + this.targetSets.Add(targetSet); + } + + public int Count { + get { return targetSets.Count; } + } + + public Target Lookup(string targetGuid) { + Target t = null; + if (!targetCache.TryGetValue(targetGuid, out t)) { + foreach (TargetSet ts in targetSets) { + t = ts.Lookup(targetGuid); + if (t != null) { + if (targetCache.Count >= MaxTargetCacheSize) + targetCache.Clear(); + targetCache.Add(targetGuid, t); + break; + } + } + } + return t; + } + } + + class Target { + TargetSet targetSet; + public TargetSet TargetSet { + get { return targetSet; } + } + + string url; + public string Url { + get { return url; } + } + + string text; + public string Text { + get { return text; } + } + + public Target(TargetSet targetSet, string url, string text) { + if (targetSet == null) + throw new ArgumentNullException("targetSet"); + this.targetSet = targetSet; + this.url = url; + this.text = text; + } + } + + #endregion DataStructures + } +}
\ No newline at end of file diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile new file mode 100644 index 0000000..29ec09b --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile @@ -0,0 +1,13 @@ +LIBRARY = ..\BuildAssembler\BuildAssembler.exe + +FLOW_COMPONENTS = IfThenComponent.cs ForEachComponent.cs CloneComponent.cs +DIAGNOSTIC_COMPONENTS = DisplayComponent.cs ValidateComponent.cs +PROCESSING_COMPONENTS = CopyFromFileComponent.cs CopyFromDirectoryComponent.cs SharedContentComponent.cs TransformComponent.cs SaveComponent.cs ResolveLinksComponent.cs MsdnService.cs + +COMPONENTS = $(FLOW_COMPONENTS) $(DIAGNOSTIC_COMPONENTS) $(PROCESSING_COMPONENTS) + +all: BuildComponents.dll + +BuildComponents.dll: *Component.cs CustomContext.cs MsdnService.cs $(LIBRARY) + csc /t:library /out:BuildComponents.dll *Component.cs CustomContext.cs MsdnService.cs /r:$(LIBRARY) + copy BuildComponents.dll ..\..\ProductionTools\BuildComponents diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile.inc b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile.inc new file mode 100644 index 0000000..32f7350 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile.inc @@ -0,0 +1,2 @@ +!INCLUDE $(INETROOT)\build\makefile.inc + diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/placefile b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/placefile new file mode 100644 index 0000000..bc01306 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/placefile @@ -0,0 +1 @@ +Microsoft.Ddue.Tools.BuildComponents.dll Manifold diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/plan.txt b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/plan.txt new file mode 100644 index 0000000..d72dc7a --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/plan.txt @@ -0,0 +1,62 @@ + +Flow Control +------------ + +<IfThen> + <if>xpath_format</if> + <then>component_list</then> + <else>component_list</else> +</IfThen> + +<ForEach> + <xpath>xpath_format</file> + component_list +</ForEach> + +<Clone> + <branch>component_list</branch>+ +</Clone> + +<Save> + <file>xpath_format</file> +</Save> + + + +Diagnostic +---------- + +<Validate> + <schema></schema> +</Validate> + + +<Display> + <xpath></xpath> +</Display> + + + +Processing +---------- + +<InsertComments> + <directory></directory> + <schema></schema> + <comment></comment> + <identifier></identifier> + <source></source> + <target></target> +</InsertComments> + +<InsertData> + <file>file_path</file> + <schema>file_path</schema> + <source>xpath_format</source> + <target>xpath_format</target> +</InsertData> + +<Transform> + <transform>file_path</transform> + <parameter name=" " value=" " />+ +</Transform> |