@@ -133,7 +133,8 @@ protected static Expression BinaryExpression(IEnumerable<Expression> expressions
133133 return expressions . Aggregate ( methodExp ) ;
134134 }
135135
136- static readonly Regex _regexIndexed = new Regex ( @"(\w+)\[(\d+)\]" , RegexOptions . Compiled ) ;
136+ private static readonly Regex _regexIndexed =
137+ new Regex ( @"(?'Collection'\w+)\[(?:(?'Index'\d+)|(?:['""](?'Key'\w+)[""']))\]" , RegexOptions . Compiled ) ;
137138
138139 private static Expression GetProperty ( Expression param , string propname )
139140 {
@@ -146,26 +147,38 @@ private static Expression GetProperty(Expression param, string propname)
146147 var isIndexed = _regexIndexed . Match ( childprop ) ;
147148 if ( isIndexed . Success )
148149 {
149- var collectionname = isIndexed . Groups [ 1 ] . Value ;
150- var index = Int32 . Parse ( isIndexed . Groups [ 2 ] . Value ) ;
150+ var indexType = typeof ( int ) ;
151+ var collectionname = isIndexed . Groups [ "Collection" ] . Value ;
151152 var collectionProp = propertyType . GetProperty ( collectionname ) ;
152153 if ( collectionProp == null )
153- throw new RulesException (
154- $ "Cannot find collection property { collectionname } in class { propertyType . Name } (\" { propname } \" )") ;
154+ throw new RulesException (
155+ $ "Cannot find collection property { collectionname } in class { propertyType . Name } (\" { propname } \" )") ;
155156 var collexpr = Expression . PropertyOrField ( propExpression , collectionname ) ;
156157
158+ Expression expIndex ;
159+ if ( isIndexed . Groups [ "Index" ] . Success )
160+ {
161+ var index = Int32 . Parse ( isIndexed . Groups [ "Index" ] . Value ) ;
162+ expIndex = Expression . Constant ( index ) ;
163+ }
164+ else
165+ {
166+ expIndex = Expression . Constant ( isIndexed . Groups [ "Key" ] . Value ) ;
167+ indexType = typeof ( string ) ;
168+ }
169+
157170 var collectionType = collexpr . Type ;
158171 if ( collectionType . IsArray )
159172 {
160- propExpression = Expression . ArrayAccess ( collexpr , Expression . Constant ( index ) ) ;
173+ propExpression = Expression . ArrayAccess ( collexpr , expIndex ) ;
161174 propertyType = propExpression . Type ;
162175 }
163176 else
164177 {
165- var getter = collectionType . GetMethod ( "get_Item" , new Type [ ] { typeof ( Int32 ) } ) ;
178+ var getter = collectionType . GetMethod ( "get_Item" , new Type [ ] { indexType } ) ;
166179 if ( getter == null )
167180 throw new RulesException ( $ "'{ collectionname } ({ collectionType . Name } ) cannot be indexed") ;
168- propExpression = Expression . Call ( collexpr , getter , Expression . Constant ( index ) ) ;
181+ propExpression = Expression . Call ( collexpr , getter , expIndex ) ;
169182 propertyType = getter . ReturnType ;
170183 }
171184 }
@@ -412,25 +425,58 @@ private static Expression StringToExpression(object value, Type propType)
412425 safevalue = null ;
413426 else if ( propType . IsEnum )
414427 safevalue = Enum . Parse ( propType , txt ) ;
415- else if ( propType . Name == "Nullable`1" )
428+ else
416429 {
417- valuetype = Nullable . GetUnderlyingType ( propType ) ;
418- safevalue = Convert . ChangeType ( value , valuetype ) ;
430+ if ( propType . Name == "Nullable`1" )
431+ valuetype = Nullable . GetUnderlyingType ( propType ) ;
432+
433+ safevalue = IsTime ( txt , propType ) ?? Convert . ChangeType ( value , valuetype ) ;
419434 }
420- else
421- safevalue = Convert . ChangeType ( value , valuetype ) ;
422435 }
423- else if ( propType . Name == "Nullable`1" )
436+ else
424437 {
425- valuetype = Nullable . GetUnderlyingType ( propType ) ;
438+ if ( propType . Name == "Nullable`1" )
439+ valuetype = Nullable . GetUnderlyingType ( propType ) ;
426440 safevalue = Convert . ChangeType ( value , valuetype ) ;
427441 }
428- else
429- safevalue = Convert . ChangeType ( value , valuetype ) ;
430442
431443 return Expression . Constant ( safevalue , propType ) ;
432444 }
433445
446+ private static readonly Regex reNow = new Regex ( @"#NOW([-+])(\d+)([SMHDY])" , RegexOptions . IgnoreCase
447+ | RegexOptions . Compiled
448+ | RegexOptions . Singleline ) ;
449+
450+ private static DateTime ? IsTime ( string text , Type targetType )
451+ {
452+ if ( targetType != typeof ( DateTime ) && targetType != typeof ( DateTime ? ) )
453+ return null ;
454+
455+ var match = reNow . Match ( text ) ;
456+ if ( ! match . Success )
457+ return null ;
458+
459+ var amt = Int32 . Parse ( match . Groups [ 2 ] . Value ) ;
460+ if ( match . Groups [ 1 ] . Value == "-" )
461+ amt = - amt ;
462+
463+ switch ( Char . ToUpperInvariant ( match . Groups [ 3 ] . Value [ 0 ] ) )
464+ {
465+ case 'S' :
466+ return DateTime . Now . AddSeconds ( amt ) ;
467+ case 'M' :
468+ return DateTime . Now . AddMinutes ( amt ) ;
469+ case 'H' :
470+ return DateTime . Now . AddHours ( amt ) ;
471+ case 'D' :
472+ return DateTime . Now . AddDays ( amt ) ;
473+ case 'Y' :
474+ return DateTime . Now . AddYears ( amt ) ;
475+ }
476+ // it should not be possible to reach here.
477+ throw new ArgumentException ( ) ;
478+ }
479+
434480 private static Type ElementType ( Type seqType )
435481 {
436482 Type ienum = FindIEnumerable ( seqType ) ;
@@ -508,12 +554,14 @@ public static bool IsSimpleType(Type type)
508554 ;
509555 }
510556 public static BindingFlags flags = BindingFlags . Instance | BindingFlags . Public ;
511- public static List < Member > GetFields ( System . Type type , string memberName = null , string parentPath = null )
557+ public static List < Member > GetFields ( Type type , string memberName = null , string parentPath = null )
512558 {
513559 List < Member > toReturn = new List < Member > ( ) ;
514- var fi = new Member ( ) ;
515- fi . Name = string . IsNullOrEmpty ( parentPath ) ? memberName : $ "{ parentPath } .{ memberName } ";
516- fi . Type = type . ToString ( ) ;
560+ var fi = new Member
561+ {
562+ Name = string . IsNullOrEmpty ( parentPath ) ? memberName : $ "{ parentPath } .{ memberName } ",
563+ Type = type . ToString ( )
564+ } ;
517565 fi . PossibleOperators = Member . Operators ( type , string . IsNullOrEmpty ( fi . Name ) ) ;
518566 toReturn . Add ( fi ) ;
519567 if ( ! Member . IsSimpleType ( type ) )
@@ -589,7 +637,7 @@ public static bool IsGenericList(Type type)
589637 mreOperator . IsDouble . ToString ( "g" ) ,
590638 mreOperator . IsDecimal . ToString ( "g" )
591639 } ;
592- public static List < Operator > Operators ( System . Type type , bool addLogicOperators = false , bool noOverloads = true )
640+ public static List < Operator > Operators ( Type type , bool addLogicOperators = false , bool noOverloads = true )
593641 {
594642 List < Operator > operators = new List < Operator > ( ) ;
595643 if ( addLogicOperators )
@@ -788,10 +836,10 @@ public static DataRule Create(string member, mreOperator oper, object target, Ty
788836
789837 internal static class Placeholder
790838 {
791- public static int Int ;
792- public static float Float ;
793- public static double Double ;
794- public static decimal Decimal ;
839+ public static int Int = 0 ;
840+ public static float Float = 0.0f ;
841+ public static double Double = 0.0 ;
842+ public static decimal Decimal = 0.0m ;
795843 }
796844
797845 // Nothing specific to MRE. Can be moved to a more common location
@@ -880,7 +928,7 @@ public enum mreOperator
880928 /// <summary>
881929 /// Checks that a string value matches a Regex expression
882930 /// </summary>
883- IsMatch = 100 ,
931+ IsMatch = 100 ,
884932 /// <summary>
885933 /// Checks that a value can be 'TryParsed' to an Int32
886934 /// </summary>
0 commit comments