diff --git a/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj b/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj
index db8a9ba48..6d0b92865 100644
--- a/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj
+++ b/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj
@@ -16,6 +16,7 @@
+
@@ -25,7 +26,7 @@
-
+
diff --git a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj
index ea28e8e31..5b4cda0bd 100644
--- a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj
+++ b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/OpenTabletDriver.Tests/ConfigurationTest.cs b/OpenTabletDriver.Tests/ConfigurationTest.cs
index 2331816c7..c110e2037 100644
--- a/OpenTabletDriver.Tests/ConfigurationTest.cs
+++ b/OpenTabletDriver.Tests/ConfigurationTest.cs
@@ -1,9 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Schema;
+using Newtonsoft.Json.Schema.Generation;
using OpenTabletDriver.Components;
using OpenTabletDriver.Tablet;
using Xunit;
@@ -270,6 +274,46 @@ static void AssertGroup(List identificationContexts, Iden
}
}
+ private static readonly string ConfigurationProjectDir = Path.GetFullPath(Path.Join("../../../..", "OpenTabletDriver.Configurations"));
+ private static readonly string ConfigurationDir = Path.Join(ConfigurationProjectDir, "Configurations");
+ private static readonly IEnumerable<(string, string)> ConfigFiles = Directory.EnumerateFiles(ConfigurationDir, "*.json", SearchOption.AllDirectories)
+ .Select(f => (Path.GetRelativePath(ConfigurationDir, f), File.ReadAllText(f)));
+
+ [Fact]
+ public void Configurations_Verify_Configs_With_Schema()
+ {
+ var gen = new JSchemaGenerator();
+ var schema = gen.Generate(typeof(TabletConfiguration));
+ DisallowAdditionalItemsAndProperties(schema);
+
+ var failed = false;
+
+ foreach (var (tabletFilename, tabletConfigString) in ConfigFiles)
+ {
+ var tabletConfig = JObject.Parse(tabletConfigString);
+ if (tabletConfig.IsValid(schema, out IList errors)) continue;
+
+ _testOutputHelper.WriteLine($"Tablet Configuration {tabletFilename} did not match schema:\r\n{string.Join("\r\n", errors)}\r\n");
+ failed = true;
+ }
+
+ Assert.False(failed);
+ }
+
+ private static void DisallowAdditionalItemsAndProperties(JSchema schema)
+ {
+ schema.AllowAdditionalItems = false;
+ schema.AllowAdditionalProperties = false;
+ schema.AllowUnevaluatedItems = false;
+ schema.AllowUnevaluatedProperties = false;
+
+ foreach (var child in schema.Properties)
+ {
+ if (child.Key == nameof(TabletConfiguration.Attributes)) continue;
+ DisallowAdditionalItemsAndProperties(child.Value);
+ }
+ }
+
private static void AssertInequal(IdentificationContext a, IdentificationContext b)
{
if (IsEqual(a.Identifier, b.Identifier))
diff --git a/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj b/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj
index 43dce9a8e..cbd82ee73 100644
--- a/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj
+++ b/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj
@@ -9,6 +9,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/OpenTabletDriver/OpenTabletDriver.csproj b/OpenTabletDriver/OpenTabletDriver.csproj
index 7b25fcb96..f6e302238 100644
--- a/OpenTabletDriver/OpenTabletDriver.csproj
+++ b/OpenTabletDriver/OpenTabletDriver.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/OpenTabletDriver/Tablet/ButtonSpecifications.cs b/OpenTabletDriver/Tablet/ButtonSpecifications.cs
index 43aa6440a..d33711d8c 100644
--- a/OpenTabletDriver/Tablet/ButtonSpecifications.cs
+++ b/OpenTabletDriver/Tablet/ButtonSpecifications.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -13,6 +14,7 @@ public class ButtonSpecifications
/// The amount of buttons.
///
[DisplayName("Buttons")]
+ [Required(ErrorMessage = $"{nameof(ButtonCount)} must be defined")]
public uint ButtonCount { set; get; }
}
}
diff --git a/OpenTabletDriver/Tablet/DeviceIdentifier.cs b/OpenTabletDriver/Tablet/DeviceIdentifier.cs
index f16e53c07..0f8345f49 100644
--- a/OpenTabletDriver/Tablet/DeviceIdentifier.cs
+++ b/OpenTabletDriver/Tablet/DeviceIdentifier.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -14,12 +15,16 @@ public class DeviceIdentifier
/// The Vendor ID of the device.
///
[DisplayName("Vendor ID")]
+ [Required(ErrorMessage = $"{nameof(VendorID)} identifier must be defined")]
+ [Range(0, 0xFFFF)]
public int VendorID { set; get; }
///
/// The Product ID of the device.
///
[DisplayName("Product ID")]
+ [Required(ErrorMessage = $"{nameof(ProductID)} identifier must be defined")]
+ [Range(0, 0xFFFF)]
public int ProductID { set; get; }
///
@@ -38,6 +43,7 @@ public class DeviceIdentifier
/// The device report parser used by the detected device.
///
[DisplayName("Report Parser")]
+ [RegularExpression(@"^OpenTabletDriver(\.[\w\d]+)+$", ErrorMessage = $"{nameof(ReportParser)} for identifier must match regular expression")]
public string ReportParser { set; get; } = string.Empty;
///
diff --git a/OpenTabletDriver/Tablet/DigitizerSpecifications.cs b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs
index 7683919dc..74ddc6146 100644
--- a/OpenTabletDriver/Tablet/DigitizerSpecifications.cs
+++ b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -12,22 +13,26 @@ public class DigitizerSpecifications
///
/// The width of the digitizer in millimeters.
///
+ [Required(ErrorMessage = $"Digitizer ${nameof(Width)} must be defined")]
public float Width { set; get; }
///
/// The height of the digitizer in millimeters.
///
+ [Required(ErrorMessage = $"Digitizer ${nameof(Height)} must be defined")]
public float Height { set; get; }
///
/// The maximum X coordinate for the digitizer.
///
+ [Required(ErrorMessage = $"Digitizer ${nameof(MaxX)} must be defined")]
[DisplayName("Max X")]
public float MaxX { set; get; }
///
/// The maximum Y coordinate for the digitizer.
///
+ [Required(ErrorMessage = $"Digitizer ${nameof(MaxY)} must be defined")]
[DisplayName("Max Y")]
public float MaxY { set; get; }
}
diff --git a/OpenTabletDriver/Tablet/PenSpecifications.cs b/OpenTabletDriver/Tablet/PenSpecifications.cs
index 33979f548..cef7684bf 100644
--- a/OpenTabletDriver/Tablet/PenSpecifications.cs
+++ b/OpenTabletDriver/Tablet/PenSpecifications.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -13,6 +14,7 @@ public class PenSpecifications : ButtonSpecifications
/// The maximum pressure that the pen supports.
///
[DisplayName("Max Pressure")]
+ [Required(ErrorMessage = $"Pen {nameof(MaxPressure)} must be defined")]
public uint MaxPressure { set; get; }
}
}
diff --git a/OpenTabletDriver/Tablet/TabletConfiguration.cs b/OpenTabletDriver/Tablet/TabletConfiguration.cs
index 917c8a6cf..22006b627 100644
--- a/OpenTabletDriver/Tablet/TabletConfiguration.cs
+++ b/OpenTabletDriver/Tablet/TabletConfiguration.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -13,28 +14,34 @@ public class TabletConfiguration
///
/// The tablet's name.
///
+ [Required(ErrorMessage = $"Tablet {nameof(Name)} is required")]
public string Name { set; get; } = string.Empty;
///
/// The tablet's specifications.
///
+ [Required(ErrorMessage = $"Tablet {nameof(Specifications)} is required")]
public TabletSpecifications Specifications { set; get; } = new TabletSpecifications();
///
/// The digitizer device identifier.
///
+ [Required(ErrorMessage = $"Tablet {nameof(DigitizerIdentifiers)} are required")]
[DisplayName("Digitizer Identifiers")]
+ [MinLength(1, ErrorMessage = "Requires at least 1 identifier")]
public List DigitizerIdentifiers { set; get; } = new List();
///
/// The auxiliary device identifier.
///
+ [Required(ErrorMessage = $"Tablet {nameof(AuxiliaryDeviceIdentifiers)} must be present")]
[DisplayName("Auxiliary Identifiers")]
public List AuxiliaryDeviceIdentifiers { set; get; } = new List();
///
/// Other information about the tablet that can be used in tools or other applications.
///
+ [Required(ErrorMessage = $"Tablet {nameof(Attributes)} must be present")]
public Dictionary Attributes { set; get; } = new Dictionary();
}
}
diff --git a/OpenTabletDriver/Tablet/TabletSpecifications.cs b/OpenTabletDriver/Tablet/TabletSpecifications.cs
index 020f240ce..5f5a93084 100644
--- a/OpenTabletDriver/Tablet/TabletSpecifications.cs
+++ b/OpenTabletDriver/Tablet/TabletSpecifications.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace OpenTabletDriver.Tablet
@@ -12,11 +13,13 @@ public class TabletSpecifications
///
/// Specifications for the tablet digitizer.
///
+ [Required(ErrorMessage = $"{nameof(Digitizer)} specifications must be defined")]
public DigitizerSpecifications? Digitizer { set; get; }
///
/// Specifications for the tablet's pen.
///
+ [Required(ErrorMessage = $"{nameof(Pen)} specifications must be defined")]
public PenSpecifications? Pen { set; get; }
///