Skip to content

Commit

Permalink
Refactored type filtering on individual schemas.
Browse files Browse the repository at this point in the history
Replaced IIfcTypeConstraintProvider.TypeFilters with
GetTypesFilter(SchemaInfo schema), to return only the types identified
in the relevant schema.

Schemas are tested independently for their consistency.
This allows a better feedback on the schema context of a problem.

Also added a few extra valid types for property clauses.
  • Loading branch information
CBenghi committed Oct 1, 2023
1 parent a12074d commit efd01fd
Show file tree
Hide file tree
Showing 19 changed files with 408 additions and 252 deletions.
49 changes: 34 additions & 15 deletions ids-lib/IdsSchema/IdsNodes/Facets/IdsAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using IdsLib.IfcSchema.TypeFilters;
using IdsLib.Messages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

namespace IdsLib.IdsSchema.IdsNodes;
Expand All @@ -15,34 +17,51 @@ public IdsAttribute(System.Xml.XmlReader reader, IdsXmlNode? parent) : base(read

public bool IsValid { get; private set; }

public IIfcTypeConstraint? TypesFilter { get; private set; } = null;
/// <summary>
/// prepared typefilters per schema version
/// </summary>
private readonly Dictionary<SchemaInfo, IIfcTypeConstraint> typeFilters = new();

internal protected override Audit.Status PerformAudit(ILogger? logger)
/// <inheritdoc />
public IIfcTypeConstraint? GetTypesFilter(SchemaInfo schema)
{
if (typeFilters.TryGetValue(schema, out var typeFilter))
return typeFilter;
return null;
}

internal protected override Audit.Status PerformAudit(ILogger? logger)
{
if (!TryGetUpperNode<IdsSpecification>(logger, this, IdsSpecification.SpecificationIdentificationArray, out var spec, out var retStatus))
return retStatus;
var requiredSchemaVersions = spec.SchemaVersions;
var name = GetChildNodes("name").FirstOrDefault(); // name must exist
var sm = name?.GetListMatcher();

// the first child must be a valid string matcher
if (sm is null)
return IdsMessages.Report102NoStringMatcher(logger, this, "name");

var validAttributeNames = SchemaInfo.AllAttributes
.Where(x => (x.ValidSchemaVersions & requiredSchemaVersions) == requiredSchemaVersions)
.Select(y => y.IfcAttributeName);
var ret = sm.DoesMatch(validAttributeNames, false, logger, out var matches, "attribute names", requiredSchemaVersions);
if (ret != Audit.Status.Ok)
return SetInvalid();
IsValid = true;
// if we have valid attributes we can restrict the valid types depending on them
TypesFilter = new IfcConcreteTypeList(SchemaInfo.SharedClassesForAttributes(requiredSchemaVersions, matches));
return ret;

Audit.Status ret = Audit.Status.Ok;
requiredSchemaVersions.TryGetSchemaInformation(out var schemas);
foreach (var schema in schemas)
{
var validAttributeNames = SchemaInfo.AllAttributes
.Where(x => (x.ValidSchemaVersions & schema.Version) == schema.Version)
.Select(y => y.IfcAttributeName);
ret |= sm.DoesMatch(validAttributeNames, false, logger, out var matches, "attribute names", schema.Version);
if (ret != Audit.Status.Ok)
return SetInvalid();
// if we have valid attributes we can restrict the valid types depending on them
typeFilters.Add(schema, new IfcConcreteTypeList(SchemaInfo.SharedClassesForAttributes(schema.Version, matches)));
}
IsValid = true;


return ret;
}
private Audit.Status SetInvalid()
{
TypesFilter = null;
typeFilters.Clear();
IsValid = false;
return Audit.Status.IdsContentError;
}
Expand Down
82 changes: 49 additions & 33 deletions ids-lib/IdsSchema/IdsNodes/Facets/IdsEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,29 @@
using IdsLib.IfcSchema.TypeFilters;
using IdsLib.Messages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace IdsLib.IdsSchema.IdsNodes;

internal class IdsEntity : IdsXmlNode, IIfcTypeConstraintProvider, IIdsFacet
{
public IIfcTypeConstraint? TypesFilter { get; private set; } = null;
/// <summary>
/// prepared typefilters per schema version
/// </summary>
private readonly Dictionary<SchemaInfo, IIfcTypeConstraint> typeFilters = new();

public IdsEntity(System.Xml.XmlReader reader, IdsXmlNode? parent) : base(reader, parent)
/// <inheritdoc />
public IIfcTypeConstraint? GetTypesFilter(SchemaInfo schema)
{
if (typeFilters.TryGetValue(schema, out var typeFilter))
return typeFilter;
return null;
}

public IdsEntity(System.Xml.XmlReader reader, IdsXmlNode? parent) : base(reader, parent)
{
IsValid = false;
}
Expand All @@ -30,57 +43,60 @@ internal protected override Audit.Status PerformAudit(ILogger? logger)
var sm = name?.GetListMatcher();
if (sm is null)
return IdsMessages.Report102NoStringMatcher(logger, this, "name");
var ValidClassNames = SchemaInfo.AllClasses
.Where(x => (x.ValidSchemaVersions & requiredSchemaVersions) == requiredSchemaVersions)
.Select(y => y.UpperCaseName);
var ret = sm.DoesMatch(ValidClassNames, false, logger, out var possibleClasses, "entity names", requiredSchemaVersions);
if (ret != Audit.Status.Ok)
return SetInvalid();

IsValid = true;
TypesFilter = new IfcConcreteTypeList(possibleClasses);
// we introduce a schema-by-schema evaluation of the valid classes
//
requiredSchemaVersions.TryGetSchemaInformation(out var schemas);
Audit.Status ret = Audit.Status.Ok;
IsValid = true;
foreach (var schema in schemas)
{
var ValidClassNames = schema
.Select(y => y.Name.ToUpperInvariant());
ret |= sm.DoesMatch(ValidClassNames, false, logger, out var possibleClasses, "entity names", schema.Version);
if (ret != Audit.Status.Ok)
return SetInvalid();
typeFilters.Add(schema, new IfcConcreteTypeList(possibleClasses));

// predefined types that are common for the possibleClasses across defined schemas
var type = GetChildNodes("predefinedType").FirstOrDefault();
if (type is null)
return ret;
// predefined types that are common for the possibleClasses across defined schemas
var predefinedType = GetChildNodes("predefinedType").FirstOrDefault();
if (predefinedType is null)
continue;

var predefinedTypeMatcher = type.GetListMatcher();
if (predefinedTypeMatcher is null)
return IdsMessages.Report102NoStringMatcher(logger, this, "predefinedType");
var predefinedTypeMatcher = predefinedType.GetListMatcher();
if (predefinedTypeMatcher is null)
return IdsMessages.Report102NoStringMatcher(logger, this, "predefinedType");

var schemas = SchemaInfo.GetSchemas(spec.SchemaVersions);
List<string>? possiblePredefined = null;
foreach (var s in schemas)
{

List<string>? possiblePredefined = null;
foreach (var ifcClass in possibleClasses)
{
var c = s[ifcClass];
var c = schema[ifcClass];
if (c is null)
{
ret |= IdsMessages.Report501UnexpectedScenario(logger, $"class metadata for {ifcClass} not found in required schema.", this);
ret |= IdsMessages.Report501UnexpectedScenario(logger, $"class metadata for {ifcClass} not found in schema {schema.Version}.", this);
continue;
}
if (possiblePredefined == null)
possiblePredefined = new List<string>(c.PredefinedTypeValues);
else
else
possiblePredefined = possiblePredefined.Intersect(c.PredefinedTypeValues).ToList();
}

if (possiblePredefined == null)
ret |= IdsMessages.Report105InvalidDataConfiguration(logger, this, "predefinedType");
else if (possiblePredefined.Contains("USERDEFINED")) // if a user defined option is available then any value is acceptable
return ret;
else
// todo: ensure that this notifies an error and that error cases are added for multiple enumeration values
ret |= predefinedTypeMatcher.DoesMatch(possiblePredefined, false, logger, out var matches, "PredefinedTypes", schema.Version);
}
if (possiblePredefined == null)
ret |= IdsMessages.Report105InvalidDataConfiguration(logger, this, "predefinedType");
else if (possiblePredefined.Contains("USERDEFINED")) // if a user defined option is available then any value is acceptable
return ret;
else
// todo: ensure that this notifies an error and that error cases are added for multiple enumeration values
ret |= predefinedTypeMatcher.DoesMatch(possiblePredefined, false, logger, out var matches, "PredefinedTypes", requiredSchemaVersions);

return ret;
}

private Audit.Status SetInvalid()
{
TypesFilter = null;
typeFilters.Clear();
IsValid = false;
return Audit.Status.IdsContentError;
}
Expand Down
42 changes: 17 additions & 25 deletions ids-lib/IdsSchema/IdsNodes/Facets/IdsFacet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using IdsLib.IfcSchema.TypeFilters;
using IdsLib.Messages;
using Microsoft.Extensions.Logging;
using System;

namespace IdsLib.IdsSchema.IdsNodes;

Expand All @@ -18,39 +19,30 @@ public IdsFacet(System.Xml.XmlReader reader, IdsXmlNode? parent) : base(reader,
minMaxOccurr = new MinMaxCardinality(reader);
}

public bool IsValid { get; private set; } = true;
/// <inheritdoc />
public bool IsValid { get; private set; } = true;

public bool IsRequired => minMaxOccurr.IsRequired;
/// <inheritdoc />
public bool IsRequired => minMaxOccurr.IsRequired;

private IIfcTypeConstraint? typesFilter;
public IIfcTypeConstraint? TypesFilter
{
get
{
if (!IsRequired)
return null;
return typesFilter;
}

private set => typesFilter = value;
}
/// <inheritdoc />
public IIfcTypeConstraint? GetTypesFilter(SchemaInfo schema)
{
if (!IsRequired)
return null;
if (type == "classification")
return new IfcConcreteTypeList(schema.GetRelAsssignClassificationClasses());
else
return new IfcConcreteTypeList(schema.GetRelAsssignClasses());
}

protected internal override Audit.Status PerformAudit(ILogger? logger)
{
if (!TryGetUpperNode<IdsSpecification>(logger, this, IdsSpecification.SpecificationIdentificationArray, out var spec, out var retStatus))
return retStatus;
var requiredSchemaVersions = spec.SchemaVersions;
if (IsRequired)
{
if (type == "classification")
TypesFilter = new IfcConcreteTypeList(SchemaInfo.GetRelAsssignClassificationClasses(requiredSchemaVersions));
else
TypesFilter = new IfcConcreteTypeList(SchemaInfo.GetRelAsssignClasses(requiredSchemaVersions));
}
return base.PerformAudit(logger);
}

public Audit.Status PerformCardinalityAudit(ILogger? logger)
/// <inheritdoc />
public Audit.Status PerformCardinalityAudit(ILogger? logger)
{
var ret = Audit.Status.Ok;
if (minMaxOccurr.Audit(out var _) != Audit.Status.Ok)
Expand Down
78 changes: 50 additions & 28 deletions ids-lib/IdsSchema/IdsNodes/Facets/IdsPartOf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using IdsLib.IfcSchema.TypeFilters;
using IdsLib.Messages;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

namespace IdsLib.IdsSchema.IdsNodes;
Expand All @@ -11,11 +13,21 @@ internal class IdsPartOf : IdsXmlNode, IIdsCardinalityFacet, IIfcTypeConstraintP
private const string relationXmlAttributeName = "relation";
private readonly MinMaxCardinality minMaxOccurr;
private readonly string relationValue;


public IIfcTypeConstraint? TypesFilter { get; private set; }

/// <summary>
/// prepared typefilters per schema version
/// </summary>
private readonly Dictionary<SchemaInfo, IIfcTypeConstraint> typeFilters = new();

public bool IsRequired => minMaxOccurr.IsRequired;
/// <inheritdoc />
public IIfcTypeConstraint? GetTypesFilter(SchemaInfo schema)
{
if (typeFilters.TryGetValue(schema, out var typeFilter))
return typeFilter;
return null;
}

public bool IsRequired => minMaxOccurr.IsRequired;

public IdsPartOf(System.Xml.XmlReader reader, IdsXmlNode? parent) : base(reader, parent)
{
Expand Down Expand Up @@ -50,7 +62,8 @@ internal protected override Audit.Status PerformAudit(ILogger? logger)
if (!IsRequired)
{
IsValid = true;
TypesFilter = null;
typeFilters.Clear();
return ret;
}

// if we have a match then there are other constraints we can evaluate on the
Expand All @@ -65,31 +78,40 @@ internal protected override Audit.Status PerformAudit(ILogger? logger)
return SetInvalid(ret);
}

// Entities of the partOf need to be of type of relationInfo.ManySideIfcType
TypesFilter = new IfcInheritanceTypeConstraint(relationInfo.ManySideIfcType, requiredSchemaVersions);
if (IfcTypeConstraint.IsNotNullAndEmpty(TypesFilter))
requiredSchemaVersions.TryGetSchemaInformation(out var schemas);
foreach (var schema in schemas)
{
ret |= IdsMessages.Report501UnexpectedScenario(logger, $"no valid types found for {relationInfo.ManySideIfcType}", this);
return SetInvalid(ret);
}
// Entities of the partOf need to be of type of relationInfo.ManySideIfcType
//
var filter = new IfcInheritanceTypeConstraint(relationInfo.ManySideIfcType, schema.Version);
if (IfcTypeConstraint.IsNotNullAndEmpty(filter))
{
ret |= IdsMessages.Report501UnexpectedScenario(logger, $"no valid types found for {relationInfo.ManySideIfcType}", this);
return SetInvalid(ret);
}
typeFilters.Add(schema, filter);

// The entity needs to be of type of relationInfo.OneSideIfcType
if (GetChildNodes("entity").FirstOrDefault() is not IIfcTypeConstraintProvider childEntity)
{
ret |= IdsMessages.Report106InvalidEmtpyValue(logger, this, "entity");
return SetInvalid(ret);
}
// The entity needs to be of type of relationInfo.OneSideIfcType
//
if (GetChildNodes("entity").FirstOrDefault() is not IIfcTypeConstraintProvider childEntity)
{
ret |= IdsMessages.Report106InvalidEmtpyValue(logger, this, "entity");
return SetInvalid(ret);
}

IsValid = true;
var validChildEntityType = new IfcInheritanceTypeConstraint(relationInfo.OneSideIfcType, requiredSchemaVersions);
var possible = validChildEntityType.Intersect(childEntity.TypesFilter);
if (possible.IsEmpty)
{
ret |= IdsMessages.Report201IncompatibleClauses(logger, this, "relation not compatible with provided child entity");
return SetInvalid(ret);
var validChildEntityType = new IfcInheritanceTypeConstraint(relationInfo.OneSideIfcType, schema.Version);
var possible = validChildEntityType.Intersect(childEntity.GetTypesFilter(schema));
if (possible.IsEmpty)
{
ret |= IdsMessages.Report201IncompatibleClauses(logger, this, schema, "relation not compatible with provided child entity");
return SetInvalid(ret);
}

}
return ret;
}
IsValid = true;
return ret;
}

public Audit.Status PerformCardinalityAudit(ILogger? logger)
{
Expand All @@ -103,8 +125,8 @@ public Audit.Status PerformCardinalityAudit(ILogger? logger)
}
private Audit.Status SetInvalid(Audit.Status status = Audit.Status.IdsContentError)
{
TypesFilter = null;
IsValid = false;
typeFilters.Clear();
IsValid = false;
return status;
}

Expand Down
Loading

0 comments on commit efd01fd

Please sign in to comment.