@@ -23,6 +23,7 @@ public sealed class AdaptiveCardsTemplateVisitor : AdaptiveCardsTemplateParserBa
2323 {
2424 private Stack < DataContext > dataContext = new Stack < DataContext > ( ) ;
2525 private readonly JToken root ;
26+ private readonly JToken host ;
2627 private readonly Options options ;
2728 private ArrayList templateVisitorWarnings ;
2829
@@ -33,10 +34,12 @@ private sealed class DataContext
3334 {
3435 public JToken token ;
3536 public AdaptiveCardsTemplateSimpleObjectMemory AELMemory ;
36- public bool IsArrayType = false ;
37+ public bool IsArrayType ;
3738
3839 public JToken RootDataContext ;
40+ public JToken HostDataContext ;
3941 public const string rootKeyword = "$root" ;
42+ public const string hostKeyword = "$host" ;
4043 public const string dataKeyword = "$data" ;
4144 public const string indexKeyword = "$index" ;
4245
@@ -45,9 +48,10 @@ private sealed class DataContext
4548 /// </summary>
4649 /// <param name="jtoken">new data to kept as data context</param>
4750 /// <param name="rootDataContext">root data context</param>
48- public DataContext ( JToken jtoken , JToken rootDataContext )
51+ /// <param name="hostDataContext">optional host data context</param>
52+ public DataContext ( JToken jtoken , JToken rootDataContext , JToken hostDataContext = null )
4953 {
50- Init ( jtoken , rootDataContext ) ;
54+ Init ( jtoken , rootDataContext , hostDataContext ) ;
5155 }
5256
5357 /// <summary>
@@ -56,25 +60,30 @@ public DataContext(JToken jtoken, JToken rootDataContext)
5660 /// <exception cref="JsonException"><c>JToken.Parse(text)</c> can throw JsonException if <paramref name="text"/> is invalid json</exception>
5761 /// <param name="text">json in string</param>
5862 /// <param name="rootDataContext">a root data context</param>
59- public DataContext ( string text , JToken rootDataContext )
63+ /// <param name="hostDataContext">optional host data context</param>
64+ public DataContext ( string text , JToken rootDataContext , JToken hostDataContext = null )
6065 {
6166 // disable date parsing handling
62- var jsonReader = new JsonTextReader ( new StringReader ( text ) ) { DateParseHandling = DateParseHandling . None } ;
63- var jtoken = JToken . Load ( jsonReader ) ;
64- Init ( jtoken , rootDataContext ) ;
67+ using ( var jsonReader = new JsonTextReader ( new StringReader ( text ) ) { DateParseHandling = DateParseHandling . None } )
68+ {
69+ var jtoken = JToken . Load ( jsonReader ) ;
70+ Init ( jtoken , rootDataContext , hostDataContext ) ;
71+ }
6572 }
6673
6774 /// <summary>
6875 /// Initializer method that takes jtoken and root data context to initialize a data context object
6976 /// </summary>
7077 /// <param name="jtoken">current data context</param>
7178 /// <param name="rootDataContext">root data context</param>
72- private void Init ( JToken jtoken , JToken rootDataContext )
79+ /// <param name="hostDataContext">optional host data context</param>
80+ private void Init ( JToken jtoken , JToken rootDataContext , JToken hostDataContext )
7381 {
7482 AELMemory = ( jtoken is JObject ) ? new AdaptiveCardsTemplateSimpleObjectMemory ( jtoken ) : new AdaptiveCardsTemplateSimpleObjectMemory ( new JObject ( ) ) ;
7583
7684 token = jtoken ;
7785 RootDataContext = rootDataContext ;
86+ HostDataContext = hostDataContext ;
7887
7988 if ( jtoken is JArray )
8089 {
@@ -83,6 +92,7 @@ private void Init(JToken jtoken, JToken rootDataContext)
8392
8493 AELMemory . SetValue ( dataKeyword , token ) ;
8594 AELMemory . SetValue ( rootKeyword , rootDataContext ) ;
95+ AELMemory . SetValue ( hostKeyword , hostDataContext ) ;
8696 }
8797
8898 /// <summary>
@@ -94,7 +104,7 @@ public DataContext GetDataContextAtIndex(int index)
94104 {
95105 var jarray = token as JArray ;
96106 var jtokenAtIndex = jarray [ index ] ;
97- var dataContext = new DataContext ( jtokenAtIndex , RootDataContext ) ;
107+ var dataContext = new DataContext ( jtokenAtIndex , RootDataContext , HostDataContext ) ;
98108 dataContext . AELMemory . SetValue ( indexKeyword , index ) ;
99109 return dataContext ;
100110 }
@@ -104,23 +114,41 @@ public DataContext GetDataContextAtIndex(int index)
104114 /// a constructor for AdaptiveCardsTemplateVisitor
105115 /// </summary>
106116 /// <param name="nullSubstitutionOption">it will called upon when AEL finds no suitable functions registered in given AEL expression during evaluation the expression</param>
107- /// <param name="data">json data in string which will be set as a root data context</param>
108- public AdaptiveCardsTemplateVisitor ( Func < string , object > nullSubstitutionOption , string data = null )
117+ /// <param name="data">json data as string which will be set as a root data context</param>
118+ /// <param name="hostData">json data as string which will be set as the host data context</param>
119+ public AdaptiveCardsTemplateVisitor ( Func < string , object > nullSubstitutionOption , string data = null , string hostData = null )
109120 {
110- if ( data ? . Length != 0 )
121+ if ( ! String . IsNullOrEmpty ( hostData ) )
122+ {
123+ // parse and save host context
124+ try
125+ {
126+ using ( var jsonReader = new JsonTextReader ( new StringReader ( hostData ) ) { DateParseHandling = DateParseHandling . None } )
127+ {
128+ host = JToken . Load ( jsonReader ) ;
129+ }
130+ }
131+ catch ( JsonException innerException )
132+ {
133+ throw new AdaptiveTemplateException ( "Setting host data failed" , innerException ) ;
134+ }
135+ }
136+
137+ if ( ! String . IsNullOrEmpty ( data ) )
111138 {
112139 // set data as root data context
113140 try
114141 {
115- var jsonReader = new JsonTextReader ( new StringReader ( data ) ) { DateParseHandling = DateParseHandling . None } ;
116- root = JToken . Load ( jsonReader ) ;
117- PushDataContext ( data , root ) ;
142+ using ( var jsonReader = new JsonTextReader ( new StringReader ( data ) ) { DateParseHandling = DateParseHandling . None } )
143+ {
144+ root = JToken . Load ( jsonReader ) ;
145+ PushDataContext ( data , root ) ;
146+ }
118147 }
119148 catch ( JsonException innerException )
120149 {
121150 throw new AdaptiveTemplateException ( "Setting root data failed with given data context" , innerException ) ;
122151 }
123-
124152 }
125153
126154 // if null, set default option
@@ -148,7 +176,7 @@ private DataContext GetCurrentDataContext()
148176 /// <param name="rootDataContext">current root data context</param>
149177 private void PushDataContext ( string stringToParse , JToken rootDataContext )
150178 {
151- dataContext . Push ( new DataContext ( stringToParse , rootDataContext ) ) ;
179+ dataContext . Push ( new DataContext ( stringToParse , rootDataContext , host ) ) ;
152180 }
153181
154182 /// <summary>
@@ -177,13 +205,13 @@ private void PushTemplatedDataContext(string jpath)
177205 {
178206 if ( value is JToken jvalue )
179207 {
180- dataContext . Push ( new DataContext ( jvalue , parentDataContext . RootDataContext ) ) ;
208+ dataContext . Push ( new DataContext ( jvalue , parentDataContext . RootDataContext , parentDataContext . HostDataContext ) ) ;
181209
182210 }
183211 else
184212 {
185213 var serializedValue = JsonConvert . SerializeObject ( value ) ;
186- dataContext . Push ( new DataContext ( serializedValue , parentDataContext . RootDataContext ) ) ;
214+ dataContext . Push ( new DataContext ( serializedValue , parentDataContext . RootDataContext , parentDataContext . HostDataContext ) ) ;
187215 }
188216 }
189217 else
@@ -220,7 +248,7 @@ public ArrayList getTemplateVisitorWarnings()
220248 }
221249
222250 /// <summary>
223- /// antlr runtime wil call this method when parse tree's context is <see cref="AdaptiveCardsTemplateParser.TemplateDataContext"/>
251+ /// antlr runtime will call this method when parse tree's context is <see cref="AdaptiveCardsTemplateParser.TemplateDataContext"/>
224252 /// <para>It is used in parsing a pair that has $data as key</para>
225253 /// <para>It creates new data context, and set it as current memory scope</para>
226254 /// </summary>
@@ -254,7 +282,7 @@ public override AdaptiveCardsTemplateResult VisitTemplateData([NotNull] Adaptive
254282 var templateLiteral = ( templateStrings [ 0 ] as AdaptiveCardsTemplateParser . TemplatedStringContext ) . TEMPLATELITERAL ( ) ;
255283 try
256284 {
257- string templateLiteralExpression = templateLiteral . GetText ( ) ;
285+ string templateLiteralExpression = templateLiteral . GetText ( ) ;
258286 PushTemplatedDataContext ( templateLiteralExpression . Substring ( 2 , templateLiteralExpression . Length - 3 ) ) ;
259287 }
260288 catch ( ArgumentNullException )
@@ -268,7 +296,7 @@ public override AdaptiveCardsTemplateResult VisitTemplateData([NotNull] Adaptive
268296 }
269297 }
270298 else
271- // else clause handles all of the ordinary json values
299+ // else clause handles all of the ordinary json values
272300 {
273301 string childJson = templateDataValueNode . GetText ( ) ;
274302 try
@@ -535,7 +563,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
535563
536564 templateVisitorWarnings . Add ( $ "WARN: Could not evaluate { returnedResult } because it is not an expression or the " +
537565 $ "expression is invalid. The $when condition has been set to false by default.") ;
538-
566+
539567 }
540568 else
541569 {
@@ -579,7 +607,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
579607 PopDataContext ( ) ;
580608 }
581609
582- // all existing json obj in input and repeated json obj if any have been removed
610+ // all existing json obj in input and repeated json obj if any have been removed
583611 if ( removedCounts == repeatsCounts )
584612 {
585613 combinedResult . HasItBeenDropped = true ;
@@ -589,7 +617,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
589617 }
590618
591619 /// <summary>
592- /// Visitor method for <c>ITernminalNode</c>
620+ /// Visitor method for <c>ITernminalNode</c>
593621 /// <para>collects token as string and expand template if needed</para>
594622 /// </summary>
595623 /// <param name="node"></param>
@@ -651,7 +679,7 @@ public static string Expand(string unboundString, IMemory data, bool isTemplated
651679 {
652680 exp = Expression . Parse ( unboundString . Substring ( 2 , unboundString . Length - 3 ) ) ;
653681 }
654- // AEL can throw any errors, for example, System.Data.Syntax error will be thrown from AEL's ANTLR
682+ // AEL can throw any errors, for example, System.Data.Syntax error will be thrown from AEL's ANTLR
655683 // when AEL encounters unknown functions.
656684 // We can't possibly know all errors and we simply want to leave the expression as it is when there are any exceptions
657685#pragma warning disable CA1031 // Do not catch general exception types
@@ -701,9 +729,9 @@ public override AdaptiveCardsTemplateResult VisitTemplateWhen([NotNull] Adaptive
701729 {
702730 throw new ArgumentNullException ( nameof ( context ) ) ;
703731 }
704- // when this node is visited, the children of this node is shown as below:
732+ // when this node is visited, the children of this node is shown as below:
705733 // this node is visited only when parsing was correctly done
706- // [ '{', '$when', ':', ',', 'expression']
734+ // [ '{', '$when', ':', ',', 'expression']
707735 var result = Visit ( context . templateExpression ( ) ) ;
708736
709737 if ( ! result . IsWhen )
0 commit comments