Skip to content

Commit

Permalink
Merge pull request PHOENIXCONTACT#44 from PHOENIXCONTACT/feature/entr…
Browse files Browse the repository at this point in the history
…ySerialize

Introduced EntrySerialize attribute as replacement for EditorBrowsable
  • Loading branch information
Toxantron authored Sep 14, 2020
2 parents f52625f + 41bf973 commit 0365ce6
Show file tree
Hide file tree
Showing 17 changed files with 418 additions and 229 deletions.
40 changes: 27 additions & 13 deletions docs/articles/Platform/Serialization/EntryConvert.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,32 @@ dto.Properties[0].Value.Current = "Michael";
EntryConvert.UpdateInstance(fooObj, dto.Properties, serialization);
````

## Serialize Methods
## EntrySerialize Attribute

The [EntrySerializeAttribute](xref:Moryx.Serialization.EntrySerializeAttribute) will be handled by the [EntrySerializeSerialization](xref:Moryx.Serialization.EntrySerializeSerialization) which is a custom implementation of the [ICustomSerialization](xref:Moryx.Serialization.ICustomSerialization). This serialization evaluates the attribute with some defined rules depending on the serialized type:

### Serialize Properties

Properties are serialized by the following rulues by default:

| Class | Properties | Result |
|-------|------------|--------|
| Always | not relevant | All except "Never" |
| Never | not relevant | Only "Always" |
| Not defined | No "Always", No "Never" | All |
| Not defined | Some "Always", No/Some "Never" | Only "Always" |
| Not defined | No "Always", Some "Never" | All except "Never" |

### Serialize Methods

In the other sections you have learned that `EntryConvert` is able to serialize and deserialize objects. With the `GetMethods` and `InvokeMethod` features of `EntryConvert` you are able to build your own `RPC (Remote Procedure Call)` service.

To enable the `RPC` features of `EntryConvert` you need to use the [EditorBrowsableSerialization](xref:Moryx.Serialization.EditorBrowsableSerialization) serializer on `EntryConvert`. Then you add the [EditorBrowsableAttribute](xref:Moryx.Serialization.EditorBrowsableAttribute) to all private/public methods or properties you want to expose.
To enable the `RPC` features of `EntryConvert` you need to use the [EntrySerializeSerialization](xref:Moryx.Serialization.EntrySerializeSerialization) serializer on `EntryConvert`. Then you add the [EntrySerializeAttribute](xref:Moryx.Serialization.EntrySerializeAttribute) to all private/public methods or properties you want to expose. The serialization only serializes methods with the attribute defined.

````cs
public class MyLittleRPC
{
[EditorBrowsable, Description("Does soemthing parameterized")]
[EntrySerialize, Description("Does soemthing parameterized")]
public bool DoSomething(MyParams parameters)
{
return true;
Expand All @@ -199,23 +215,21 @@ The serialization will be done by the method ``
````cs
public MethodEntry[] GetMethods(string moduleName)
{
return EntryConvert.EncodeMethods(MyLittleRPC, CreateSerialization()).ToArray();
}

private ICustomSerialization CreateSerialization()
{
return new AdvancedEditorBrowsableSerialization(Container, ConfigManager);
return EntryConvert.EncodeMethods(MyLittleRPC, new EntrySerializeSerialization()).ToArray();
}
````

## Invoke Methods
**Invoke Methods**

The following code allows you to expose and invoke you `RPC` methods or properties.

````cs
public Entry InvokeMethod(MethodEntry method)
{
var result = EntryConvert.InvokeMethod(MyLittleRPC, method, CreateSerialization());
return result;
return EntryConvert.InvokeMethod(MyLittleRPC, method, new EntrySerializeSerialization());
}
````
````

### Serialize Constructors

In the previous sections, it was described that `EntryConvert` can also serialize constructors. The [EntrySerializeSerialization](xref:Moryx.Serialization.EntrySerializeSerialization) only serializes constructos like methods with the [EntrySerializeAttribute](xref:Moryx.Serialization.EntrySerializeAttribute) defined.
3 changes: 2 additions & 1 deletion src/DependentTestModule/ModuleController/ModuleConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.ComponentModel;
using Moryx.Logging;
using Moryx.Runtime.Modules;
using Moryx.Serialization;

namespace Moryx.DependentTestModule
{
Expand All @@ -22,7 +23,7 @@ public void ExecuteCommand(string[] args, Action<string> outputStream)
outputStream("The DependentTestModule does not provide any commands!");
}

[EditorBrowsable, Description("Creates a log message for the given log level")]
[EntrySerialize, Description("Creates a log message for the given log level")]
public void CreateTestLogMessage(LogLevel logLevel)
{
switch (logLevel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
<Compile Include="Plugins\Modules\Wcf\Request\SaveConfigRequest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Plugins\Modules\Wcf\Models\ServerModuleModel.cs" />
<Compile Include="Plugins\Modules\Serialization\AdvancedEditorBrowsableSerialization.cs" />
<Compile Include="Plugins\Modules\Serialization\AdvancedEntrySerializeSerialization.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Moryx.Model\Moryx.Model.csproj">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,17 @@
namespace Moryx.Runtime.Maintenance.Plugins.Modules
{
/// <inheritdoc />
internal class AdvancedEditorBrowsableSerialization : PossibleValuesSerialization
internal class AdvancedEntrySerializeSerialization : PossibleValuesSerialization
{
private static readonly EditorBrowsableSerialization EditorBrowsableFilter = new EditorBrowsableSerialization();
private static readonly EntrySerializeSerialization EntrySerializeFilter = new EntrySerializeSerialization();

/// <inheritdoc />
public AdvancedEditorBrowsableSerialization(IContainer container, IEmptyPropertyProvider emptyPropertyProvider) : base(container, emptyPropertyProvider)
public AdvancedEntrySerializeSerialization(IContainer container, IEmptyPropertyProvider emptyPropertyProvider) : base(container, emptyPropertyProvider)
{
}

/// <inheritdoc />
public override IEnumerable<MethodInfo> GetMethods(Type sourceType)
{
return EditorBrowsableFilter.GetMethods(sourceType);
}
public override IEnumerable<MethodInfo> GetMethods(Type sourceType) =>
EntrySerializeFilter.GetMethods(sourceType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public MethodEntry[] GetMethods(string moduleName)

if (serverModule?.Console != null)
{
methods = EntryConvert.EncodeMethods(serverModule.Console, CreateEditorBrowsableSerialization(serverModule)).ToArray();
methods = EntryConvert.EncodeMethods(serverModule.Console, CreateEditorSerializeSerialization(serverModule)).ToArray();
}

return methods;
Expand All @@ -216,7 +216,7 @@ public Entry InvokeMethod(string moduleName, MethodEntry method)
try
{
result = EntryConvert.InvokeMethod(serverModule.Console, method,
CreateEditorBrowsableSerialization(serverModule));
CreateEditorSerializeSerialization(serverModule));
}
catch (Exception e)
{
Expand Down Expand Up @@ -254,10 +254,10 @@ private ICustomSerialization CreateSerialization(IModule module)
/// <summary>
/// Create serialization for this module
/// </summary>
private ICustomSerialization CreateEditorBrowsableSerialization(IModule module)
private ICustomSerialization CreateEditorSerializeSerialization(IModule module)
{
var host = (IContainerHost)module;
return new AdvancedEditorBrowsableSerialization(host.Container, ConfigManager)
return new AdvancedEntrySerializeSerialization(host.Container, ConfigManager)
{
FormatProvider = Thread.CurrentThread.CurrentUICulture
};
Expand Down
61 changes: 0 additions & 61 deletions src/Moryx/Serialization/EditorBrowsableSerialization.cs

This file was deleted.

34 changes: 34 additions & 0 deletions src/Moryx/Serialization/EntryConvert/EntrySerializeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;

namespace Moryx.Serialization
{
[AttributeUsage(AttributeTargets.All)]
public class EntrySerializeAttribute : Attribute
{
/// <summary>
/// Configured mode for the entry to serialize
/// </summary>
public EntrySerializeMode Mode { get; }

/// <summary>
/// Creates a new instance of the <see cref="EntrySerializeAttribute"/>. The default mode is <see cref="EntrySerializeMode.Always"/>
/// </summary>
public EntrySerializeAttribute() : this(EntrySerializeMode.Always)
{
}

public EntrySerializeAttribute(EntrySerializeMode mode)
{
Mode = mode;
}
}

/// <summary>
/// Mode to for the <see cref="EntrySerializeAttribute"/>
/// </summary>
public enum EntrySerializeMode
{
Always,
Never
}
}
117 changes: 117 additions & 0 deletions src/Moryx/Serialization/EntrySerializeSerialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2020, Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Moryx.Tools;

namespace Moryx.Serialization
{
/// <summary>
/// Filters methods and properties with <see cref="EntrySerializeAttribute"/> set.
/// </summary>
public class EntrySerializeSerialization : DefaultSerialization
{
private static readonly Lazy<EntrySerializeSerialization> LazyInstance
= new Lazy<EntrySerializeSerialization>(LazyThreadSafetyMode.ExecutionAndPublication);

/// <summary>
/// Singleton instance
/// </summary>
public static EntrySerializeSerialization Instance => LazyInstance.Value;

/// <inheritdoc />
public override IEnumerable<ConstructorInfo> GetConstructors(Type sourceType)
{
var constructors = from ctor in base.GetConstructors(sourceType)
let mode = EvaluateSerializeMode(ctor)
where mode.HasValue && mode.Value == EntrySerializeMode.Always
select ctor;

return constructors;
}

/// <inheritdoc />
public override IEnumerable<MethodInfo> GetMethods(Type sourceType)
{
var methods = sourceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(m => !m.IsSpecialName);
methods = from method in methods
let mode = EvaluateSerializeMode(method)
where mode.HasValue && mode.Value == EntrySerializeMode.Always
select method;

return methods;
}

/// <inheritdoc />
public override IEnumerable<PropertyInfo> GetProperties(Type sourceType)
{
var properties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToArray();

var sourceTypeMode = EvaluateSerializeMode(sourceType);
var propertyModes = properties.Select(EvaluateSerializeMode).ToArray();

var alwaysProperties = propertyModes
.Where(pm => pm.Mode.HasValue && pm.Mode == EntrySerializeMode.Always)
.Select(pm => pm.Property).ToArray();

var neverProperties = propertyModes
.Where(pm => pm.Mode.HasValue && pm.Mode == EntrySerializeMode.Never)
.Select(pm => pm.Property).ToArray();

// Class: Always
// -> All properties except "Never"
if (sourceTypeMode.HasValue && sourceTypeMode.Value == EntrySerializeMode.Always)
return properties.Except(neverProperties);

// Class: Never
// -> Only "Always" properties
if (sourceTypeMode.HasValue && sourceTypeMode.Value == EntrySerializeMode.Never)
return alwaysProperties;

// Class: Not defined

// "Always" and "Never" properties, "Always" properties but no "Never"
// Only "Never" properties
if (alwaysProperties.Length > 0)
return alwaysProperties;

// No "Always" properties, but "Never" properties
// -> Properties except "Never"
if (neverProperties.Length > 0)
return properties.Except(neverProperties);

// No property mode defined, No "Always", No "Never"
// -> All property
return properties;
}

/// <summary>
/// Checks if the <see cref="EntrySerializeAttribute"/> is existent and activated
/// </summary>
private static EntrySerializeMode? EvaluateSerializeMode(ICustomAttributeProvider attributeProvider)
{
var entrySerializeAttr = attributeProvider.GetCustomAttribute<EntrySerializeAttribute>();
return entrySerializeAttr?.Mode;
}

private static PropertyMode EvaluateSerializeMode(PropertyInfo property)
{
return new PropertyMode
{
Property = property,
Mode = EvaluateSerializeMode((ICustomAttributeProvider)property)
};
}

private class PropertyMode
{
public PropertyInfo Property { get; set; }

public EntrySerializeMode? Mode { get; set; }
}
}
}
Loading

0 comments on commit 0365ce6

Please sign in to comment.