Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ArchUnitNET/Fluent/ArchRuleCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void AddPredicateConjunction(LogicalConjunction logicalConjunction)
_predicateManager.SetNextLogicalConjunction(logicalConjunction);
}

public void AddCondition(ICondition<TRuleType> condition)
public void AddCondition(IOrderedCondition<TRuleType> condition)
{
_conditionManager.AddCondition(condition);
}
Expand Down
2 changes: 1 addition & 1 deletion ArchUnitNET/Fluent/CombinedArchRuleCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void AddPredicateConjunction(LogicalConjunction logicalConjunction)
_currentArchRuleCreator.AddPredicateConjunction(logicalConjunction);
}

public void AddCondition(ICondition<TRuleType> condition)
public void AddCondition(IOrderedCondition<TRuleType> condition)
{
_currentArchRuleCreator.AddCondition(condition);
}
Expand Down
68 changes: 12 additions & 56 deletions ArchUnitNET/Fluent/ConditionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void ContinueComplexCondition<TRelatedType>(IPredicate<TRelatedType> filt
}
}

public void AddCondition(ICondition<T> condition)
public void AddCondition(IOrderedCondition<T> condition)
{
_conditionElements.Last().SetCondition(condition);
}
Expand Down Expand Up @@ -119,58 +119,19 @@ ICanBeEvaluated archRuleCreator
};
}

if (_conditionElements.All(e => e.IsOrdered()))
{
var conditionResults = _conditionElements
.Select(conditionElement =>
conditionElement.Check(filteredObjectsList, architecture).ToList()
)
.ToList();
return filteredObjectsList.Select(
(t, i) =>
CreateEvaluationResult(
conditionResults.Select(results => results[i]),
architecture,
archRuleCreator
)
);
}

//rough heuristic - if we have small number of comparisons, we are fine with sequential search
//but in large cases its quadratic behavior becomes too slow and building of a dictionary is justified
if (filteredObjectsList.Count * _conditionElements.Count > 256)
{
var conditionResults = _conditionElements
.Select(conditionElement =>
conditionElement
.Check(filteredObjectsList, architecture)
.ToDictionary(x => x.ConditionResult.AnalyzedObject)
)
.ToList();

return filteredObjectsList.Select(t =>
CreateEvaluationResult(
FindResultsForObject(conditionResults, t),
architecture,
archRuleCreator
)
);
}
else
{
var conditionResults = _conditionElements
.Select(conditionElement =>
conditionElement.Check(filteredObjectsList, architecture).ToList()
)
.ToList();
return filteredObjectsList.Select(t =>
var conditionResults = _conditionElements
.Select(conditionElement =>
conditionElement.Check(filteredObjectsList, architecture).ToList()
)
.ToList();
return filteredObjectsList.Select(
(t, i) =>
CreateEvaluationResult(
FindResultsForObject(conditionResults, t),
conditionResults.Select(results => results[i]),
architecture,
archRuleCreator
)
);
}
);
}

private IEnumerable<ConditionElementResult> FindResultsForObject(
Expand Down Expand Up @@ -291,7 +252,7 @@ private class ConditionElement<T> : IHasDescription
where T : ICanBeAnalyzed
{
private readonly LogicalConjunction _logicalConjunction;
private ICondition<T> _condition;
private IOrderedCondition<T> _condition;

[CanBeNull]
private string _customDescription;
Expand Down Expand Up @@ -338,7 +299,7 @@ public void AddReason(string reason)
_reason = "because " + reason;
}

public void SetCondition(ICondition<T> condition)
public void SetCondition(IOrderedCondition<T> condition)
{
_condition = condition;
}
Expand All @@ -365,11 +326,6 @@ Architecture architecture
.Select(result => new ConditionElementResult(result, _logicalConjunction));
}

public bool IsOrdered()
{
return _condition is IOrderedCondition<T>;
}

public bool CheckEmpty(bool currentResult)
{
if (_condition == null)
Expand Down
2 changes: 1 addition & 1 deletion ArchUnitNET/Fluent/Conditions/ComplexCondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace ArchUnitNET.Fluent.Conditions
{
public class ComplexCondition<TRuleType, TRelatedType> : ICondition<TRuleType>
public class ComplexCondition<TRuleType, TRelatedType> : IOrderedCondition<TRuleType>
where TRuleType : ICanBeAnalyzed
where TRelatedType : ICanBeAnalyzed
{
Expand Down
72 changes: 72 additions & 0 deletions ArchUnitNET/Fluent/Conditions/ConditionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ArchUnitNET.Domain;

namespace ArchUnitNET.Fluent.Conditions
{
public static class ConditionExtensions
{
private sealed class OrderedConditionWrapper<TRuleType> : IOrderedCondition<TRuleType>
where TRuleType : ICanBeAnalyzed
{
private readonly ICondition<TRuleType> _condition;

public OrderedConditionWrapper(ICondition<TRuleType> condition)
{
_condition = condition;
}

public string Description => _condition.Description;

public IEnumerable<ConditionResult> Check(
IEnumerable<TRuleType> objects,
Architecture architecture
)
{
var objectCollection = objects as ICollection<TRuleType> ?? objects.ToList();
var results = Check(architecture, objectCollection, _condition);
return objectCollection.Select(ruleType => results(ruleType));
}

public bool CheckEmpty() => _condition.CheckEmpty();

private static Func<TRuleType, ConditionResult> Check(
Architecture architecture,
ICollection<TRuleType> objects,
ICondition<TRuleType> condition
)
{
var conditionResults = condition.Check(objects, architecture);
if (objects.Count > 256)
{
var resultDictionary = conditionResults.ToDictionary(result =>
result.AnalyzedObject
);
return obj => resultDictionary[obj];
}
var resultList = conditionResults.ToList();
return obj => resultList.First(result => result.AnalyzedObject.Equals(obj));
}
}

/// <summary>
/// Wraps an ICondition as an IOrderedCondition if it is not already one, ensuring that the order of results
/// corresponds to the order of input objects.
/// </summary>
/// <param name="condition">Condition to be wrapped.</param>
/// <typeparam name="TRuleType">Type of objects the condition applies to.</typeparam>
/// <returns>An IOrderedCondition that maintains the order of input objects.</returns>
public static IOrderedCondition<TRuleType> AsOrderedCondition<TRuleType>(
this ICondition<TRuleType> condition
)
where TRuleType : ICanBeAnalyzed
{
if (condition is IOrderedCondition<TRuleType> orderedCondition)
{
return orderedCondition;
}
return new OrderedConditionWrapper<TRuleType>(condition);
}
}
}
11 changes: 11 additions & 0 deletions ArchUnitNET/Fluent/Conditions/ICondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@

namespace ArchUnitNET.Fluent.Conditions
{
/// <summary>
/// A condition that can be checked against objects of type TRuleType.
/// </summary>
/// <typeparam name="TRuleType">Type of objects the condition can be checked against.</typeparam>
public interface ICondition<in TRuleType> : IHasDescription
where TRuleType : ICanBeAnalyzed
{
/// <summary>
/// Checks the condition against the provided objects within the given architecture.
/// </summary>
/// <param name="objects">Objects to check the condition against.</param>
/// <param name="architecture">The architecture context for the check.</param>
/// <returns>A collection of ConditionResults indicating the outcome for each object.</returns>
IEnumerable<ConditionResult> Check(
IEnumerable<TRuleType> objects,
Architecture architecture
);

bool CheckEmpty();
}
}
5 changes: 5 additions & 0 deletions ArchUnitNET/Fluent/Conditions/IOrderedCondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

namespace ArchUnitNET.Fluent.Conditions
{
/// <summary>
/// An IOrderedCondition is a Condition that has the semantic requirement that the order of the results
/// corresponds to the order of the input objects.
/// </summary>
/// <typeparam name="TRuleType">Type of objects the condition can be checked against.</typeparam>
public interface IOrderedCondition<in TRuleType> : ICondition<TRuleType>
where TRuleType : ICanBeAnalyzed { }
}
9 changes: 6 additions & 3 deletions ArchUnitNET/Fluent/Conditions/RelationCondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ public class RelationCondition<TRuleType, TRelatedType> : IHasDescription
where TRuleType : ICanBeAnalyzed
where TRelatedType : ICanBeAnalyzed
{
private readonly Func<IObjectProvider<TRelatedType>, ICondition<TRuleType>> _relation;
private readonly Func<
IObjectProvider<TRelatedType>,
IOrderedCondition<TRuleType>
> _relation;

public RelationCondition(
Func<IObjectProvider<TRelatedType>, ICondition<TRuleType>> relation,
Func<IObjectProvider<TRelatedType>, IOrderedCondition<TRuleType>> relation,
string description,
string failDescription
)
Expand All @@ -26,7 +29,7 @@ string failDescription

public string Description { get; }

public ICondition<TRuleType> GetCondition(IEnumerable<TRelatedType> objects)
public IOrderedCondition<TRuleType> GetCondition(IEnumerable<TRelatedType> objects)
{
return _relation(new ObjectProvider<TRelatedType>(objects.ToList()));
}
Expand Down
2 changes: 1 addition & 1 deletion ArchUnitNET/Fluent/IArchRuleCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IArchRuleCreator<TRuleType> : ICanBeEvaluated
{
void AddPredicate(IPredicate<TRuleType> predicate);
void AddPredicateConjunction(LogicalConjunction logicalConjunction);
void AddCondition(ICondition<TRuleType> condition);
void AddCondition(IOrderedCondition<TRuleType> condition);
void AddConditionConjunction(LogicalConjunction logicalConjunction);
void AddConditionReason(string reason);
void AddPredicateReason(string reason);
Expand Down
Loading