2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 // MarkupExpressionParser.cs
21 // This code is partly salvaged from moonlight. Following licence apply.
25 // Moonlight List (moonlight-list@lists.ximian.com)
26 // Stephane Delcroix (stephane@mi8.be)
28 // Copyright 2009 Novell, Inc.
29 // Copyright 2013 Xamarin, Inc.
31 // Permission is hereby granted, free of charge, to any person obtaining
32 // a copy of this software and associated documentation files (the
33 // "Software"), to deal in the Software without restriction, including
34 // without limitation the rights to use, copy, modify, merge, publish,
35 // distribute, sublicense, and/or sell copies of the Software, and to
36 // permit persons to whom the Software is furnished to do so, subject to
37 // the following conditions:
39 // The above copyright notice and this permission notice shall be
40 // included in all copies or substantial portions of the Software.
42 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
46 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
47 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
48 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
54 namespace Tizen.NUI.Xaml
56 internal abstract class MarkupExpressionParser
58 public object ParseExpression(ref string expression, IServiceProvider serviceProvider)
60 if (serviceProvider == null)
61 throw new ArgumentNullException(nameof(serviceProvider));
62 if (expression.StartsWith("{}", StringComparison.Ordinal))
63 return expression.Substring(2);
65 if (expression[expression.Length - 1] != '}')
66 throw new Exception("Expression must end with '}'");
70 if (!MatchMarkup(out match, expression, out len))
72 expression = expression.Substring(len).TrimStart();
73 if (expression.Length == 0)
74 throw new Exception("Expression did not end in '}'");
76 var parser = Activator.CreateInstance(GetType()) as IExpressionParser;
77 return parser?.Parse(match, ref expression, serviceProvider);
80 internal static bool MatchMarkup(out string match, string expression, out int end)
82 if (expression.Length < 2)
89 if (expression[0] != '{')
98 for (i = 1; i < expression.Length; i++)
100 if (expression[i] == ' ')
114 for (c = 0; c + i < expression.Length; c++)
116 if (expression[i + c] == ' ' || expression[i + c] == '}')
120 if (i + c == expression.Length)
128 match = expression.Substring(i, c);
132 protected void HandleProperty(string prop, IServiceProvider serviceProvider, ref string remaining, bool isImplicit)
140 SetPropertyValue(null, prop, null, serviceProvider);
143 remaining = remaining.TrimStart();
144 if (remaining.StartsWith("{", StringComparison.Ordinal))
146 value = ParseExpression(ref remaining, serviceProvider);
147 remaining = remaining.TrimStart();
149 if (remaining.Length > 0 && remaining[0] == ',')
150 remaining = remaining.Substring(1);
151 else if (remaining.Length > 0 && remaining[0] == '}')
152 remaining = remaining.Substring(1);
154 str_value = value as string;
157 str_value = GetNextPiece(ref remaining, out next);
159 SetPropertyValue(prop, str_value, value, serviceProvider);
162 protected abstract void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider);
164 protected string GetNextPiece(ref string remaining, out char next)
166 bool inString = false;
168 char stringTerminator = '\0';
169 remaining = remaining.TrimStart();
170 if (remaining.Length == 0)
172 next = Char.MaxValue;
176 var piece = new StringBuilder();
177 // If we're inside a quoted string we append all chars to our piece until we hit the ending quote.
178 while (end < remaining.Length &&
179 (inString || (remaining[end] != '}' && remaining[end] != ',' && remaining[end] != '=')))
183 if (remaining[end] == stringTerminator)
192 if (remaining[end] == '\'' || remaining[end] == '"')
195 stringTerminator = remaining[end];
201 // If this is an escape char, consume it and append the next char to our piece.
202 if (remaining[end] == '\\')
205 if (end == remaining.Length)
208 piece.Append(remaining[end]);
212 if (inString && end == remaining.Length)
213 throw new Exception("Unterminated quoted string");
215 if (end == remaining.Length && !remaining.EndsWith("}", StringComparison.Ordinal))
216 throw new Exception("Expression did not end with '}'");
220 next = Char.MaxValue;
224 next = remaining[end];
225 remaining = remaining.Substring(end + 1);
227 // Whitespace is trimmed from the end of the piece before stripping
228 // quote chars from the start/end of the string.
229 while (piece.Length > 0 && char.IsWhiteSpace(piece[piece.Length - 1]))
232 if (piece.Length >= 2)
234 char first = piece[0];
235 char last = piece[piece.Length - 1];
236 if ((first == '\'' && last == '\'') || (first == '"' && last == '"'))
238 piece.Remove(piece.Length - 1, 1);
243 return piece.ToString();