Skip to content

Commit

Permalink
Refactoring of the Source Code Generator (#1141)
Browse files Browse the repository at this point in the history
* Refactoring of the Source Code Generator

- Moved the Svg_Model.cs file as SvgModel.cs into the main source.
- Restore the standard naming of the generated code files with "*.g.cs" suffix.
- Set the output directory of the generated codes to "Generated".
- Added the Generated directory to the main source codes.
- Moved the Svg.Custom project to Tests directory.

* Update runtests.yml

- NuGet/setup-nuget@v1 to NuGet/setup-nuget@v2
- Needed to eliminate the warning: Node.js 16 actions are deprecated.

* Update AvailableElementsGenerator.cs

Removed the comment out portions.
  • Loading branch information
paulushub committed Feb 3, 2024
1 parent 02518c4 commit a2c69b7
Show file tree
Hide file tree
Showing 14 changed files with 264 additions and 238 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/runtests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
with:
fetch-depth: 0 # needed for GitVersioning to work
- name: Setup NuGet
uses: NuGet/setup-nuget@v1
uses: NuGet/setup-nuget@v2
- name: Cache NuGet packages
uses: actions/cache@v4
id: cache
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
with:
fetch-depth: 0
- name: Setup NuGet
uses: NuGet/setup-nuget@v1
uses: NuGet/setup-nuget@v2
- name: Cache NuGet packages
uses: actions/cache@v4
id: cache
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,5 @@ Tests/W3CTestSuite/images
Tests/W3CTestSuite/png
Tests/W3CTestSuite/resources
Tests/W3CTestSuite/svg

Source/Generated/**/*.cs
235 changes: 9 additions & 226 deletions Generators/AvailableElementsGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace Svg.Generators
Expand All @@ -26,78 +25,6 @@ public class AvailableElementsGenerator : ISourceGenerator
DiagnosticSeverity.Error,
isEnabledByDefault: true);

#region Model

/// <summary>
/// The object model used to generate SvgElements descriptors.
/// </summary>
private const string ModelText = @"// <auto-generated />
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
namespace Svg
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class ElementFactoryAttribute : Attribute
{
}
internal enum DescriptorType
{
Property,
Event
}
internal interface ISvgPropertyDescriptor
{
DescriptorType DescriptorType { get; }
string AttributeName { get; }
string AttributeNamespace { get; }
TypeConverter Converter { get; }
Type Type { get; }
object GetValue(object component);
void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value);
}
internal class SvgPropertyDescriptor<T, TU> : ISvgPropertyDescriptor
{
public DescriptorType DescriptorType { get; }
public string AttributeName { get; }
public string AttributeNamespace { get; }
public TypeConverter Converter { get; }
public Type Type { get; } = typeof(TU);
private Func<T, TU> Getter { get; }
private Action<T, TU> Setter { get; }
public SvgPropertyDescriptor(DescriptorType descriptorType, string attributeName, string attributeNamespace, TypeConverter converter, Func<T, TU> getter, Action<T, TU> setter)
{
DescriptorType = descriptorType;
AttributeName = attributeName;
AttributeNamespace = attributeNamespace;
Converter = converter;
Getter = getter;
Setter = setter;
}
public object GetValue(object component)
{
return (object)Getter((T)component);
}
public void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (Converter != null)
{
Setter((T)component, (TU)Converter.ConvertFrom(context, culture, value));
}
}
}
}";
#endregion

/// <inheritdoc/>
public void Initialize(GeneratorInitializationContext context)
{
Expand All @@ -109,17 +36,13 @@ public void Initialize(GeneratorInitializationContext context)
/// <inheritdoc/>
public void Execute(GeneratorExecutionContext context)
{
// Add the ElementFactory model source to compilation.
context.AddSource("Svg_Model", SourceText.From(ModelText, Encoding.UTF8));

// Check is we have our SyntaxReceiver object used to filter compiled code.
if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
{
return;
}

var options = (context.Compilation as CSharpCompilation)?.SyntaxTrees[0].Options as CSharpParseOptions;
var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(ModelText, Encoding.UTF8), options));
var compilation = context.Compilation;

var elementFactoryAttribute = compilation.GetTypeByMetadataName("Svg.ElementFactoryAttribute");
if (elementFactoryAttribute is null)
Expand Down Expand Up @@ -367,7 +290,7 @@ internal virtual bool SetValue(string attributeName, ITypeDescriptorContext cont
}}
}}
");
context.AddSource($"Svg_SvgElement_Properties.cs", SourceText.From(source.ToString(), Encoding.UTF8));
context.AddSource($"Svg.SvgElement.Properties.g.cs", SourceText.From(source.ToString(), Encoding.UTF8));
source.Clear();

// Generate SvgElement derived classes with descriptor Properties dictionary.
Expand Down Expand Up @@ -456,7 +379,7 @@ internal override bool SetValue(string attributeName, ITypeDescriptorContext con
}}
}}
");
context.AddSource($"{namespaceElement.Replace('.', '_')}_{element.Symbol.Name}_Properties.cs", SourceText.From(source.ToString(), Encoding.UTF8));
context.AddSource($"{namespaceElement}.{element.Symbol.Name}.Properties.g.cs", SourceText.From(source.ToString(), Encoding.UTF8));
source.Clear();
}

Expand Down Expand Up @@ -492,7 +415,7 @@ internal static class SvgElements
}
}
");
context.AddSource($"Svg_SvgElements.cs", SourceText.From(source.ToString(), Encoding.UTF8));
context.AddSource($"Svg.SvgElements.g.cs", SourceText.From(source.ToString(), Encoding.UTF8));
source.Clear();

// Generate ElementFactory class.
Expand Down Expand Up @@ -586,7 +509,7 @@ internal partial class {classElementFactory}
}}
}}");

context.AddSource($"{namespaceElementFactory.Replace('.', '_')}_{elementFactorySymbol.Name}_ElementFactory.cs", SourceText.From(source.ToString(), Encoding.UTF8));
context.AddSource($"{namespaceElementFactory}.{elementFactorySymbol.Name}.ElementFactory.g.cs", SourceText.From(source.ToString(), Encoding.UTF8));
source.Clear();
}

Expand Down Expand Up @@ -755,6 +678,10 @@ private static IEnumerable<INamedTypeSymbol> GetBaseTypes(INamedTypeSymbol named
{
"System.String" => "System.ComponentModel.StringConverter",
"System.Single" => "System.ComponentModel.SingleConverter",
"System.Int16" => "System.ComponentModel.Int16Converter",
"System.Int32" => "System.ComponentModel.Int32Converter",
"System.Int64" => "System.ComponentModel.Int64Converter",
"System.Boolean" => "System.ComponentModel.BooleanConverter",
"System.Uri" => "System.UriTypeConverter",
_ => null
};
Expand Down Expand Up @@ -863,149 +790,5 @@ private static IEnumerable<Property> GetElementProperties(Compilation compilatio
}
}
}

/// <summary>
/// Symbol member type.
/// </summary>
private enum MemberType
{
/// <summary>
/// Property symbol.
/// </summary>
Property,
/// <summary>
/// Event symbol.
/// </summary>
Event
}

/// <summary>
/// The SvgElement object property/event.
/// </summary>
private class Property
{
/// <summary>
/// Gets or sets property/event symbol.
/// </summary>
public ISymbol Symbol { get; }

/// <summary>
/// Gets or sets property/event symbol member type.
/// </summary>
public MemberType MemberType { get; }

/// <summary>
/// Gets or sets property/event attribute name.
/// </summary>
public string AttributeName { get; }

/// <summary>
/// Gets or sets property/event attribute namespace.
/// </summary>
public string AttributeNamespace { get; }

/// <summary>
/// Gets or sets property/event type converter type string.
/// </summary>
public string? Converter { get; }

/// <summary>
/// Initializes a new instance of the <see cref="Property"/> class.
/// </summary>
/// <param name="symbol">The property/event symbol.</param>
/// <param name="memberType">The property/event symbol member type.</param>
/// <param name="attributeName">The property/event attribute name.</param>
/// <param name="attributeNamespace">The property/event attribute namespace.</param>
/// <param name="converter">The property/event type converter type string.</param>
public Property(ISymbol symbol, MemberType memberType, string attributeName, string attributeNamespace, string? converter)
{
Symbol = symbol;
MemberType = memberType;
AttributeName = attributeName;
AttributeNamespace = attributeNamespace;
Converter = converter;
}
}

/// <summary>
/// Custom <see cref="Property"/> equality comparer using <see cref="ISymbol"/> for cmparison.
/// </summary>
private class PropertyEqualityComparer : IEqualityComparer<Property>
{
/// <inheritdoc/>
public bool Equals(Property p1, Property p2)
{
return SymbolEqualityComparer.Default.Equals(p1.Symbol, p2.Symbol);
}

/// <inheritdoc/>
public int GetHashCode(Property p)
{
#pragma warning disable RS1024
return p.Symbol.GetHashCode();
#pragma warning restore RS1024
}
}

/// <summary>
/// The SvgElement object.
/// </summary>
private class Element
{
/// <summary>
/// Gets or sets element type symbol.
/// </summary>
public INamedTypeSymbol Symbol { get; }

/// <summary>
/// Gets or sets element name.
/// </summary>
public string? ElementName { get; }

/// <summary>
/// Gets or sets classes that use element name.
/// </summary>
public List<string> ClassNames { get; }

/// <summary>
/// Gets or sets element properties list.
/// </summary>
public List<Property> Properties { get; }

/// <summary>
/// Initializes a new instance of the <see cref="Element"/> class.
/// </summary>
/// <param name="symbol">The element type symbol.</param>
/// <param name="elementName">The element name.</param>
/// <param name="classNames">The classes that use element name.</param>
/// <param name="properties">The element properties list.</param>
public Element(INamedTypeSymbol symbol, string? elementName, List<string> classNames, List<Property> properties)
{
Symbol = symbol;
ElementName = elementName;
ClassNames = classNames;
Properties = properties;
}
}

/// <summary>
/// The SyntaxReceiver is used to filter compiled code. This enable quick and easy way to filter compiled code.
/// </summary>
private class SyntaxReceiver : ISyntaxReceiver
{
/// <summary>
/// Gets the list of all candidate class.
/// </summary>
public List<ClassDeclarationSyntax> CandidateClasses { get; } = new();

/// <inheritdoc/>
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
{
CandidateClasses.Add(classDeclarationSyntax);
}
}
}
}
}
46 changes: 46 additions & 0 deletions Generators/Element.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;

namespace Svg.Generators
{
/// <summary>
/// The SvgElement object.
/// </summary>
class Element
{
/// <summary>
/// Gets or sets element type symbol.
/// </summary>
public INamedTypeSymbol Symbol { get; }

/// <summary>
/// Gets or sets element name.
/// </summary>
public string? ElementName { get; }

/// <summary>
/// Gets or sets classes that use element name.
/// </summary>
public List<string> ClassNames { get; }

/// <summary>
/// Gets or sets element properties list.
/// </summary>
public List<Property> Properties { get; }

/// <summary>
/// Initializes a new instance of the <see cref="Element"/> class.
/// </summary>
/// <param name="symbol">The element type symbol.</param>
/// <param name="elementName">The element name.</param>
/// <param name="classNames">The classes that use element name.</param>
/// <param name="properties">The element properties list.</param>
public Element(INamedTypeSymbol symbol, string? elementName, List<string> classNames, List<Property> properties)
{
Symbol = symbol;
ElementName = elementName;
ClassNames = classNames;
Properties = properties;
}
}
}
17 changes: 17 additions & 0 deletions Generators/MemberType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Svg.Generators
{
/// <summary>
/// Symbol member type.
/// </summary>
enum MemberType
{
/// <summary>
/// Property symbol.
/// </summary>
Property,
/// <summary>
/// Event symbol.
/// </summary>
Event
}
}
Loading

0 comments on commit a2c69b7

Please sign in to comment.