using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace FastReport.Utils
{
public struct ValidationError
{
public enum ErrorLevel
{
Warning,
Error
}
public string Name;
public ErrorLevel Level;
public string Message;
public ReportComponentBase Object;
public ValidationError(string name, ErrorLevel level, string message, ReportComponentBase obj)
{
this.Name = name;
this.Level = level;
this.Message = message;
this.Object = obj;
}
}
///
/// Contains methods used for validation of report.
///
public static class Validator
{
///
/// Check all objects on band, do they intersect or not.
///
/// Band that should be checked.
/// Token for cancelling method if it execute in thread.
/// Returns true if band has intersecting objects. Otherwise false.
static public bool ValidateIntersectionAllObjects(BandBase band, CancellationToken token = default)
{
bool result = false;
try
{
foreach (ReportComponentBase component in band.Objects)
{
if (token.IsCancellationRequested)
return false;
component.IsIntersectingWithOtherObject = false;
if (!band.Bounds.Contains(GetReducedRect(component.AbsBounds)))
{
component.IsIntersectingWithOtherObject = true;
result = true;
}
}
for (int i = 0; i < band.Objects.Count; i++)
{
for (int j = i + 1; j < band.Objects.Count; j++)
{
if (token.IsCancellationRequested)
return false;
if (band.Objects[i].Bounds.IntersectsWith(GetReducedRect(band.Objects[j].Bounds)))
{
result = true;
band.Objects[i].IsIntersectingWithOtherObject = true;
band.Objects[j].IsIntersectingWithOtherObject = true;
}
}
}
}
catch (Exception e)
{
if (token.IsCancellationRequested)
return false;
else
throw e;
}
return result;
}
///
/// Starting new task with validating band. For stoping task use ValidateBandToken.
///
///
///
///
static public Task ValidateIntersectionAllObjectsAsync(BandBase band, CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
return ValidateIntersectionAllObjects(band, token);
}, token);
}
///
/// Check child rectangle on contains in parent rectangle.
///
///
///
///
static public bool RectContainInOtherRect(RectangleF parent, RectangleF child)
{
return parent.Contains(GetReducedRect(child));
}
private static RectangleF GetReducedRect(RectangleF rect)
{
return new RectangleF(rect.X + 0.01f, rect.Y + 0.01f, rect.Width - 0.02f, rect.Height - 0.02f);
}
///
/// Validate report.
///
///
/// Need set false if enabled backlight intersecting objects and report is designing.
/// Token for cancelling method if it execute in thread.
/// List of errors.
static public List ValidateReport(Report report, bool checkIntersectObj = true, CancellationToken token = default)
{
if (report == null)
return null;
List listError = new List();
try
{
foreach (PageBase page in report.Pages)
{
foreach (Base c in page.AllObjects)
{
if (token.IsCancellationRequested)
return null;
if (c is BandBase && checkIntersectObj)
ValidateIntersectionAllObjects(c as BandBase, token);
if (c is ReportComponentBase)
listError.AddRange((c as ReportComponentBase).Validate());
}
}
bool duplicateName;
for (int i = 0; i < report.AllObjects.Count - 1; i++)
{
duplicateName = false;
for (int j = i + 1; j < report.AllObjects.Count; j++)
{
if (token.IsCancellationRequested)
return null;
if (report.AllObjects[j] is ReportComponentBase && report.AllObjects[i].Name == report.AllObjects[j].Name)
{
listError.Add(new ValidationError(report.AllObjects[j].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)report.AllObjects[j]));
duplicateName = true;
}
}
if (report.AllObjects[i] is ReportComponentBase && duplicateName)
listError.Add(new ValidationError(report.AllObjects[i].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)report.AllObjects[i]));
}
}
catch (Exception e)
{
if (token.IsCancellationRequested)
return null;
else
throw e;
}
return listError.Distinct().ToList();
}
}
}