Skip to content

Commit

Permalink
[Draft] Converter Inheritance Support (#385)
Browse files Browse the repository at this point in the history
* Updated converter for inheritance.
* Added tests.
* Broke the two constructors apart.

---------

Co-authored-by: Aria Bounds <abounds@fullsailpartners.com>
  • Loading branch information
Soyvolon and abounds-fsp committed Jun 19, 2024
1 parent 87b651c commit 2e3d52f
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 16 deletions.
41 changes: 41 additions & 0 deletions src/ReverseMarkdown.Test/ChildConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using ReverseMarkdown.Converters;
using ReverseMarkdown.Test.Children;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using Xunit;

namespace ReverseMarkdown.Test
{
public class ChildConverterTests
{
[Fact]
public void WhenConverter_A_IsReplacedByConverter_IgnoreAWhenHasClass()
{
var converter = new ReverseMarkdown.Converter(new Config(), typeof(IgnoreAWhenHasClass).Assembly);

var type = converter.GetType();
var prop = type.GetField("_converters", BindingFlags.NonPublic | BindingFlags.Instance);

Assert.NotNull(prop);

var propValRaw = prop.GetValue(converter);

Assert.NotNull(propValRaw);

var propVal = (IDictionary<string, IConverter>)propValRaw;

Assert.NotNull(propVal);

var converters = propVal.Select(e => e.Value.GetType()).ToArray();

Assert.DoesNotContain(typeof(A), converters);
Assert.Contains(typeof(IgnoreAWhenHasClass), converters);
}
}
}
28 changes: 28 additions & 0 deletions src/ReverseMarkdown.Test/Children/IgnoreAWhenHasClass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using HtmlAgilityPack;

using ReverseMarkdown.Converters;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReverseMarkdown.Test.Children
{
internal class IgnoreAWhenHasClass : A
{
private readonly string _ignore = "ignore";

public IgnoreAWhenHasClass(Converter converter) : base(converter)
{ }

public override string Convert(HtmlNode node)
{
if (node.HasClass(_ignore))
return "";

return base.Convert(node);
}
}
}
66 changes: 50 additions & 16 deletions src/ReverseMarkdown/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,71 @@ namespace ReverseMarkdown
{
public class Converter
{
private readonly IDictionary<string, IConverter> _converters = new Dictionary<string, IConverter>();
private readonly IConverter _passThroughTagsConverter;
private readonly IConverter _dropTagsConverter;
private readonly IConverter _byPassTagsConverter;
protected readonly IDictionary<string, IConverter> _converters = new Dictionary<string, IConverter>();
protected readonly IConverter _passThroughTagsConverter;
protected readonly IConverter _dropTagsConverter;
protected readonly IConverter _byPassTagsConverter;

public Converter() : this(new Config()) {}

public Converter(Config config)
public Converter(Config config) : this(config, null) {}

public Converter(Config config, params Assembly[] additionalAssemblies)
{
Config = config;

List<Assembly> assemblies = new List<Assembly>()
{
typeof(IConverter).GetTypeInfo().Assembly
};

if (!(additionalAssemblies is null))
assemblies.AddRange(additionalAssemblies);

List<Type> types = new List<Type>();
// instantiate all converters excluding the unknown tags converters
foreach (var ctype in typeof(IConverter).GetTypeInfo().Assembly.GetTypes()
.Where(t => t.GetTypeInfo().GetInterfaces().Contains(typeof(IConverter)) &&
!t.GetTypeInfo().IsAbstract
&& t != typeof(PassThrough)
&& t != typeof(Drop)
&& t != typeof(ByPass)))
foreach (var assembly in assemblies)
{
Activator.CreateInstance(ctype, this);
foreach (var ctype in assembly.GetTypes()
.Where(t => t.GetTypeInfo().GetInterfaces().Contains(typeof(IConverter)) &&
!t.GetTypeInfo().IsAbstract
&& t != typeof(PassThrough)
&& t != typeof(Drop)
&& t != typeof(ByPass)))
{
// Check to see if any existing types are children/equal to
// the type to add.
if (types.Any(e => ctype.IsAssignableFrom(e)))
// If they are, ignore the type.
continue;

// See if there is a type that is a parent of the
// current type.
Type toRemove = types.FirstOrDefault(e => e.IsAssignableFrom(ctype));
// if there is ...
if (!(toRemove is null))
// ... remove the parent.
types.Remove(toRemove);

// finally, add the type.
types.Add(ctype);
}
}

// For each type to register ...
foreach (var ctype in types)
// ... activate them
Activator.CreateInstance(ctype, this);

// register the unknown tags converters
_passThroughTagsConverter = new PassThrough(this);
_dropTagsConverter = new Drop(this);
_byPassTagsConverter = new ByPass(this);
}

public Config Config { get; }
public Config Config { get; protected set; }

public string Convert(string html)
public virtual string Convert(string html)
{
html = Cleaner.PreTidy(html, Config.RemoveComments);

Expand All @@ -63,12 +97,12 @@ public string Convert(string html)
return result.Trim().FixMultipleNewlines();
}

public void Register(string tagName, IConverter converter)
public virtual void Register(string tagName, IConverter converter)
{
_converters[tagName] = converter;
}

public IConverter Lookup(string tagName)
public virtual IConverter Lookup(string tagName)
{
// if a tag is in the pass through list then use the pass through tags converter
if (Config.PassThroughTags.Contains(tagName))
Expand Down

0 comments on commit 2e3d52f

Please sign in to comment.