public static IEnumerable <Field> LoadResourceReference(MessageDescriptor msgDesc, FieldDescriptor fieldDesc, IReadOnlyDictionary <string, Definition> resourcesByUrt, IReadOnlyDictionary <string, IReadOnlyList <Definition> > resourcesByParentComparison) { // Is this field the name-field of a resource descriptor? var resourceDesc = msgDesc.SafeGetOption(ResourceExtensions.Resource); if (resourceDesc is object) { var def = resourcesByUrt[resourceDesc.Type]; if (fieldDesc.Name == def.NameField) { if (def.HasNotWildcard) { yield return(new Field(fieldDesc, def)); } if (def.HasWildcard) { yield return(new Field(fieldDesc, Definition.WildcardResource, def.HasNotWildcard ? new[] { def } : null)); } yield break; } } // Is this field a resource reference? var resourceRef = fieldDesc.SafeGetOption(ResourceExtensions.ResourceReference); if (resourceRef is object) { if (!string.IsNullOrEmpty(resourceRef.Type)) { if (resourceRef.Type == "*" || resourceRef.Type == "**") { yield return(new Field(fieldDesc, Definition.WildcardResource)); yield break; } if (!resourcesByUrt.TryGetValue(resourceRef.Type, out var def)) { throw new InvalidOperationException($"No resource type with name: '{resourceRef.Type}' for field {msgDesc.Name}.{fieldDesc.Name}"); } if (def.HasNotWildcard) { yield return(new Field(fieldDesc, def)); } if (def.HasWildcard) { yield return(new Field(fieldDesc, Definition.WildcardResource, def.HasNotWildcard ? new[] { def } : null)); } } else if (!string.IsNullOrEmpty(resourceRef.ChildType)) { if (resourceRef.ChildType == "*" || resourceRef.ChildType == "**") { yield return(new Field(fieldDesc, Definition.WildcardResource)); yield break; } if (!resourcesByUrt.TryGetValue(resourceRef.ChildType, out var childDef)) { throw new InvalidOperationException($"No resource type with child name: '{resourceRef.ChildType}' for field {msgDesc.Name}.{fieldDesc.Name}"); } // Find all resources in which the patterns are a subset of the child patterns; a wildcard matches a child wildcard. // Verify that these resources together match all parent patterns of the child resource. // Order by most-specific-parent first, then by order in child-type patterns (then alphabetically as a last resort, to keep it deterministic). var parentPatterns = childDef.Patterns.Select(x => x.IsWildcard ? "*" : x.Template.Parent().ParentComparisonString).ToImmutableList(); var parentPatternsSet = parentPatterns.ToImmutableHashSet(); var parentDefs = parentPatterns .SelectMany(x => resourcesByParentComparison.TryGetValue(x, out var defs) ? defs : Enumerable.Empty <Definition>()) .Where(def => def.Patterns.All(x => parentPatternsSet.Contains(x.Template.ParentComparisonString))) .Distinct(Definition.TypeComparer.Instance) .OrderBy(def => def.Patterns.Count) .ThenBy(def => def.Patterns.Select(x => parentPatterns.IndexOf(x.Template.ParentComparisonString)).Where(x => x >= 0).Min()) .ThenBy(def => def.UnifiedResourceTypeName) .ToImmutableList(); // "*" pattern is not required to be matched; all others are required. if (parentPatternsSet.Except(parentDefs.SelectMany(x => x.Patterns).Select(x => x.Template.ParentComparisonString).Append("*")).Any()) { throw new InvalidOperationException($"Not all patterns can be matched to a parent resource for field {msgDesc.Name}.{fieldDesc.Name}"); } foreach (var parentDef in parentDefs) { yield return(new Field(fieldDesc, parentDef)); } if (parentDefs.Any(x => x.HasWildcard) || childDef.IsWildcardOnly || parentDefs.Count > 1) { yield return(new Field(fieldDesc, Definition.WildcardResource, parentDefs.Count > 1 ? parentDefs : null, parentPatternsSet.Contains("*"))); } } else { throw new InvalidOperationException("type or child_type must be set."); } } }