mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
[DSC] Add support for ImageresizerSizes property (#32657)
Add support for ImageresizerSizes property - do not use ints for enums in ImageSize struct - add required converters - extend setAdditional functionality - add samples Co-authored-by: Andrey Nekrasov <1828123+yuyoyuppe@users.noreply.github.com>
This commit is contained in:
@@ -3,8 +3,11 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
@@ -28,18 +31,25 @@ public sealed class SetAdditionalSettingsCommandLineCommand
|
||||
|
||||
private struct AdditionalPropertyInfo
|
||||
{
|
||||
public string RootPropertyName;
|
||||
public JsonValueKind RootObjectType;
|
||||
// A path to the property starting from the root module Settings object in the following format: "RootPropertyA.NestedPropertyB[...]"
|
||||
public string PropertyPath;
|
||||
|
||||
// Property Type hint so we know how to handle it
|
||||
public JsonValueKind PropertyType;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, AdditionalPropertyInfo> SupportedAdditionalPropertiesInfoForModules = new Dictionary<string, AdditionalPropertyInfo> { { "PowerLauncher", new AdditionalPropertyInfo { RootPropertyName = "Plugins", RootObjectType = JsonValueKind.Array } } };
|
||||
private static readonly Dictionary<string, AdditionalPropertyInfo> SupportedAdditionalPropertiesInfoForModules = new Dictionary<string, AdditionalPropertyInfo> { { "PowerLauncher", new AdditionalPropertyInfo { PropertyPath = "Plugins", PropertyType = JsonValueKind.Array } }, { "ImageResizer", new AdditionalPropertyInfo { PropertyPath = "Properties.ImageresizerSizes.Value", PropertyType = JsonValueKind.Array } } };
|
||||
|
||||
private static void ExecuteRootArray(JsonElement.ArrayEnumerator properties, IEnumerable<object> currentPropertyValuesArray)
|
||||
private static IEnumerable<object> ExecuteRootArray(IEnumerable<JsonElement> properties, IEnumerable<object> currentPropertyValuesArray)
|
||||
{
|
||||
// In case it's an array of object -> combine the existing values with the provided
|
||||
var currentPropertyValueType = currentPropertyValuesArray.FirstOrDefault()?.GetType();
|
||||
// In case it's an array of objects -> combine the existing values with the provided
|
||||
var result = currentPropertyValuesArray;
|
||||
|
||||
var currentPropertyValueType = GetUnderlyingTypeOfCollection(currentPropertyValuesArray);
|
||||
object matchedElement = null;
|
||||
|
||||
object newKeyPropertyValue = null;
|
||||
|
||||
foreach (var arrayElement in properties)
|
||||
{
|
||||
var newElementPropertyValues = new Dictionary<string, object>();
|
||||
@@ -47,15 +57,16 @@ public sealed class SetAdditionalSettingsCommandLineCommand
|
||||
{
|
||||
var elementPropertyName = elementProperty.Name;
|
||||
var elementPropertyType = currentPropertyValueType.GetProperty(elementPropertyName).PropertyType;
|
||||
var elemePropertyValue = ICmdLineRepresentable.ParseFor(elementPropertyType, elementProperty.Value.ToString());
|
||||
var elementNewPropertyValue = ICmdLineRepresentable.ParseFor(elementPropertyType, elementProperty.Value.ToString());
|
||||
if (elementPropertyName == KeyPropertyName)
|
||||
{
|
||||
newKeyPropertyValue = elementNewPropertyValue;
|
||||
foreach (var currentElementValue in currentPropertyValuesArray)
|
||||
{
|
||||
var currentElementType = currentElementValue.GetType();
|
||||
var keyPropertyNameInfo = currentElementType.GetProperty(KeyPropertyName);
|
||||
var keyPropertyValue = keyPropertyNameInfo.GetValue(currentElementValue);
|
||||
if (string.Equals(keyPropertyValue, elemePropertyValue))
|
||||
var currentKeyPropertyValue = keyPropertyNameInfo.GetValue(currentElementValue);
|
||||
if (string.Equals(currentKeyPropertyValue, elementNewPropertyValue))
|
||||
{
|
||||
matchedElement = currentElementValue;
|
||||
break;
|
||||
@@ -64,7 +75,18 @@ public sealed class SetAdditionalSettingsCommandLineCommand
|
||||
}
|
||||
else
|
||||
{
|
||||
newElementPropertyValues.Add(elementPropertyName, elemePropertyValue);
|
||||
newElementPropertyValues.Add(elementPropertyName, elementNewPropertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Appending a new element -> create it first using a default ctor with 0 args and append it to the result
|
||||
if (matchedElement == null)
|
||||
{
|
||||
newElementPropertyValues.Add(KeyPropertyName, newKeyPropertyValue);
|
||||
matchedElement = Activator.CreateInstance(currentPropertyValueType);
|
||||
if (matchedElement != null)
|
||||
{
|
||||
result = result.Append(matchedElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +98,148 @@ public sealed class SetAdditionalSettingsCommandLineCommand
|
||||
propertyInfo.SetValue(matchedElement, overriddenProperty.Value);
|
||||
}
|
||||
}
|
||||
|
||||
matchedElement = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static object GetNestedPropertyValue(object obj, string propertyPath)
|
||||
{
|
||||
if (obj == null || string.IsNullOrWhiteSpace(propertyPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var properties = propertyPath.Split('.');
|
||||
object currentObject = obj;
|
||||
PropertyInfo currentProperty = null;
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (currentObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
currentProperty = currentObject.GetType().GetProperty(property);
|
||||
if (currentProperty == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
currentObject = currentProperty.GetValue(currentObject);
|
||||
}
|
||||
|
||||
return currentObject;
|
||||
}
|
||||
|
||||
// To apply changes to a generic collection, we must recreate it and assign it to the property
|
||||
private static object CreateCompatibleCollection(Type collectionType, Type elementType, IEnumerable<object> newValues)
|
||||
{
|
||||
if (typeof(IList<>).MakeGenericType(elementType).IsAssignableFrom(collectionType) ||
|
||||
typeof(ObservableCollection<>).MakeGenericType(elementType).IsAssignableFrom(collectionType))
|
||||
{
|
||||
var concreteType = typeof(List<>).MakeGenericType(elementType);
|
||||
if (typeof(ObservableCollection<>).MakeGenericType(elementType).IsAssignableFrom(collectionType))
|
||||
{
|
||||
concreteType = typeof(ObservableCollection<>).MakeGenericType(elementType);
|
||||
}
|
||||
else if (collectionType.IsInterface || collectionType.IsAbstract)
|
||||
{
|
||||
concreteType = typeof(List<>).MakeGenericType(elementType);
|
||||
}
|
||||
|
||||
var list = (IList)Activator.CreateInstance(concreteType);
|
||||
foreach (var newValue in newValues)
|
||||
{
|
||||
list.Add(Convert.ChangeType(newValue, elementType, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
else if (typeof(IEnumerable<>).MakeGenericType(elementType).IsAssignableFrom(collectionType))
|
||||
{
|
||||
var listType = typeof(List<>).MakeGenericType(elementType);
|
||||
var list = (IList)Activator.CreateInstance(listType);
|
||||
foreach (var newValue in newValues)
|
||||
{
|
||||
list.Add(Convert.ChangeType(newValue, elementType, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void SetNestedPropertyValue(object obj, string propertyPath, IEnumerable<object> newValues)
|
||||
{
|
||||
if (obj == null || string.IsNullOrWhiteSpace(propertyPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var properties = propertyPath.Split('.');
|
||||
object currentObject = obj;
|
||||
PropertyInfo currentProperty = null;
|
||||
|
||||
for (int i = 0; i < properties.Length - 1; i++)
|
||||
{
|
||||
if (currentObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentProperty = currentObject.GetType().GetProperty(properties[i]);
|
||||
if (currentProperty == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentObject = currentProperty.GetValue(currentObject);
|
||||
}
|
||||
|
||||
if (currentObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentProperty = currentObject.GetType().GetProperty(properties.Last());
|
||||
if (currentProperty == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var propertyType = currentProperty.PropertyType;
|
||||
var elementType = propertyType.GetGenericArguments()[0];
|
||||
|
||||
var newCollection = CreateCompatibleCollection(propertyType, elementType, newValues);
|
||||
|
||||
if (newCollection != null)
|
||||
{
|
||||
currentProperty.SetValue(currentObject, newCollection);
|
||||
}
|
||||
}
|
||||
|
||||
private static Type GetUnderlyingTypeOfCollection(IEnumerable<object> currentPropertyValuesArray)
|
||||
{
|
||||
Type collectionType = currentPropertyValuesArray.GetType();
|
||||
|
||||
if (!collectionType.IsGenericType)
|
||||
{
|
||||
throw new ArgumentException("Invalid json data supplied");
|
||||
}
|
||||
|
||||
Type[] genericArguments = collectionType.GetGenericArguments();
|
||||
if (genericArguments.Length > 0)
|
||||
{
|
||||
return genericArguments[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid json data supplied");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,14 +255,39 @@ public sealed class SetAdditionalSettingsCommandLineCommand
|
||||
return;
|
||||
}
|
||||
|
||||
var propertyValueInfo = settingsConfigType.GetProperty(additionalPropertiesInfo.RootPropertyName);
|
||||
var currentPropertyValue = propertyValueInfo.GetValue(settingsConfig);
|
||||
var currentPropertyValue = GetNestedPropertyValue(settingsConfig, additionalPropertiesInfo.PropertyPath);
|
||||
|
||||
// For now, only a certain data shapes are supported
|
||||
switch (additionalPropertiesInfo.RootObjectType)
|
||||
switch (additionalPropertiesInfo.PropertyType)
|
||||
{
|
||||
case JsonValueKind.Array:
|
||||
ExecuteRootArray(settings.RootElement.EnumerateArray(), currentPropertyValue as IEnumerable<object>);
|
||||
if (currentPropertyValue == null)
|
||||
{
|
||||
currentPropertyValue = new JsonArray();
|
||||
}
|
||||
|
||||
IEnumerable<JsonElement> propertiesToSet = null;
|
||||
|
||||
// Powershell ConvertTo-Json call omits wrapping a single value in an array, so we must do it here
|
||||
if (settings.RootElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var wrapperArray = new JsonArray();
|
||||
wrapperArray.Add(settings.RootElement);
|
||||
propertiesToSet = (IEnumerable<JsonElement>)wrapperArray.GetEnumerator();
|
||||
}
|
||||
else if (settings.RootElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
propertiesToSet = settings.RootElement.EnumerateArray().AsEnumerable();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid json data supplied");
|
||||
}
|
||||
|
||||
var newPropertyValue = ExecuteRootArray(propertiesToSet, currentPropertyValue as IEnumerable<object>);
|
||||
|
||||
SetNestedPropertyValue(settingsConfig, additionalPropertiesInfo.PropertyPath, newPropertyValue);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
|
||||
Reference in New Issue
Block a user