summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-09-20 21:18:59 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-09-21 08:06:22 -0700
commitbbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8 (patch)
treec91f66e642c4d26fca266e226b3f2765f546d700 /tools/Sandcastle/Source/BuildAssembler/BuildComponents
parent627014f0bbc3fd576277375e70f8391d150b0a67 (diff)
downloadDotNetOpenAuth-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')
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj153
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/BuildComponents.csproj.vspscc10
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/CloneComponent.cs41
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/CodeReference.cs115
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ComputeHashComponent.cs68
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFileComponent.cs143
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromFiles.cs92
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/CopyFromIndexComponent.cs674
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/DisplayComponent.cs43
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ExampleComponent.cs549
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ForEachComponent.cs101
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/HxfGeneratorComponent.cs127
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/IfThenComponent.cs82
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/IndexedFileCache.cs189
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent.cs333
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/IntellisenseComponent2.cs419
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs193
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Makefile.org13
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Microsoft.Ddue.Tools.BuildComponents.asmmeta1255
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnResolver.cs73
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/MsdnService.cs913
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/PlatformsComponent.cs679
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/AssemblyInfo.cs37
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.Designer.cs26
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Properties/Settings.settings5
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/References.cs345
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveArtLinksComponent.cs192
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveConceptualLinksComponent.cs445
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ResolveReferenceLinksComponent2.cs473
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs146
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/SharedContentComponent.cs377
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/SwitchComponent.cs73
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/SyntaxComponent.cs280
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/TargetCollection.cs2420
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/Targets.cs202
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/TaskGrabberComponent.cs368
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/TransformComponent.cs150
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/ValidateComponent.cs44
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/WdxResolveConceptualLinksComponent.cs296
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile13
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/makefile.inc2
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/placefile1
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/plan.txt62
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>