Skip to content

Commit

Permalink
separate media type analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
IS4Code committed May 3, 2023
1 parent 527137b commit 1a6942e
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 2 deletions.
1 change: 1 addition & 0 deletions SFI.Application/Inspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public Inspector()
Analyzers.Add(DataAnalyzer = new DataAnalyzer(() => new UdeEncodingDetector()));
Analyzers.Add(DataObjectAnalyzer = new DataObjectAnalyzer());
Analyzers.Add(XmlAnalyzer = new XmlAnalyzer());
Analyzers.Add(new MediaTypeAnalyzer());
Analyzers.Add(new X509CertificateAnalyzer());
Analyzers.Add(new FormatObjectAnalyzer());

Expand Down
8 changes: 7 additions & 1 deletion SFI.ExternalFormats/Documents/OpenPackageFormat.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using IS4.SFI.Services;
using IS4.SFI.Tools;
using IS4.SFI.Vocabulary;
using NPOI.OpenXml4Net.Exceptions;
using NPOI.OpenXml4Net.OPC;
Expand Down Expand Up @@ -74,7 +75,12 @@ public async ValueTask<AnalysisResult> Analyze(IContainerNode? parentNode, IFile

if(contentType != null)
{
node.Set(Properties.EncodingFormat, Vocabularies.Urim, Uri.EscapeUriString(contentType));
var typeObject = ImmutableContentType.GetCached(contentType);
var typeNode = (await analyzers.Analyze<System.Net.Mime.ContentType>(typeObject, context.WithParent(node))).Node;
if(typeNode != null)
{
node.Set(Properties.EncodingFormat, typeNode);
}
}
}catch(InvalidFormatException)
{
Expand Down
9 changes: 8 additions & 1 deletion SFI/Analyzers/FormatObjectAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using IS4.SFI.Formats;
using IS4.SFI.Services;
using IS4.SFI.Tools;
using IS4.SFI.Vocabulary;
using MorseCode.ITask;
using System;
using System.Net.Mime;
using System.Threading.Tasks;

namespace IS4.SFI.Analyzers
Expand Down Expand Up @@ -66,7 +68,12 @@ protected virtual async ValueTask<AnalysisResult> Analyze<T>(T value, IFormatObj
node.SetClass(cls);
}
}
node.Set(Properties.EncodingFormat, Vocabularies.Urim, Uri.EscapeUriString(type));
var typeObject = ImmutableContentType.GetCached(type);
var typeNode = (await analyzers.Analyze<ContentType>(typeObject, context.WithParent(node))).Node;
if(typeNode != null)
{
node.Set(Properties.EncodingFormat, typeNode);
}
}

var label = result.Label;
Expand Down
51 changes: 51 additions & 0 deletions SFI/Analyzers/MediaTypeAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using IS4.SFI.Services;
using IS4.SFI.Vocabulary;
using System;
using System.Net.Mime;
using System.Threading.Tasks;

namespace IS4.SFI.Analyzers
{
/// <summary>
/// An analyzer of media types, as instances of <see cref="ContentType"/>.
/// </summary>
public sealed class MediaTypeAnalyzer : EntityAnalyzer<ContentType>
{
/// <summary>
/// Whether to add class information to the created node.
/// </summary>
public bool AddClasses { get; set; } = true;

/// <inheritdoc cref="EntityAnalyzer.EntityAnalyzer"/>
public MediaTypeAnalyzer()
{

}

/// <inheritdoc/>
public async override ValueTask<AnalysisResult> Analyze(ContentType entity, AnalysisContext context, IEntityAnalyzers analyzers)
{
var type = entity.ToString();
var node = context.NodeFactory.Create(Vocabularies.Urim, Uri.EscapeUriString(type));
if(AddClasses)
{
node.SetClass(Classes.MediaType);
if(type.StartsWith(TextTools.ImpliedMediaTypePrefix, StringComparison.OrdinalIgnoreCase))
{
node.SetClass(Classes.MediaTypeImplied);
}
int semicolon = type.IndexOf(';');
if(semicolon != -1)
{
node.SetClass(Classes.MediaTypeParametrized);
}
int plus = type.IndexOf('+');
if(plus != -1 && (semicolon == -1 || plus < semicolon))
{
node.SetClass(Classes.MediaTypeStructured);
}
}
return new(node, type);
}
}
}
5 changes: 5 additions & 0 deletions SFI/TextTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public static string SizeSuffix(long value, int decimalPlaces)
return String.Format(CultureInfo.InvariantCulture, $"{{0:0.{new string('#', decimalPlaces)}}} {{1}}", adjustedSize, units[n]);
}

/// <summary>
/// The prefix used for implied media types.
/// </summary>
public static readonly string ImpliedMediaTypePrefix = "application/prs.implied-";

/// <summary>
/// Creates an implied media type from a namespace URI, PUBLIC identifier,
/// and the root element name in an XML document.
Expand Down
87 changes: 87 additions & 0 deletions SFI/Tools/ImmutableContentType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using IS4.SFI.Services;
using System;
using System.Net.Mime;
using System.Runtime.CompilerServices;

namespace IS4.SFI.Tools
{
/// <summary>
/// Provides an implementation of <see cref="ContentType"/> that preserves the value
/// it was constructed from and prevents changes to it from taking effects.
/// </summary>
/// <remarks>
/// The <see cref="ToString"/> method returns the value passed to
/// <see cref="ImmutableContentType(string)"/>, but if the object is changed,
/// the method throws an exception, signifying changes to the object.
/// </remarks>
public class ImmutableContentType : ContentType, ICloneable, IEquatable<ContentType>, IPersistentKey
{
static readonly ConditionalWeakTable<string, ImmutableContentType> cache = new();

/// <summary>
/// Obtains an instance of <see cref="ImmutableContentType"/> constructed
/// from a particular string, potentially reusing an earlier constructed object.
/// </summary>
/// <param name="contentType"></param>
/// <returns></returns>
public static ImmutableContentType GetCached(string contentType)
{
return cache.GetValue(contentType, ct => new ImmutableContentType(ct));
}

readonly string value;
readonly string reparsedValue;

bool Changed => reparsedValue != base.ToString();

object? IPersistentKey.ReferenceKey => null;

object? IPersistentKey.DataKey => ToString();

/// <inheritdoc/>
public ImmutableContentType(string contentType) : base(contentType)
{
value = contentType;
reparsedValue = base.ToString();
}

/// <inheritdoc cref="ICloneable.Clone"/>
public virtual ImmutableContentType Clone()
{
return new ImmutableContentType(value);
}

object ICloneable.Clone()
{
return Clone();
}

/// <inheritdoc cref="Equals(object)"/>
public virtual bool Equals(ContentType other)
{
return ToString() == other.ToString();
}

/// <inheritdoc/>
public override bool Equals(object rparam)
{
return rparam is ContentType other && Equals(other);
}

/// <inheritdoc/>
public override int GetHashCode()
{
return ToString().GetHashCode();
}

/// <inheritdoc/>
public override string ToString()
{
if(Changed)
{
throw new InvalidOperationException("The instance was modified during its lifetime.");
}
return value;
}
}
}
24 changes: 24 additions & 0 deletions SFI/Vocabulary/Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,30 @@ public static class Classes
[Uri(Http, "MessageHeader")]
public static readonly ClassUri HttpMessageHeader;

/// <summary>
/// <see cref="Uriv"/>:Mimetype.
/// </summary>
[Uri(Uriv, "Mimetype")]
public static readonly ClassUri MediaType;

/// <summary>
/// <see cref="Uriv"/>:Mimetype-Implied.
/// </summary>
[Uri(Uriv, "Mimetype-Implied")]
public static readonly ClassUri MediaTypeImplied;

/// <summary>
/// <see cref="Uriv"/>:Mimetype-Structured.
/// </summary>
[Uri(Uriv, "Mimetype-Structured")]
public static readonly ClassUri MediaTypeStructured;

/// <summary>
/// <see cref="Uriv"/>:Mimetype-Parametrized.
/// </summary>
[Uri(Uriv, "Mimetype-Parametrized")]
public static readonly ClassUri MediaTypeParametrized;

static Classes()
{
typeof(Classes).InitializeUris();
Expand Down
5 changes: 5 additions & 0 deletions SFI/Vocabulary/Vocabularies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ public static class Uri
/// </summary>
public const string Dbo = "http://dbpedia.org/ontology/";

/// <summary>
/// uri4uri vocabulary
/// </summary>
public const string Uriv = "https://w3id.org/uri4uri/vocab#";

/// <summary>
/// uri4uri MIME types
/// </summary>
Expand Down

0 comments on commit 1a6942e

Please sign in to comment.