public static IEnumerable <Field> LoadResourceReference(MessageDescriptor msgDesc, FieldDescriptor fieldDesc, IReadOnlyDictionary <string, Definition> resourcesByUrt, IReadOnlyDictionary <string, IReadOnlyList <Definition> > resourcesByParentComparison, bool required) { // Is this field the name-field of a resource descriptor? var resourceDesc = msgDesc.GetExtension(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.GetExtension(ResourceExtensions.ResourceReference); if (resourceRef is object) { // Resource references must be string fields, or StringValue (well-known type) fields. // It's a relatively common error to put the annotation on a message field instead, which // will otherwise generate invalid code - better to fail generation. if (fieldDesc.FieldType != FieldType.String && !(fieldDesc.FieldType == FieldType.Message && fieldDesc.MessageType.FullName == StringValue.Descriptor.FullName)) { throw new InvalidOperationException($"Field {msgDesc.Name}.{fieldDesc.Name} has a resource reference annotation, but is not a string or StringValue field."); } 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)) { if (required) { throw new InvalidOperationException($"No resource type with name: '{resourceRef.Type}' for field {msgDesc.Name}.{fieldDesc.Name}"); } else { yield break; } } 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)) { if (required) { throw new InvalidOperationException($"No resource type with child name: '{resourceRef.ChildType}' for field {msgDesc.Name}.{fieldDesc.Name}"); } else { yield break; } } // 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."); } } }