@ -15,21 +15,12 @@ namespace Perspex.Markup.Data
{
internal class IndexerNode : ExpressionNode
{
private readonly int [ ] _ intArgs ;
public IndexerNode ( IList < object > arguments )
public IndexerNode ( IList < string > arguments )
{
Arguments = arguments ;
var intArgs = Arguments . OfType < int > ( ) . ToArray ( ) ;
if ( intArgs . Length = = arguments . Count )
{
_ intArgs = intArgs ;
}
}
public IList < object > Arguments { get ; }
public IList < string > Arguments { get ; }
protected override void SubscribeAndUpdate ( object target )
{
@ -75,29 +66,41 @@ namespace Perspex.Markup.Data
private void CollectionChanged ( object sender , NotifyCollectionChangedEventArgs e )
{
bool update = false ;
switch ( e . Action )
var update = false ;
if ( sender is IList )
{
object indexObject ;
if ( ! TypeUtilities . TryConvert ( typeof ( int ) , Arguments [ 0 ] , CultureInfo . InvariantCulture , out indexObject ) )
{
return ;
}
var index = ( int ) indexObject ;
switch ( e . Action )
{
case NotifyCollectionChangedAction . Add :
update = index > = e . NewStartingIndex ;
break ;
case NotifyCollectionChangedAction . Remove :
update = index > = e . OldStartingIndex ;
break ;
case NotifyCollectionChangedAction . Replace :
update = index > = e . NewStartingIndex & &
index < e . NewStartingIndex + e . NewItems . Count ;
break ;
case NotifyCollectionChangedAction . Move :
update = ( index > = e . NewStartingIndex & &
index < e . NewStartingIndex + e . NewItems . Count ) | |
( index > = e . OldStartingIndex & &
index < e . OldStartingIndex + e . OldItems . Count ) ;
break ;
case NotifyCollectionChangedAction . Reset :
update = true ;
break ;
}
}
else
{
case NotifyCollectionChangedAction . Add :
update = _ intArgs [ 0 ] > = e . NewStartingIndex ;
break ;
case NotifyCollectionChangedAction . Remove :
update = _ intArgs [ 0 ] > = e . OldStartingIndex ;
break ;
case NotifyCollectionChangedAction . Replace :
update = _ intArgs [ 0 ] > = e . NewStartingIndex & &
_ intArgs [ 0 ] < e . NewStartingIndex + e . NewItems . Count ;
break ;
case NotifyCollectionChangedAction . Move :
update = ( _ intArgs [ 0 ] > = e . NewStartingIndex & &
_ intArgs [ 0 ] < e . NewStartingIndex + e . NewItems . Count ) | |
( _ intArgs [ 0 ] > = e . OldStartingIndex & &
_ intArgs [ 0 ] < e . OldStartingIndex + e . OldItems . Count ) ;
break ;
case NotifyCollectionChangedAction . Reset :
update = true ;
break ;
update = true ;
}
if ( update )
@ -110,72 +113,130 @@ namespace Perspex.Markup.Data
{
var typeInfo = target . GetType ( ) . GetTypeInfo ( ) ;
var list = target as IList ;
if ( typeInfo . IsArray & & _ intArgs ! = null )
var dictionary = target as IDictionary ;
//TODO: Implement array as special case. It doesn't have an indexer property.
var indexerProperty = GetIndexer ( typeInfo ) ;
var indexerParameters = indexerProperty ? . GetIndexParameters ( ) ;
if ( indexerProperty ! = null & & indexerParameters . Length = = Arguments . Count )
{
var array = ( Array ) target ;
if ( InBounds ( _ intArgs , array ) )
var convertedObjectArray = new object [ indexerParameters . Length ] ;
for ( int i = 0 ; i < Arguments . Count ; i + + )
{
return array . GetValue ( _ intArgs ) ;
object temp = null ;
if ( ! TypeUtilities . TryConvert ( indexerParameters [ i ] . ParameterType , Arguments [ i ] , CultureInfo . InvariantCulture , out temp ) )
{
return PerspexProperty . UnsetValue ;
}
convertedObjectArray [ i ] = temp ;
}
}
else if ( target is IList & & _ intArgs ? . Length = = 1 )
{
if ( _ intArgs [ 0 ] < list . Count )
var intArgs = convertedObjectArray . OfType < int > ( ) . ToArray ( ) ;
// Try special cases where we can validate indicies
if ( typeInfo . IsArray )
{
return list [ _ intArgs [ 0 ] ] ;
return GetValueFromArray ( ( Array ) target , intArgs ) ;
}
}
else
{
var indexerProperty = GetIndexer ( typeInfo ) ;
var indexerParameters = indexerProperty ? . GetIndexParameters ( ) ;
if ( indexerProperty ! = null & & indexerParameters . Length = = Arguments . Count )
else if ( Arguments . Count = = 1 )
{
var convertedObjectArray = new object [ indexerParameters . Length ] ;
for ( int i = 0 ; i < Arguments . Count ; i + + )
if ( list ! = null )
{
if ( intArgs . Length = = Arguments . Count & & intArgs [ 0 ] > = 0 & & intArgs [ 0 ] < list . Count )
{
return list [ intArgs [ 0 ] ] ;
}
return PerspexProperty . UnsetValue ;
}
else if ( dictionary ! = null )
{
object temp = null ;
if ( ! TypeUtilities . TryConvert ( indexerParameters [ i ] . ParameterType , Arguments [ i ] , CultureInfo . InvariantCulture , out temp ) )
if ( dictionary . Contains ( convertedObjectArray [ 0 ] ) )
{
return PerspexProperty . UnsetValue ;
return dictionary [ convertedObjectArray [ 0 ] ] ;
}
convertedObjectArray [ i ] = temp ;
return PerspexProperty . UnsetValue ;
}
return indexerProperty . GetValue ( target , convertedObjectArray ) ;
else
{
// Fallback to unchecked access
return indexerProperty . GetValue ( target , convertedObjectArray ) ;
}
}
else
{
// Fallback to unchecked access
return indexerProperty . GetValue ( target , convertedObjectArray ) ;
}
}
// Multidimensional arrays end up here because the indexer search picks up the IList indexer instead of the
// multidimensional indexer, which doesn't take the same number of arguments
else if ( typeInfo . IsArray )
{
return GetValueFromArray ( ( Array ) target ) ;
}
return PerspexProperty . UnsetValue ;
}
private static PropertyInfo GetIndexer ( TypeInfo typeInfo )
private object GetValueFromArray ( Array array )
{
PropertyInfo indexer ;
// Check for the default indexer name first to make this faster.
// This will only be false when a class in VB has a custom indexer name.
if ( ( indexer = typeInfo . GetDeclaredProperty ( CommonPropertyNames . IndexerName ) ) ! = null )
int [ ] intArgs ;
if ( ! ConvertArgumentsToInts ( out intArgs ) )
return PerspexProperty . UnsetValue ;
return GetValueFromArray ( array , intArgs ) ;
}
private object GetValueFromArray ( Array array , int [ ] indicies )
{
if ( ValidBounds ( indicies , array ) )
{
return indexer ;
return array . GetValue ( indicies ) ;
}
foreach ( var property in typeInfo . DeclaredProperties )
return PerspexProperty . UnsetValue ;
}
private bool ConvertArgumentsToInts ( out int [ ] intArgs )
{
intArgs = new int [ Arguments . Count ] ;
for ( int i = 0 ; i < Arguments . Count ; + + i )
{
if ( property . GetIndexParameters ( ) . Any ( ) )
object value ;
if ( ! TypeUtilities . TryConvert ( typeof ( int ) , Arguments [ i ] , CultureInfo . InvariantCulture , out value ) )
{
return property ;
return false ;
}
intArgs [ i ] = ( int ) value ;
}
return true ;
}
private static PropertyInfo GetIndexer ( TypeInfo typeInfo )
{
PropertyInfo indexer ;
for ( ; typeInfo ! = null ; typeInfo = typeInfo . BaseType ? . GetTypeInfo ( ) )
{
// Check for the default indexer name first to make this faster.
// This will only be false when a class in VB has a custom indexer name.
if ( ( indexer = typeInfo . GetDeclaredProperty ( CommonPropertyNames . IndexerName ) ) ! = null )
{
return indexer ;
}
foreach ( var property in typeInfo . DeclaredProperties )
{
if ( property . GetIndexParameters ( ) . Any ( ) )
{
return property ;
}
}
}
return null ;
}
private bool InBounds ( int [ ] args , Array array )
private bool Valid Bounds( int [ ] indicie s, Array array )
{
if ( args . Length = = array . Rank )
if ( indicie s. Length = = array . Rank )
{
for ( var i = 0 ; i < args . Length ; + + i )
for ( var i = 0 ; i < indicie s. Length ; + + i )
{
if ( args [ i ] > = array . GetLength ( i ) )
if ( indicie s[ i ] > = array . GetLength ( i ) )
{
return false ;
}