Files
OxyPlot/Source/Examples/ExampleLibrary/ExampleInfo.cs
2023-09-02 09:24:59 +02:00

314 lines
9.7 KiB
C#

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ExampleInfo.cs" company="OxyPlot">
// Copyright (c) 2014 OxyPlot contributors
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace ExampleLibrary
{
using System;
using System.Collections.Generic;
using System.Reflection;
using ExampleLibrary.Utilities;
using OxyPlot;
/// <summary>
/// Provides information about an example.
/// </summary>
public class ExampleInfo
{
/// <summary>
/// The method to invoke.
/// </summary>
private readonly MethodInfo method;
/// <summary>
/// Indicates whether this instance was already initialized.
/// </summary>
private bool initialized;
/// <summary>
/// The un-modified example.
/// </summary>
private Example Example;
/// <summary>
/// The examples for this example info.
/// </summary>
private Dictionary<ExampleFlags, Example> examples;
/// <summary>
/// The options supported by this example.
/// </summary>
private ExampleFlags exampleSupport;
/// <summary>
/// Initializes a new instance of the <see cref="ExampleInfo"/> class.
/// </summary>
/// <param name="category">The category.</param>
/// <param name="title">The title.</param>
/// <param name="tags">The tags.</param>
/// <param name="method">The method.</param>
/// <param name="excludeFromAutomatedTests">A value indiciating whether the example should be excluded from automated tests.</param>
public ExampleInfo(string category, string title, string[] tags, MethodInfo method, bool excludeFromAutomatedTests)
{
this.Category = category;
this.Title = title;
this.Tags = tags;
this.method = method;
this.ExcludeFromAutomatedTests = excludeFromAutomatedTests;
}
/// <summary>
/// Gets the category.
/// </summary>
/// <value>
/// The category.
/// </value>
public string Category { get; }
/// <summary>
/// Gets the code.
/// </summary>
/// <value>
/// The code.
/// </value>
public string Code => this.PlotModel?.ToCode();
/// <summary>
/// Gets a value indicating whether the plot model is reversible.
/// </summary>
public bool IsReversible
{
get
{
this.EnsureInitialized();
return exampleSupport.HasFlag(ExampleFlags.Reverse);
}
}
/// <summary>
/// Gets a value indicating whether the plot model is transposable.
/// </summary>
public bool IsTransposable
{
get
{
this.EnsureInitialized();
return exampleSupport.HasFlag(ExampleFlags.Transpose);
}
}
/// <summary>
/// Gets the code for the example with the given flags.
/// </summary>
/// <param name="flags">The flags for the example.</param>
/// <returns>Code that produces the example.</returns>
/// <remarks>Ignores unsupported flags.</remarks>
public string GetCode(ExampleFlags flags)
{
return this.GetModel(flags)?.ToCode();
}
/// <summary>
/// Gets the <see cref="PlotModel"/> for the example with the given flags.
/// </summary>
/// <param name="flags">The flags for the example.</param>
/// <returns>The <see cref="PlotModel"/>.</returns>
/// <remarks>Ignores unsupported flags.</remarks>
public PlotModel GetModel(ExampleFlags flags)
{
return this.GetExample(flags).Model;
}
/// <summary>
/// Gets the <see cref="IPlotController"/> for the example with the given flags.
/// </summary>
/// <param name="flags">The flags for the example.</param>
/// <returns>The <see cref="IPlotController"/>.</returns>
/// <remarks>Ignores unsupported flags.</remarks>
public IPlotController GetController(ExampleFlags flags)
{
return this.GetExample(flags).Controller;
}
/// <summary>
/// Gets the <see cref="Example"/> with the given flags.
/// </summary>
/// <param name="flags">The flags for the example.</param>
/// <returns>The <see cref="Example"/>.</returns>
/// <remarks>Ignores unsupported flags.</remarks>
private Example GetExample(ExampleFlags flags)
{
this.EnsureInitialized();
// ignore flags we don't support
flags = FilterFlags(flags);
if (!examples.TryGetValue(flags, out var example))
{
example = GetDefaultExample();
if (flags.HasFlag(ExampleFlags.Transpose))
{
example.Model.Transpose();
}
if (flags.HasFlag(ExampleFlags.Reverse))
{
example.Model.ReverseAllAxes();
}
examples[flags] = example;
}
return example;
}
/// <summary>
/// Gets the plot controller for the default example.
/// </summary>
/// <value>
/// The plot controller.
/// </value>
public IPlotController PlotController
{
get
{
this.EnsureInitialized();
return this.Example.Controller;
}
}
/// <summary>
/// Gets the plot model for the default example.
/// </summary>
/// <value>
/// The plot model.
/// </value>
public PlotModel PlotModel
{
get
{
this.EnsureInitialized();
return this.Example.Model;
}
}
/// <summary>
/// Gets the tags.
/// </summary>
/// <value>
/// The tags.
/// </value>
public string[] Tags { get; }
/// <summary>
/// Gets a value indiciating whether this example should be excluded from automated tests.
/// </summary>
/// <value>
/// <c>true</c> if the example should be excluded from automated tests, otherwise <c>false</c>.
/// </value>
public bool ExcludeFromAutomatedTests { get; }
/// <summary>
/// Gets the title.
/// </summary>
/// <value>
/// The title.
/// </value>
public string Title { get; }
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return this.Title;
}
/// <summary>
/// Prepares <see cref="ExampleFlags"/> from the given parameters.
/// </summary>
/// <param name="transpose">Whether to set the <see cref="ExampleFlags.Transpose"/> flag.</param>
/// <param name="reverse">Whether to set the <see cref="ExampleFlags.Reverse"/> flag.</param>
/// <returns>The <see cref="ExampleFlags"/>.</returns>
public static ExampleFlags PrepareFlags(bool transpose, bool reverse)
{
var flags = (ExampleFlags)0;
if (transpose)
{
flags |= ExampleFlags.Transpose;
}
if (reverse)
{
flags |= ExampleFlags.Reverse;
}
return flags;
}
/// <summary>
/// Initializes this instance if it is not already initialized.
/// </summary>
private void EnsureInitialized()
{
if (this.initialized)
{
return;
}
this.initialized = true;
this.examples = new Dictionary<ExampleFlags, Example>();
this.Example = GetDefaultExample();
this.exampleSupport = PrepareFlags(this.Example.Model?.IsTransposable() == true, this.Example.Model?.IsReversible() == true);
// remember the 'default' model: loads the transposed/reversed ones as we need them
this.examples.Add(PrepareFlags(false, false), this.Example);
}
/// <summary>
/// Gets a new instance of the default example.
/// </summary>
/// <returns>An <see cref="Example"/>.</returns>
private Example GetDefaultExample()
{
var result = this.method.Invoke(null, null);
if (result is null)
{
return new Example(null, null);
}
if (result is PlotModel plotModel)
{
return new Example(plotModel, null);
}
else if (result is Example example)
{
return example;
}
throw new Exception($"Unsupport type returned by example method for example {Category} > {Title}: {result.GetType().FullName}.");
}
/// <summary>
/// Filters unsupported flags from the given flags.
/// </summary>
/// <param name="flags">The original set of flags.</param>
/// <returns>The filtered flags.</returns>
private ExampleFlags FilterFlags(ExampleFlags flags)
{
return flags & exampleSupport;
}
}
}