public string?TryLookupResourceName(string?typeString, LanguageExpression nameExpression) { if (typeString is null) { var nameString = ExpressionsEngine.SerializeExpression(nameExpression); var resourceKeySuffix = EscapeIdentifier($"_{nameString}"); var matchingResources = assignedResourceNames.Where(kvp => kvp.Key.EndsWith(resourceKeySuffix, StringComparison.OrdinalIgnoreCase)); if (matchingResources.Count() == 1) { // only return a value if we're sure about the match return(matchingResources.First().Value); } return(null); } // it's valid to include a trailing slash, so we need to normalize it typeString = typeString.TrimEnd('/'); var assignedResourceKey = GetResourceNameKey(typeString, nameExpression); if (!assignedResourceNames.TryGetValue(assignedResourceKey, out var name)) { return(null); } return(name); }
public static LanguageExpression ParseExpression(string value) { if (ExpressionsEngine.IsLanguageExpression(value)) { return(ExpressionsEngine.ParseLanguageExpression(value)); } return(new JTokenExpression(value)); }
public static IEnumerable <JObject> FlattenAndNormalizeResource(JToken resourceJtoken) { var resource = resourceJtoken as JObject ?? throw new ConversionFailedException($"Unable to read resource", resourceJtoken); var(parentType, parentName, _) = ParseResource(resource); var childResources = GetProperty(resource, "resources"); if (childResources != null) { childResources.Remove(); } yield return(resource); var childResourcesArray = childResources?.Value as JArray; if (childResourcesArray is null) { yield break; } foreach (var childResource in childResourcesArray) { var childResourceObject = childResource as JObject ?? throw new ConversionFailedException($"Unable to read child resource", childResource); var(childType, childName, _) = ParseResource(childResourceObject); if (GetProperty(resource, "copy") is { } copyProperty) { childResourceObject["copy"] = copyProperty.Value; } if (GetProperty(resource, "condition") is { } conditionProperty) { childResourceObject["condition"] = conditionProperty.Value; } // child may sometimes be specified using the fully-qualified type and name if (!StringComparer.OrdinalIgnoreCase.Equals(parentType.Split("/")[0], childType.Split("/")[0])) { childResourceObject["type"] = $"{parentType}/{childType}"; childResourceObject["name"] = ExpressionsEngine.SerializeExpression(ExpressionHelpers.Concat(ExpressionHelpers.ParseExpression(parentName), new JTokenExpression("/"), ExpressionHelpers.ParseExpression(childName))); } foreach (var result in FlattenAndNormalizeResource(childResourceObject)) { // recurse yield return(result); } } }
public static void VisitExpressions <TToken>(TToken input, Action <LanguageExpression> visitFunc) where TToken : JToken { var visitor = new LanguageExpressionVisitor { OnFunctionExpression = visitFunc, OnJTokenExpression = visitFunc, }; void VisitLanguageExpressions(string value) { if (ExpressionsEngine.IsLanguageExpression(value)) { var expression = ExpressionsEngine.ParseLanguageExpression(value); expression.Accept(visitor); } } if (input is JValue jValue && jValue.ToObject <string>() is { } value) { VisitLanguageExpressions(value); return; } JsonUtility.WalkJsonRecursive( input, objectAction: @object => { foreach (var property in @object.Properties()) { VisitLanguageExpressions(property.Name); } }, tokenAction: token => { if (token.Type == JTokenType.String && token.ToObject <string>() is { } value) { VisitLanguageExpressions(value); } });
private string GetResourceNameKey(string typeString, LanguageExpression nameExpression) { var nameString = ExpressionsEngine.SerializeExpression(nameExpression); return(EscapeIdentifier($"{typeString}_{nameString}")); }
public static LanguageExpression FlattenStringOperations(LanguageExpression original) { if (original is not FunctionExpression functionExpression) { return(original); } // convert a 'format' to a 'concat' function if (functionExpression.NameEquals("format")) { var formatString = (functionExpression.Parameters[0] as JTokenExpression)?.Value.Value <string>() ?? throw new ArgumentException($"Unable to read format statement {ExpressionsEngine.SerializeExpression(functionExpression)} as string"); var formatHoleMatches = Regex.Matches(formatString, "{([0-9]+)}"); var concatExpressions = new List <LanguageExpression>(); var nextStart = 0; for (var i = 0; i < formatHoleMatches.Count; i++) { var match = formatHoleMatches[i]; var position = match.Groups[1].Index; var length = match.Groups[1].Length; var intValue = int.Parse(match.Groups[1].Value); // compensate for the { if (nextStart < position - 1) { var betweenPortion = formatString.Substring(nextStart, position - 1 - nextStart); concatExpressions.Add(new JTokenExpression(betweenPortion)); } // replace it with the appropriately-numbered expression concatExpressions.Add(functionExpression.Parameters[intValue + 1]); // compensate for the } nextStart = position + length + 1; } if (nextStart < formatString.Length) { var betweenPortion = formatString.Substring(nextStart, formatString.Length - nextStart); concatExpressions.Add(new JTokenExpression(betweenPortion)); } // overwrite the original expression functionExpression = Concat(concatExpressions.ToArray()); } // flatten nested 'concat' functions if (functionExpression.NameEquals("concat")) { var concatExpressions = new List <LanguageExpression>(); foreach (var parameter in functionExpression.Parameters) { // recurse var flattenedParameter = FlattenStringOperations(parameter); if (flattenedParameter is FunctionExpression childFunction && childFunction.NameEquals("concat")) { // concat directly inside a concat - break it out concatExpressions.AddRange(childFunction.Parameters); continue; } concatExpressions.Add(flattenedParameter); } // overwrite the original expression functionExpression = Concat(CombineConcatArguments(concatExpressions).ToArray()); } // just return the inner portion if there's only one concat entry if (functionExpression.NameEquals("concat") && functionExpression.Parameters.Length == 1) { return(functionExpression.Parameters[0]); } return(functionExpression); }