Skip to content

Commit

Permalink
Merge pull request #435 from kronic/fix_doc
Browse files Browse the repository at this point in the history
Fix documentation.
  • Loading branch information
mganss authored Oct 31, 2023
2 parents 2dae885 + decb2d9 commit 96f6c19
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 24 deletions.
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
generator.NamingProvider = new SubstituteNamingProvider(nameSubstituteMap);
}

generator.CommentLanguages.AddRange(commentLanguages);
generator.CommentLanguages.UnionWith(commentLanguages);

if (pclCompatible)
{
Expand Down
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator.Tests/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public static Assembly GenerateFiles(string name, IEnumerable<string> files, Gen
};

gen.CommentLanguages.Clear();
gen.CommentLanguages.AddRange(generatorPrototype.CommentLanguages);
gen.CommentLanguages.UnionWith(generatorPrototype.CommentLanguages);

output.Configuration = gen.Configuration;

Expand Down
184 changes: 184 additions & 0 deletions XmlSchemaClassGenerator.Tests/DocumentationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Schema;

using Xunit;

namespace XmlSchemaClassGenerator.Tests;

public sealed class DocumentationTests
{
static DocumentationTests() => Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

private static IEnumerable<string> ConvertXml(string xsd, Generator generatorPrototype)
{
var writer = new MemoryOutputWriter();

var gen = new Generator
{
OutputWriter = writer,
Version = new("Tests", "1.0.0.1"),
NamespaceProvider = generatorPrototype.NamespaceProvider,
//DataAnnotationMode = generatorPrototype.DataAnnotationMode,
GenerateDescriptionAttribute = generatorPrototype.GenerateDescriptionAttribute
};

var set = new XmlSchemaSet();

using (var stringReader = new StringReader(xsd))
{
var schema = XmlSchema.Read
(
stringReader,
(_, e) => throw new InvalidOperationException($"{e.Severity}: {e.Message}", e.Exception)
);

ArgumentNullException.ThrowIfNull(schema);
set.Add(schema);
}

gen.Generate(set);

return writer.Content;
}

[Fact]
public void TestSummaryDoc()
{
const string xsd = """
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:customs.ru:Envelope:ApplicationInf:1.0" elementFormDefault="qualified" version="1.0.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:app="urn:customs.ru:Envelope:ApplicationInf:1.0">
<xs:element name="ApplicationInf" type="app:ApplicationInfType"/>
<xs:complexType name="ApplicationInfType">
<xs:annotation>
<xs:documentation>Заголовок конверта.</xs:documentation>
<xs:documentation>Информация приложения</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="SoftKind" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Тип программного обеспечения</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SoftVersion" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Версия программного</xs:documentation>
<xs:documentation>обеспечения</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="MessageKind" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Тип сообщения</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
""";

var code = string.Join
(
"\r\n",
ConvertXml(xsd, new() {NamespacePrefix = "Test"})
.Single()
.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.TrimEntries)
);

Assert.Contains
(
"/// <summary>\r\n/// <para>Заголовок конверта.</para>\r\n/// <para>Информация приложения</para>\r\n/// </summary>",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"/// <summary>\r\n/// <para>Тип программного обеспечения</para>\r\n/// </summary>",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"/// <summary>\r\n/// <para>Версия программного</para>\r\n/// <para>обеспечения</para>\r\n/// </summary>",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"/// <summary>\r\n/// <para>Тип сообщения</para>\r\n/// </summary>",
code,
StringComparison.Ordinal
);
}

[Fact]
public void TestDescriptionAttributeValue()
{
const string xsd = """
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:customs.ru:Envelope:ApplicationInf:1.0" elementFormDefault="qualified" version="1.0.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:app="urn:customs.ru:Envelope:ApplicationInf:1.0">
<xs:element name="ApplicationInf" type="app:ApplicationInfType"/>
<xs:complexType name="ApplicationInfType">
<xs:annotation>
<xs:documentation>Заголовок конверта.</xs:documentation>
<xs:documentation>Информация приложения</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="SoftKind" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Тип программного обеспечения</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SoftVersion" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Версия программного</xs:documentation>
<xs:documentation>обеспечения</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="MessageKind" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>Тип сообщения</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
""";

var code = ConvertXml(xsd, new() {NamespacePrefix = "Test", GenerateDescriptionAttribute = true})
.Single();

Assert.Contains
(
"""[System.ComponentModel.DescriptionAttribute("Заголовок конверта. Информация приложения")]""",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"""[System.ComponentModel.DescriptionAttribute("Тип программного обеспечения")]""",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"""[System.ComponentModel.DescriptionAttribute("Версия программного обеспечения")]""",
code,
StringComparison.Ordinal
);

Assert.Contains
(
"""[System.ComponentModel.DescriptionAttribute("Тип сообщения")]""",
code,
StringComparison.Ordinal
);
}
}
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator.Tests/XmlTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private static IEnumerable<string> ConvertXml(string name, IEnumerable<string> x
};

gen.CommentLanguages.Clear();
gen.CommentLanguages.AddRange(generatorPrototype.CommentLanguages);
gen.CommentLanguages.UnionWith(generatorPrototype.CommentLanguages);

var set = new XmlSchemaSet();

Expand Down
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ public bool CompactTypeNames
set { _configuration.CompactTypeNames = value; }
}

public List<string> CommentLanguages
public HashSet<string> CommentLanguages
{
get { return _configuration.CommentLanguages; }
}
Expand Down
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator/GeneratorConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ public void WriteLog(string message)
/// <summary>
/// The language identifiers comments will be generated for, e.g. "en", "de-DE".
/// </summary>
public List<string> CommentLanguages { get; } = new List<string>();
public HashSet<string> CommentLanguages { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Create unique type names across all namespaces. See https://github.com/mganss/XmlSchemaClassGenerator/issues/240
Expand Down
2 changes: 1 addition & 1 deletion XmlSchemaClassGenerator/ModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ public static List<DocumentationModel> GetDocumentation(XmlSchemaAnnotated annot
return annotated.Annotation == null ? new List<DocumentationModel>()
: annotated.Annotation.Items.OfType<XmlSchemaDocumentation>()
.Where(d => d.Markup?.Length > 0)
.Select(d => d.Markup.Select(m => new DocumentationModel { Language = d.Language, Text = new XText($"{m.Name}: {m.InnerText}").ToString() }))
.Select(d => d.Markup.Select(m => new DocumentationModel { Language = d.Language, Text = m.Value }))
.SelectMany(d => d)
.Where(d => !string.IsNullOrEmpty(d.Text))
.ToList();
Expand Down
58 changes: 40 additions & 18 deletions XmlSchemaClassGenerator/TypeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using static System.Net.Mime.MediaTypeNames;

namespace XmlSchemaClassGenerator
{
Expand Down Expand Up @@ -1401,43 +1402,64 @@ protected CodeTypeReference NullableTypeRef(CodeTypeReference typeReference)
}
public static bool DisableComments { get; set; }

protected IEnumerable<CodeCommentStatement> GetComments(IList<DocumentationModel> docs)
protected IEnumerable<CodeCommentStatement> GetComments(IReadOnlyList<DocumentationModel> docs)
{
if (DisableComments || docs.Count == 0)
yield break;

yield return new CodeCommentStatement("<summary>", true);

foreach (var doc in docs
.Where(d => string.IsNullOrEmpty(d.Language) || Configuration.CommentLanguages.Exists(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase)))
.OrderBy(d => d.Language))
foreach (var doc in docs.Where
(
d => !string.IsNullOrWhiteSpace(d.Text)
&& (string.IsNullOrEmpty(d.Language)
|| Configuration.CommentLanguages.Count is 0
|| Configuration.CommentLanguages.Contains(d.Language)
|| Configuration.CommentLanguages
.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase)))
)
.OrderBy(d => d.Language))
{
var text = doc.Text;
var comment = $"<para{(string.IsNullOrEmpty(doc.Language) ? "" : $@" xml:lang=""{doc.Language}""")}>{CodeUtilities.NormalizeNewlines(text).Trim()}</para>";
yield return new CodeCommentStatement(comment, true);
var text = doc.Text;
var comment = $"<para{(string.IsNullOrEmpty(doc.Language) ? "" : $@" xml:lang=""{doc.Language}""")}>{CodeUtilities.NormalizeNewlines(text).Trim()}</para>";

yield return new(comment, true);
}

yield return new CodeCommentStatement("</summary>", true);
}

protected void AddDescription(CodeAttributeDeclarationCollection attributes, IEnumerable<DocumentationModel> docs)
protected void AddDescription(CodeAttributeDeclarationCollection attributes, IReadOnlyList<DocumentationModel> docs)
{
if (!Configuration.GenerateDescriptionAttribute || DisableComments || !docs.Any()) return;
if (!Configuration.GenerateDescriptionAttribute || DisableComments || docs.Count is 0) return;

var doc = GetSingleDoc(docs.Where(d => string.IsNullOrEmpty(d.Language) || Configuration.CommentLanguages.Exists(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase))));
var docText = GetSingleDoc(docs);

if (doc != null)
if (string.IsNullOrWhiteSpace(docText) is false)
{
var descriptionAttribute = AttributeDecl<DescriptionAttribute>(new CodeAttributeArgument(new CodePrimitiveExpression(Regex.Replace(doc.Text, @"\s+", " ").Trim())));
var descriptionAttribute = AttributeDecl<DescriptionAttribute>(new CodeAttributeArgument(new CodePrimitiveExpression(Regex.Replace(docText, @"\s+", " ").Trim())));
attributes.Add(descriptionAttribute);
}
}

private static DocumentationModel GetSingleDoc(IEnumerable<DocumentationModel> docs)
{
return docs.Count() == 1 ? docs.Single()
: docs.FirstOrDefault(d => string.IsNullOrEmpty(d.Language) || d.Language.StartsWith(English, StringComparison.OrdinalIgnoreCase))
?? docs.FirstOrDefault();
}
private string GetSingleDoc(IReadOnlyList<DocumentationModel> docs) => string.Join
(
" ",
docs.Where
(
d => !string.IsNullOrWhiteSpace(d.Text)
&& (string.IsNullOrEmpty(d.Language)
|| Configuration.CommentLanguages.Count is 0
|| Configuration.CommentLanguages.Contains(d.Language)
|| Configuration.CommentLanguages
.Any(l => d.Language.StartsWith(l, StringComparison.OrdinalIgnoreCase)))
)
.Where
(
d => string.IsNullOrEmpty(d.Language)
|| d.Language.StartsWith(English, StringComparison.OrdinalIgnoreCase)
)
.Select(x => x.Text)
);
}
}

0 comments on commit 96f6c19

Please sign in to comment.