Skip to content

Commit

Permalink
Implement selective children retrieval
Browse files Browse the repository at this point in the history
References #21
  • Loading branch information
andreashuber-lawo committed Apr 26, 2016
1 parent 4dfd3a1 commit c8a723e
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 44 deletions.
8 changes: 4 additions & 4 deletions Lawo.EmberPlusSharp/Model/CollectionNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ internal sealed override Element ReadNewChildContents(
}

[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Method is not public, CA bug?")]
internal sealed override bool ChangeVisibility(Element child)
internal sealed override bool ChangeVisibility(IElement child)
{
base.ChangeVisibility(child);

if (child.RetrieveDetails)
if (child.IsOnline)
{
this.children.Add((TElement)(IElement)child);
this.children.Add((TElement)child);
}
else
{
this.children.Remove((TElement)(IElement)child);
this.children.Remove((TElement)child);
}

return true;
Expand Down
2 changes: 1 addition & 1 deletion Lawo.EmberPlusSharp/Model/DynamicFieldNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public ReadOnlyObservableCollection<IElement> DynamicChildren

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

internal sealed override bool ChangeVisibility(Element child)
internal sealed override bool ChangeVisibility(IElement child)
{
return DynamicNodeHelper.ChangeVisibility(base.ChangeVisibility, this.dynamicChildren, child);
}
Expand Down
4 changes: 2 additions & 2 deletions Lawo.EmberPlusSharp/Model/DynamicNodeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ internal static Element ReadDynamicChildContents(
}

internal static bool ChangeVisibility(
Func<Element, bool> baseImpl, ObservableCollection<IElement> dynamicChildren, Element child)
Func<IElement, bool> baseImpl, ObservableCollection<IElement> dynamicChildren, IElement child)
{
if (!baseImpl(child))
{
if (child.RetrieveDetails)
if (child.IsOnline)
{
dynamicChildren.Add(child);
}
Expand Down
2 changes: 1 addition & 1 deletion Lawo.EmberPlusSharp/Model/DynamicRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal sealed override Element ReadNewDynamicChildContents(
return DynamicNodeHelper.ReadDynamicChildContents(reader, actualType, context, out childRetrievalState);
}

internal sealed override bool ChangeVisibility(Element child)
internal sealed override bool ChangeVisibility(IElement child)
{
return DynamicNodeHelper.ChangeVisibility(base.ChangeVisibility, this.dynamicChildren, child);
}
Expand Down
44 changes: 27 additions & 17 deletions Lawo.EmberPlusSharp/Model/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,11 @@ public bool IsOnline

internal set
{
var oldValue = this.RetrieveDetails;

if (this.SetValue(ref this.isOnline, value))
{
// We're deliberately not simply setting this to Changed here, because we want to correctly handle
// the case when IsOnline is changed twice without being observed between the changes.
if (this.RetrieveDetailsChangeStatus == RetrieveDetailsChangeStatus.Unchanged)
{
this.RetrieveDetailsChangeStatus = RetrieveDetailsChangeStatus.Changed;
}
else if (this.RetrieveDetailsChangeStatus == RetrieveDetailsChangeStatus.Changed)
{
this.RetrieveDetailsChangeStatus = RetrieveDetailsChangeStatus.Unchanged;
}
this.AdaptRetrieveDetailsChangeStatus(oldValue);
}
}
}
Expand Down Expand Up @@ -148,6 +141,23 @@ internal virtual bool RetrieveDetails

internal RetrieveDetailsChangeStatus RetrieveDetailsChangeStatus { get; set; }

internal void AdaptRetrieveDetailsChangeStatus(bool oldValue)
{
if (this.RetrieveDetails != oldValue)
{
// We're deliberately not simply setting this to Changed here, because we want to correctly handle
// the case when IsOnline is changed twice without being observed between the changes.
if (this.RetrieveDetailsChangeStatus == RetrieveDetailsChangeStatus.Unchanged)
{
this.RetrieveDetailsChangeStatus = RetrieveDetailsChangeStatus.Changed;
}
else if (this.RetrieveDetailsChangeStatus == RetrieveDetailsChangeStatus.Changed)
{
this.RetrieveDetailsChangeStatus = RetrieveDetailsChangeStatus.Unchanged;
}
}
}

internal virtual RetrievalState RetrievalState
{
get { return RetrievalState.Complete; }
Expand Down Expand Up @@ -214,13 +224,13 @@ internal void SetConsumerValue<T>(ref T field, T newValue, [CallerMemberName] st
/// interested in. In all other cases, the state of the node is lowered to the lowest state of the
/// interesting children appearing in the payload.</para>
/// <para>This approach ensures that any node for which incomplete interesting children have been received will
/// be visited by <see cref="UpdateRetrievalState"/>. This is necessary because some providers send messages with
/// payloads where the same node appears multiple times. For example, the first time the state of the node may
/// be set to <see cref="RetrievalState.None"/>, due to the fact that there are indirect children for which a
/// <code>getDirectory</code> request needs to be sent. The second time the node appears only with direct and
/// indirect children that have the state <see cref="RetrievalState.Complete"/>. Now the state of the node cannot
/// be set to <see cref="RetrievalState.Complete"/> because then no <code>getDirectory</code> requests would be
/// issued for the children that appeared the first time.</para>
/// be visited by <see cref="UpdateRetrievalState"/>. This is necessary because some providers send messages
/// with payloads where the same node appears multiple times. For example, the first time the state of the node
/// may be set to <see cref="RetrievalState.None"/>, due to the fact that there are indirect children for which
/// a <code>getDirectory</code> request needs to be sent. The second time the node appears only with direct and
/// indirect children that have the state <see cref="RetrievalState.Complete"/>. Now the state of the node
/// cannot be set to <see cref="RetrievalState.Complete"/> because then no <code>getDirectory</code> requests
/// would be issued for the children that appeared the first time.</para>
/// <para>It follows that the state of a node cannot be set to its definitive value while its children are read.
/// Instead there needs to be a second step that visits all affected nodes and updates their state, which is
/// implemented by <see cref="UpdateRetrievalState"/>.</para></remarks>
Expand Down
2 changes: 1 addition & 1 deletion Lawo.EmberPlusSharp/Model/FieldNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal virtual Element ReadNewDynamicChildContents(
}

[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Method is not public, CA bug?")]
internal override bool ChangeVisibility(Element child)
internal override bool ChangeVisibility(IElement child)
{
base.ChangeVisibility(child);
MetaElement metaChild;
Expand Down
2 changes: 1 addition & 1 deletion Lawo.EmberPlusSharp/Model/MetaElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal abstract Element ReadContents(

internal abstract bool IsAvailable(IParent parent, bool throwIfMissing);

internal abstract void ChangeVisibility(IParent parent, Element element);
internal abstract void ChangeVisibility(IParent parent, IElement element);

////////////////////////////////////////////////////////////////////////////////////////////////////////////

Expand Down
6 changes: 3 additions & 3 deletions Lawo.EmberPlusSharp/Model/MetaElement1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ internal sealed override bool IsAvailable(IParent parent, bool throwIfMissing)
}

[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Method is not public, CA bug?")]
internal sealed override void ChangeVisibility(IParent parent, Element element)
internal sealed override void ChangeVisibility(IParent parent, IElement element)
{
if (!this.IsOptional && !element.RetrieveDetails)
if (!this.IsOptional && !element.IsOnline)
{
const string Format =
"The required property {0}.{1} in the node with the path {2} has been set offline by the provider.";
throw CreateRequiredPropertyException(parent, Format);
}

this.set((TMostDerived)parent, (TProperty)(element.RetrieveDetails ? element : null));
this.set((TMostDerived)parent, (TProperty)(element.IsOnline ? element : null));
parent.OnPropertyChanged(new PropertyChangedEventArgs(this.Property.Name));
}

Expand Down
4 changes: 2 additions & 2 deletions Lawo.EmberPlusSharp/Model/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ internal virtual bool GetIsRoot()
}

[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Method is not public, CA bug?")]
internal override bool ChangeVisibility(Element child)
internal override bool ChangeVisibility(IElement child)
{
if (child.RetrieveDetails)
if (child.IsOnline)
{
this.observableChildren.Add(child);
}
Expand Down
12 changes: 11 additions & 1 deletion Lawo.EmberPlusSharp/Model/NodeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ public ChildrenRetrievalPolicy ChildrenRetrievalPolicy
"value");
}

var oldValue = this.RetrieveDetails;
this.SetValue(ref this.childrenRetrievalPolicy, value);
this.AdaptRetrieveDetailsChangeStatus(oldValue);
}
}
}
Expand Down Expand Up @@ -84,6 +86,14 @@ internal NodeBase() : base(RetrievalState.None)
{
}

internal sealed override bool RetrieveDetails
{
get
{
return (this.ChildrenRetrievalPolicy != ChildrenRetrievalPolicy.None) && base.RetrieveDetails;
}
}

internal sealed override void SetContext(Context context)
{
base.SetContext(context);
Expand Down Expand Up @@ -170,7 +180,7 @@ internal virtual bool ReadChildrenCore(EmberReader reader)
/// <summary>Changes the visibility of <paramref name="child"/>.</summary>
/// <param name="child">The child to change the visibility for.</param>
/// <returns><c>true</c> if the visibility has been changed; otherwise, <c>false</c>.</returns>
internal virtual bool ChangeVisibility(Element child)
internal virtual bool ChangeVisibility(IElement child)
{
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions Lawo.EmberPlusSharp/Model/RetrieveDetailsChangeStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace Lawo.EmberPlusSharp.Model
{
internal enum RetrieveDetailsChangeStatus
{
/// <summary><see cref="Element.RetrieveDetails"/> has the value set during initialization or first read operation.
/// </summary>
/// <summary><see cref="Element.RetrieveDetails"/> has the value set during initialization or first read
/// operation.</summary>
Initialized,

/// <summary><see cref="Element.RetrieveDetails"/> has not been changed since
Expand Down
12 changes: 11 additions & 1 deletion Lawo.EmberPlusSharpTest/Lawo.EmberPlusSharpTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,17 @@
<EmbeddedResource Include="Model\Test\EmberDataPayloads\StreamLog.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Model\Test\EmberDataPayloads\ChildrenRetrievalPolicyLog.xml">
<EmbeddedResource Include="Model\Test\EmberDataPayloads\ChildrenRetrievalPolicyLog1.xml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Model\Test\EmberDataPayloads\ChildrenRetrievalPolicyLog2.xml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Model\Test\EmberDataPayloads\ChildrenRetrievalPolicyLog3.xml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
Expand Down
27 changes: 19 additions & 8 deletions Lawo.EmberPlusSharpTest/Model/ConsumerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ await MonitorConnection(
Assert.IsTrue(root.GetPath() == "/");
Assert.AreEqual(true, root.IsRoot);
Assert.AreEqual(true, root.IsOnline);
Assert.IsNull(root.Tag);
var randomValue = new Random().Next();
root.Tag = randomValue;
Assert.AreEqual(randomValue, root.Tag);

Assert.AreEqual(null, ((IParameter)root.GetChild("OctetstringParameter")).Minimum);
Assert.AreEqual(null, ((IParameter)root.GetChild("OctetstringParameter")).Maximum);
Expand Down Expand Up @@ -451,9 +455,9 @@ public void ChildrenRetrievalPolicyTest()
AsyncPump.Run(
async () =>
{
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.None);
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.DirectOnly);
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.All);
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.None, "ChildrenRetrievalPolicyLog1.xml");
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.DirectOnly, "ChildrenRetrievalPolicyLog2.xml");
await ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy.All, "ChildrenRetrievalPolicyLog3.xml");
});
}

Expand Down Expand Up @@ -1584,7 +1588,7 @@ private static void AssertAccess(RecursiveFieldNode node)
}
}

private static Task ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy policy)
private static Task ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy policy, string logName)
{
return TestWithRobot<ModelPayloads>(
async client =>
Expand All @@ -1611,16 +1615,23 @@ private static Task ChildrenRetrievalPolicyTestCoreAsync(ChildrenRetrievalPolicy
AssertThrow<ArgumentException>(() => root.ChildrenRetrievalPolicy -= 1, expectedMessage);
}

var childPolicy = policy == ChildrenRetrievalPolicy.All ?
ChildrenRetrievalPolicy.All : ChildrenRetrievalPolicy.None;
Assert.AreEqual(childPolicy, root.Node.ChildrenRetrievalPolicy);
if (policy == ChildrenRetrievalPolicy.None)
{
Assert.IsNull(root.Node);
}
else
{
var childPolicy = policy == ChildrenRetrievalPolicy.All ?
ChildrenRetrievalPolicy.All : ChildrenRetrievalPolicy.None;
Assert.AreEqual(childPolicy, root.Node.ChildrenRetrievalPolicy);
}
}
},
null,
null,
GlowTypes.Instance,
false,
"ChildrenRetrievalPolicyLog.xml");
logName);
}

[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Objects are disposed within the called method.")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2012-2016 Lawo AG (http://www.lawo.com). -->
<!-- Distributed under the Boost Software License, Version 1.0. -->
<!-- (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -->
<S101Log>
</S101Log>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2012-2016 Lawo AG (http://www.lawo.com). -->
<!-- Distributed under the Boost Software License, Version 1.0. -->
<!-- (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -->
<S101Log>
<Event type="Message" timeUtc="12:54:14.19" direction="Send" number="1">
<Slot>00</Slot>
<Command>EmberData 01 0A 02</Command>
<Payload>
<Root type="RootElementCollection">
<RootElement type="Command">
<number type="Integer">32</number>
</RootElement>
</Root>
</Payload>
</Event>
<Event type="Message" timeUtc="12:54:14.34" direction="Receive" number="1">
<Slot>00</Slot>
<Command>EmberData 01 0A 02</Command>
<Payload>
<Root type="RootElementCollection">
<RootElement type="Node">
<number type="Integer">1</number>
<contents type="Set">
<identifier type="UTF8String">Node</identifier>
</contents>
</RootElement>
</Root>
</Payload>
</Event>
</S101Log>

0 comments on commit c8a723e

Please sign in to comment.