@ -2,22 +2,18 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System ;
using System.Reactive.Disposables ;
using System.Reactive.Linq ;
using System.Reactive.Subjects ;
using Avalonia.Data ;
namespace Avalonia.Data.Core
{
internal abstract class ExpressionNode : ISubject < object >
internal abstract class ExpressionNode
{
private static readonly object CacheInvalid = new object ( ) ;
protected static readonly WeakReference UnsetReference =
new WeakReference ( AvaloniaProperty . UnsetValue ) ;
private WeakReference _ target = UnsetReference ;
private IDisposable _ valueSubscription ;
private IObserver < object > _ observer ;
private Action < object > _ subscriber ;
private bool _l istening ;
protected WeakReference LastValue { get ; private set ; }
@ -33,92 +29,66 @@ namespace Avalonia.Data.Core
var oldTarget = _ target ? . Target ;
var newTarget = value . Target ;
var running = _ valueSubscription ! = null ;
if ( ! ReferenceEquals ( oldTarget , newTarget ) )
{
_ valueSubscription ? . Dispose ( ) ;
_ valueSubscription = null ;
if ( _l istening )
{
StopListening ( ) ;
}
_ target = value ;
if ( running )
if ( _ subscriber ! = null )
{
_ valueSubscription = StartListening ( ) ;
StartListening ( ) ;
}
}
}
}
public IDisposable Subscribe ( IObserver < object > observ er)
public void Subscribe ( Action < object > subscrib er)
{
if ( _ observ er ! = null )
if ( _ subscrib er ! = null )
{
throw new AvaloniaInternalException ( "ExpressionNode can only be subscribed once." ) ;
}
_ observer = observer ;
var nextSubscription = Next ? . Subscribe ( this ) ;
_ valueSubscription = StartListening ( ) ;
return Disposable . Create ( ( ) = >
{
_ valueSubscription ? . Dispose ( ) ;
_ valueSubscription = null ;
LastValue = null ;
nextSubscription ? . Dispose ( ) ;
_ observer = null ;
} ) ;
_ subscriber = subscriber ;
Next ? . Subscribe ( NextValueChanged ) ;
StartListening ( ) ;
}
void IObserver < object > . OnCompleted ( )
public void Unsubscribe ( )
{
throw new AvaloniaInternalException ( "ExpressionNode.OnCompleted should not be called." ) ;
}
Next ? . Unsubscribe ( ) ;
void IObserver < object > . OnError ( Exception error )
{
throw new AvaloniaInternalException ( "ExpressionNode.OnError should not be called." ) ;
if ( _l istening )
{
StopListening ( ) ;
}
LastValue = null ;
_ subscriber = null ;
}
void IObserver < object > . OnNext ( object value )
protected virtual void StartListeningCore ( WeakReference referenc e)
{
Next ValueChanged( value ) ;
ValueChanged ( reference . Target ) ;
}
protected virtual IObservable < object > StartListeningCore ( WeakReference reference )
protected virtual void StopListeningCore ( )
{
return Observable . Return ( reference . Target ) ;
}
protected virtual void NextValueChanged ( object value )
{
var bindingBroken = BindingNotification . ExtractError ( value ) as MarkupBindingChainException ;
bindingBroken ? . AddNode ( Description ) ;
_ observer . OnNext ( value ) ;
}
private IDisposable StartListening ( )
{
var target = _ target . Target ;
IObservable < object > source ;
if ( target = = null )
{
source = Observable . Return ( TargetNullNotification ( ) ) ;
}
else if ( target = = AvaloniaProperty . UnsetValue )
{
source = Observable . Empty < object > ( ) ;
}
else
{
source = StartListeningCore ( _ target ) ;
}
return source . Subscribe ( ValueChanged ) ;
_ subscriber ( value ) ;
}
private void ValueChanged ( object value )
protected void ValueChanged ( object value )
{
var notification = value as BindingNotification ;
@ -131,24 +101,50 @@ namespace Avalonia.Data.Core
}
else
{
_ observer . OnNext ( value ) ;
_ subscriber ( value ) ;
}
}
else
{
LastValue = new WeakReference ( notification . Value ) ;
if ( Next ! = null )
{
Next . Target = new WeakReference ( notification . Value ) ;
}
if ( Next = = null | | notification . Error ! = null )
{
_ observer . OnNext ( value ) ;
_ subscriber ( value ) ;
}
}
}
private void StartListening ( )
{
var target = _ target . Target ;
if ( target = = null )
{
ValueChanged ( TargetNullNotification ( ) ) ;
_l istening = false ;
}
else if ( target ! = AvaloniaProperty . UnsetValue )
{
StartListeningCore ( _ target ) ;
_l istening = true ;
}
else
{
_l istening = false ;
}
}
private void StopListening ( )
{
StopListeningCore ( ) ;
}
private BindingNotification TargetNullNotification ( )
{
return new BindingNotification (