Validator.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. namespace FastReport.Utils
  8. {
  9. public struct ValidationError
  10. {
  11. public enum ErrorLevel
  12. {
  13. Warning,
  14. Error
  15. }
  16. public string Name;
  17. public ErrorLevel Level;
  18. public string Message;
  19. public ReportComponentBase Object;
  20. public ValidationError(string name, ErrorLevel level, string message, ReportComponentBase obj)
  21. {
  22. this.Name = name;
  23. this.Level = level;
  24. this.Message = message;
  25. this.Object = obj;
  26. }
  27. }
  28. /// <summary>
  29. /// Contains methods used for validation of report.
  30. /// </summary>
  31. public static class Validator
  32. {
  33. /// <summary>
  34. /// Check all objects on band, do they intersect or not.
  35. /// </summary>
  36. /// <param name="band">Band that should be checked.</param>
  37. /// <param name="token">Token for cancelling method if it execute in thread.</param>
  38. /// <returns>Returns <b>true</b> if band has intersecting objects. Otherwise <b>false</b>.</returns>
  39. static public bool ValidateIntersectionAllObjects(BandBase band, CancellationToken token = default)
  40. {
  41. bool result = false;
  42. try
  43. {
  44. foreach (ReportComponentBase component in band.Objects)
  45. {
  46. if (token.IsCancellationRequested)
  47. return false;
  48. component.IsIntersectingWithOtherObject = false;
  49. if (!band.Bounds.Contains(GetReducedRect(component.AbsBounds)))
  50. {
  51. component.IsIntersectingWithOtherObject = true;
  52. result = true;
  53. }
  54. }
  55. for (int i = 0; i < band.Objects.Count; i++)
  56. {
  57. for (int j = i + 1; j < band.Objects.Count; j++)
  58. {
  59. if (token.IsCancellationRequested)
  60. return false;
  61. if (band.Objects[i].Bounds.IntersectsWith(GetReducedRect(band.Objects[j].Bounds)))
  62. {
  63. result = true;
  64. band.Objects[i].IsIntersectingWithOtherObject = true;
  65. band.Objects[j].IsIntersectingWithOtherObject = true;
  66. }
  67. }
  68. }
  69. }
  70. catch (Exception e)
  71. {
  72. if (token.IsCancellationRequested)
  73. return false;
  74. else
  75. throw e;
  76. }
  77. return result;
  78. }
  79. /// <summary>
  80. /// Starting new task with validating band. For stoping task use <b>ValidateBandToken</b>.
  81. /// </summary>
  82. /// <param name="band"></param>
  83. /// <param name="token"></param>
  84. /// <returns></returns>
  85. static public Task<bool> ValidateIntersectionAllObjectsAsync(BandBase band, CancellationToken token)
  86. {
  87. return Task.Factory.StartNew(() =>
  88. {
  89. return ValidateIntersectionAllObjects(band, token);
  90. }, token);
  91. }
  92. /// <summary>
  93. /// Check child rectangle on contains in parent rectangle.
  94. /// </summary>
  95. /// <param name="parent"></param>
  96. /// <param name="child"></param>
  97. /// <returns></returns>
  98. static public bool RectContainInOtherRect(RectangleF parent, RectangleF child)
  99. {
  100. return parent.Contains(GetReducedRect(child));
  101. }
  102. private static RectangleF GetReducedRect(RectangleF rect)
  103. {
  104. return new RectangleF(rect.X + 0.01f, rect.Y + 0.01f, rect.Width - 0.02f, rect.Height - 0.02f);
  105. }
  106. /// <summary>
  107. /// Validate report.
  108. /// </summary>
  109. /// <param name="report"></param>
  110. /// <param name="checkIntersectObj">Need set false if enabled backlight intersecting objects and report is designing.</param>
  111. /// <param name="token">Token for cancelling method if it execute in thread.</param>
  112. /// <returns>List of errors.</returns>
  113. static public List<ValidationError> ValidateReport(Report report, bool checkIntersectObj = true, CancellationToken token = default)
  114. {
  115. if (report == null)
  116. return null;
  117. List<ValidationError> listError = new List<ValidationError>();
  118. try
  119. {
  120. foreach (PageBase page in report.Pages)
  121. {
  122. foreach (Base c in page.AllObjects)
  123. {
  124. if (token.IsCancellationRequested)
  125. return null;
  126. if (c is BandBase && checkIntersectObj)
  127. ValidateIntersectionAllObjects(c as BandBase, token);
  128. if (c is ReportComponentBase)
  129. listError.AddRange((c as ReportComponentBase).Validate());
  130. }
  131. }
  132. bool duplicateName;
  133. for (int i = 0; i < report.AllObjects.Count - 1; i++)
  134. {
  135. duplicateName = false;
  136. for (int j = i + 1; j < report.AllObjects.Count; j++)
  137. {
  138. if (token.IsCancellationRequested)
  139. return null;
  140. if (report.AllObjects[j] is ReportComponentBase && report.AllObjects[i].Name == report.AllObjects[j].Name)
  141. {
  142. listError.Add(new ValidationError(report.AllObjects[j].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)report.AllObjects[j]));
  143. duplicateName = true;
  144. }
  145. }
  146. if (report.AllObjects[i] is ReportComponentBase && duplicateName)
  147. listError.Add(new ValidationError(report.AllObjects[i].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)report.AllObjects[i]));
  148. }
  149. }
  150. catch (Exception e)
  151. {
  152. if (token.IsCancellationRequested)
  153. return null;
  154. else
  155. throw e;
  156. }
  157. return listError.Distinct().ToList();
  158. }
  159. }
  160. }