JoinItems.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using Microsoft.Build.Framework;
  8. using Microsoft.Build.Utilities;
  9. namespace RepoTasks
  10. {
  11. public class JoinItems : Task
  12. {
  13. [Required]
  14. public ITaskItem[] Left { get; set; }
  15. [Required]
  16. public ITaskItem[] Right { get; set; }
  17. // The metadata to use as the new item spec. If not specified, LeftKey is used.
  18. public string LeftItemSpec { get; set; }
  19. // LeftKey and RightKey: The metadata to join on. If not set, then use the ItemSpec
  20. public string LeftKey { get; set; }
  21. public string RightKey { get; set; }
  22. // LeftMetadata and RightMetadata: The metadata names to include in the result. Specify "*" to include all metadata
  23. public string[] LeftMetadata { get; set; }
  24. public string[] RightMetadata { get; set; }
  25. [Output]
  26. public ITaskItem[] JoinResult { get; private set; }
  27. public override bool Execute()
  28. {
  29. bool useAllLeftMetadata = LeftMetadata != null && LeftMetadata.Length == 1 && LeftMetadata[0] == "*";
  30. bool useAllRightMetadata = RightMetadata != null && RightMetadata.Length == 1 && RightMetadata[0] == "*";
  31. var newItemSpec = string.IsNullOrEmpty(LeftItemSpec)
  32. ? LeftKey
  33. : LeftItemSpec;
  34. JoinResult = Left.Join(Right,
  35. item => GetKeyValue(LeftKey, item),
  36. item => GetKeyValue(RightKey, item),
  37. (left, right) =>
  38. {
  39. // If including all metadata from left items and none from right items, just return left items directly
  40. if (useAllLeftMetadata &&
  41. string.IsNullOrEmpty(LeftKey) &&
  42. string.IsNullOrEmpty(LeftItemSpec) &&
  43. (RightMetadata == null || RightMetadata.Length == 0))
  44. {
  45. return left;
  46. }
  47. // If including all metadata from right items and none from left items, just return the right items directly
  48. if (useAllRightMetadata &&
  49. string.IsNullOrEmpty(RightKey) &&
  50. string.IsNullOrEmpty(LeftItemSpec) &&
  51. (LeftMetadata == null || LeftMetadata.Length == 0))
  52. {
  53. return right;
  54. }
  55. var ret = new TaskItem(GetKeyValue(newItemSpec, left));
  56. // Weird ordering here is to prefer left metadata in all cases, as CopyToMetadata doesn't overwrite any existing metadata
  57. if (useAllLeftMetadata)
  58. {
  59. // CopyMetadata adds an OriginalItemSpec, which we don't want. So we subsequently remove it
  60. left.CopyMetadataTo(ret);
  61. ret.RemoveMetadata("OriginalItemSpec");
  62. }
  63. if (!useAllRightMetadata && RightMetadata != null)
  64. {
  65. foreach (string name in RightMetadata)
  66. {
  67. ret.SetMetadata(name, right.GetMetadata(name));
  68. }
  69. }
  70. if (!useAllLeftMetadata && LeftMetadata != null)
  71. {
  72. foreach (string name in LeftMetadata)
  73. {
  74. ret.SetMetadata(name, left.GetMetadata(name));
  75. }
  76. }
  77. if (useAllRightMetadata)
  78. {
  79. // CopyMetadata adds an OriginalItemSpec, which we don't want. So we subsequently remove it
  80. right.CopyMetadataTo(ret);
  81. ret.RemoveMetadata("OriginalItemSpec");
  82. }
  83. return (ITaskItem)ret;
  84. },
  85. StringComparer.OrdinalIgnoreCase).ToArray();
  86. return true;
  87. }
  88. static string GetKeyValue(string key, ITaskItem item)
  89. {
  90. if (string.IsNullOrEmpty(key))
  91. {
  92. return item.ItemSpec;
  93. }
  94. else
  95. {
  96. return item.GetMetadata(key);
  97. }
  98. }
  99. }
  100. }