Skip to content

Commit

Permalink
Addressing issues.
Browse files Browse the repository at this point in the history
- #7: documentation fixed for correct capitalisation of `Pset_`
- #8: added test case with regex error and fixed reporting.
- #9: Provided new error code and prevented exception.
- #11: the code was already ok, but added a test case.
  • Loading branch information
CBenghi committed Oct 7, 2023
1 parent ce816ee commit 62f47c9
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 86 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,20 @@ We are planning to implement a number of audits (the ones with a check-mark are
- [x] Multiple values allowed
- [x] Properties
- [x] Prepare metadata infrastructure
- [x] Prop and PSet names are treated as case sensitive
- [x] no extensions of standard PSets
- [x] Prop and PropertySet names are treated as case sensitive
- [x] no extensions of standard `Psets`
- [x] properties are limited to the agreed set
- [x] Includes IFC type inheritance
- [x] Reserved prefix
- [x] No custom PSet can mandate a property starting with "PSet_" this prefix is reserved for the standard
- [x] No custom PropertySet can mandate a property starting with "Pset_" this prefix is reserved for the standard
- [x] SimpleValues
- [x] Enumerations
- [x] Property Measures
- [x] If a value is provided, it is checked against a closed list
- [ ] Discuss the scope of the list, currently only measures, IFCTEXT and IFCLABEL (but it's now datatype, so we should probably expand).
- [ ] The attribute is required, but it can be empty, is that valid?
- [x] Test cases added
- [x] Constrain IfcMeasure for identified properties in standard PSets
- [x] Constrain datatype for properties identified in standard Psets
- [x] Cardinality for facets (in requirements)
- [x] partOf
- [x] classification
Expand Down Expand Up @@ -113,7 +113,7 @@ We are planning to implement a number of audits (the ones with a check-mark are
- [ ] perhaps just consider this a warning for user-defined property sets
- [ ] warning or error as configuration (strict mode?)
- [ ] Reserved prefix
- [ ] can the regex match any string starting with "PSet_".
- [ ] can the regex match any string starting with "Pset_".
- [ ] we have a working solution to be included, but it may raise the technical complexity for implementers considerably
- [ ] Perhaps one for the strict mode?
- [ ] Measures
Expand Down
134 changes: 72 additions & 62 deletions ids-lib/Audit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,81 +242,91 @@ private static async Task<Status> AuditStreamAsync(Stream theStream, AuditHelper
IdsXmlNode? current = null;
var prevSchemaStatus = auditSettings.SchemaStatus;

while (await reader.ReadAsync()) // the loop reads the entire file to trigger validation events.
try
{
cntRead++;

switch (reader.NodeType)
while (await reader.ReadAsync()) // the loop reads the entire file to trigger validation events.
{
// audits are performed on closing the element end, so that all the children are available for evaluation.
// but empty elements (e.g., <someElement />) are audited upon opening, as there are no children to evaluate
//
case XmlNodeType.Element:
IdsXmlNode? parent = null;
cntRead++;

switch (reader.NodeType)
{
// audits are performed on closing the element end, so that all the children are available for evaluation.
// but empty elements (e.g., <someElement />) are audited upon opening, as there are no children to evaluate
//
case XmlNodeType.Element:
IdsXmlNode? parent = null;
#if NETSTANDARD2_0
if (elementsStack.Count > 0)
parent = elementsStack.Peek();
#else
if (elementsStack.TryPeek(out var peeked))
parent = peeked;
if (elementsStack.TryPeek(out var peeked))
parent = peeked;
#endif
var newContext = IdsXmlHelpers.GetContextFromElement(reader, parent, logger); // this is always not null
if (newContext is IdsSpecification spec)
// parents of IdsSpecification do not retain children for Garbage Collection purposes
// so we need to set the positional index manually
spec.PositionalIndex = iSpecification++;
while (auditSettings.BufferedValidationIssues.Any())
{
var queuedIssue = auditSettings.BufferedValidationIssues.Dequeue();
if (
newContext.type == "attribute"
&&
(queuedIssue.Message.Contains("minOccurs") || queuedIssue.Message.Contains("maxOccurs"))
)
var newContext = IdsXmlHelpers.GetContextFromElement(reader, parent, logger); // this is always not null
if (newContext is IdsSpecification spec)
// parents of IdsSpecification do not retain children for Garbage Collection purposes
// so we need to set the positional index manually
spec.PositionalIndex = iSpecification++;
while (auditSettings.BufferedValidationIssues.Any())
{
// this could fail under some circumstances, but it's a temporary workaround
auditSettings.SchemaStatus = prevSchemaStatus;
continue;
}
queuedIssue.Notify(logger, newContext);
}
var queuedIssue = auditSettings.BufferedValidationIssues.Dequeue();
if (
newContext.type == "attribute"
&&
(queuedIssue.Message.Contains("minOccurs") || queuedIssue.Message.Contains("maxOccurs"))
)
{
// this could fail under some circumstances, but it's a temporary workaround
auditSettings.SchemaStatus = prevSchemaStatus;
continue;
}
queuedIssue.Notify(logger, newContext);
}

// we only push on the stack if it's not empty, e.g.: <some /> does not go on the stack
if (!reader.IsEmptyElement)
elementsStack.Push(newContext);
else
{
if (!auditSettings.Options.OmitIdsContentAudit)
contentStatus |= newContext.PerformAudit(logger); // invoking audit on empty element happens immediately
}
current = newContext;
break;
// we only push on the stack if it's not empty, e.g.: <some /> does not go on the stack
if (!reader.IsEmptyElement)
elementsStack.Push(newContext);
else
{
if (!auditSettings.Options.OmitIdsContentAudit)
contentStatus |= newContext.PerformAudit(logger); // invoking audit on empty element happens immediately
}
current = newContext;
break;

case XmlNodeType.Text:
// Debug.WriteLine($" Text Node: {reader.GetValueAsync().Result}");
current!.SetContent(reader.GetValueAsync().Result);
break;
case XmlNodeType.EndElement:
// Debug.WriteLine($"End Element {reader.LocalName}");
var closing = elementsStack.Pop();
// Debug.WriteLine($" auditing {closing.type} on end element");
if (!auditSettings.Options.OmitIdsContentAudit)
{
contentStatus |= closing.PerformAudit(logger); // invoking audit on end of element
}
break;
default:
// Debug.WriteLine("Other node {0} with value '{1}'.", reader.NodeType, reader.Value);
break;
case XmlNodeType.Text:
// Debug.WriteLine($" Text Node: {reader.GetValueAsync().Result}");
current!.SetContent(reader.GetValueAsync().Result);
break;
case XmlNodeType.EndElement:
// Debug.WriteLine($"End Element {reader.LocalName}");
var closing = elementsStack.Pop();
// Debug.WriteLine($" auditing {closing.type} on end element");
if (!auditSettings.Options.OmitIdsContentAudit)
{
contentStatus |= closing.PerformAudit(logger); // invoking audit on end of element
}
break;
default:
// Debug.WriteLine("Other node {0} with value '{1}'.", reader.NodeType, reader.Value);
break;
}
prevSchemaStatus = auditSettings.SchemaStatus;
}
prevSchemaStatus = auditSettings.SchemaStatus;
IdsToolMessages.InformReadCount(logger, cntRead);
}

reader.Dispose();
catch (XmlException ex_xml)
{
contentStatus = contentStatus | IdsToolMessages.Report502XmlSchemaException(logger, ex_xml);
}
catch (Exception ex)
{
contentStatus = contentStatus | IdsToolMessages.Report503Exception(logger, ex);
}
reader.Dispose();
if (!auditSettings.Options.OmitIdsSchemaAudit)
rSettings.ValidationEventHandler -= new ValidationEventHandler(auditSettings.ValidationReporter);

IdsToolMessages.ReportReadCount(logger, cntRead);

return auditSettings.SchemaStatus | contentStatus;
}
Expand Down Expand Up @@ -348,7 +358,7 @@ private static Status PopulateSchema(Stream vrs, ISchemaProvider schemaProvider,
private static Status ProcessSingleFile(FileInfo theFile, IBatchAuditOptions batchOptions, ILogger? logger)
{
Status ret = Status.Ok;
IdsToolMessages.ReportFileProcessingStarted(logger, theFile.FullName);
IdsToolMessages.InformFileProcessingStarted(logger, theFile.FullName);
ret |= AuditIdsComplianceAsync(batchOptions, theFile, logger).Result;
return ret;
}
Expand All @@ -369,7 +379,7 @@ private static Status ProcessFolder(DirectoryInfo directoryInfo, IBatchAuditOpti
ret |= sgl;
tally++;
}
IdsToolMessages.ReportFileProcessingEnded(logger, tally);
IdsToolMessages.InformFileProcessingEnded(logger, tally);

return ret;
}
Expand Down
4 changes: 2 additions & 2 deletions ids-lib/AuditHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ internal void Notify(ILogger? logger, IdsSchema.IdsXmlNode newContext)
loc.StartLinePosition = Position;
loc.NodeType = newContext.type;
if (Schema == Original.SchemaError)
IdsErrorMessages.ReportSchema306ComplianceError(logger, loc, Message);
IdsErrorMessages.Report306SchemaComplianceError(logger, loc, Message);
else
IdsErrorMessages.ReportSchema307ComplianceWarning(logger, Level, loc, Message);
IdsErrorMessages.Report307SchemaComplianceWarning(logger, Level, loc, Message);
}
}

Expand Down
8 changes: 7 additions & 1 deletion ids-lib/ErrorCodes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# List of error codes for Ids-Audit-Tool
# List of error codes for Ids-Audit

## Errors

Expand All @@ -12,6 +12,8 @@
| 105 | No valid option exist |
| 106 | Invalid empty attribute |
| 107 | Invalid IFC schema identification |
| 108 | Unsupported Ids Schema |
| 109 | Invalid regex |
| 200 | Integration issues |
| 201 | Incompatible clauses |
| 300 | Schema clauses |
Expand All @@ -26,3 +28,7 @@
| 401 | Reserved keyword |
| 500 | Application errors |
| 501 | Unexpected scenario |
| 502 | XML Schema Exception |
| 503 | Processing Exception |
| 504 | IDS Schema version not Embedded |

14 changes: 12 additions & 2 deletions ids-lib/IdsSchema/XsNodes/XsPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@ public Audit.Status DoesMatch(IEnumerable<string> candidateStrings, bool ignoreC
: IdsErrorMessages.Report103InvalidListMatcher(this, pattern, logger, listToMatchName, schemaContext, candidateStrings);
}

public bool TryMatch(IEnumerable<string> candidateStrings, bool ignoreCase, out IEnumerable<string> matches)
protected internal override Audit.Status PerformAudit(ILogger? logger)
{
var ret = Audit.Status.Ok;
if (!EnsureRegex(out var _, false))
{
ret = IdsErrorMessages.Report109InvalidRegex(this, pattern, logger);
}
return base.PerformAudit(logger) | ret;
}

public bool TryMatch(IEnumerable<string> candidateStrings, bool ignoreCase, out IEnumerable<string> matches)
{
if (!EnsureRegex(out var _, ignoreCase))
{
Expand Down Expand Up @@ -58,7 +68,7 @@ private bool EnsureRegex(out string errorMessage, bool ignoreCase)
return true;
try
{
var preProcess = XmlRegex.Preprocess(pattern);
var preProcess = XmlRegex.Preprocess(pattern, false);
if (!ignoreCase)
compiledCaseSensitiveRegex = new Regex(preProcess, RegexOptions.Compiled | RegexOptions.Singleline);
else
Expand Down
2 changes: 1 addition & 1 deletion ids-lib/LibraryInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ public static class LibraryInformation
/// <summary>
/// Static field with hardcoded DLL version number.
/// </summary>
public static string AssemblyVersion => "1.0.44";
public static string AssemblyVersion => "1.0.45";
}
}
12 changes: 10 additions & 2 deletions ids-lib/Messages/IdsErrorMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ internal static void Report108UnsupportedIdsSchema(ILogger? logger, string versi
logger?.LogError("Error {errorCode}: Unsupported schema version '{vers}' on `ids` element, please use 'http://standards.buildingsmart.org/IDS/0.9.6/ids.xsd' instead.", 108, version);
}

internal static Audit.Status Report109InvalidRegex(IdsXmlNode locationContext, string pattern, ILogger? logger)
{
logger?.LogError("Error {errorCode}: Ivalid pattern string '{pattern}' on {location}.", 109, pattern, locationContext.GetNodeIdentification());
return Audit.Status.IdsContentError;
}

internal static Audit.Status Report201IncompatibleClauses(ILogger? logger, IdsXmlNode locationContext, SchemaInfo schemaInfo, string scenarioMessage)
{
logger?.LogError("Error {errorCode}: Inconsistent clauses for {ifcSchema} : {message} on {location}.", 201, schemaInfo.Version, scenarioMessage, locationContext.GetNodeIdentification());
Expand Down Expand Up @@ -131,12 +137,12 @@ internal static Audit.Status Report305BadConstraintValue(ILogger? logger, IdsXml
return Audit.Status.IdsContentError;
}

internal static void ReportSchema306ComplianceError(ILogger? logger, NodeIdentification location, string message)
internal static void Report306SchemaComplianceError(ILogger? logger, NodeIdentification location, string message)
{
logger?.LogError("Error {errorCode}: Schema compliance error on {location}; {message}", 306, location, message);
}

internal static void ReportSchema307ComplianceWarning(ILogger? logger, LogLevel level, NodeIdentification location, string message)
internal static void Report307SchemaComplianceWarning(ILogger? logger, LogLevel level, NodeIdentification location, string message)
{
logger?.Log(level, "Error {errorCode}: Schema compliance warning on {location}; {message}", 307, location, message);
}
Expand All @@ -152,4 +158,6 @@ internal static Audit.Status Report501UnexpectedScenario(ILogger? logger, string
logger?.LogCritical("Error {errorCode}: Unhandled scenario: {message} on {location}.", 501, scenarioMessage, context.GetNodeIdentification());
return Audit.Status.NotImplementedError;
}


}
26 changes: 20 additions & 6 deletions ids-lib/Messages/IdsToolMessages.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using IdsLib.IdsSchema;
using IdsLib.IdsSchema.IdsNodes;
using Microsoft.Extensions.Logging;
using System;
using System.Xml;
using static IdsLib.Audit;

namespace IdsLib.Messages
Expand Down Expand Up @@ -42,17 +44,17 @@ internal static Status ReportInvalidSource(ILogger? logger, string inputSource)
return Status.NotFoundError;
}

internal static void ReportReadCount(ILogger? logger, int cntRead)
internal static void InformReadCount(ILogger? logger, int cntRead)
{
logger?.LogDebug("Completed reading {cntRead} xml elements.", cntRead);
logger?.LogInformation("Completed reading {cntRead} xml elements.", cntRead);
}

internal static void ReportFileProcessingStarted(ILogger? logger, string fullName)
internal static void InformFileProcessingStarted(ILogger? logger, string fullName)
{
logger?.LogInformation("Auditing file: `{filename}`.", fullName);
}

internal static void ReportFileProcessingEnded(ILogger? logger, int tally)
internal static void InformFileProcessingEnded(ILogger? logger, int tally)
{
var fileCardinality = tally != 1 ? "files" : "file";
logger?.LogInformation("{tally} {fileCardinality} processed.", tally, fileCardinality);
Expand Down Expand Up @@ -88,9 +90,21 @@ internal static Status ReportInvalidXsdSource(ILogger? logger, string diskSchema
return Status.XsdSchemaError;
}

internal static Status ReportInvalidSchemaVersion(ILogger? logger, IdsVersion vrs)
internal static Status Report502XmlSchemaException(ILogger? logger, XmlException ex_xml)
{
logger?.LogError("Embedded schema for version {vrs} not implemented.", vrs);
logger?.LogError("Error {errorCode}: XML Exception. Error message: {}", 502, ex_xml);
return Status.IdsStructureError;
}

internal static Status Report503Exception(ILogger? logger, Exception ex)
{
logger?.LogError("Error {errorCode}: Generic Exception. Error message: {}", 503, ex);
return Status.UnhandledError;
}

internal static Status Report504NotImplementedIdsSchemaVersion(ILogger? logger, IdsVersion vrs)
{
logger?.LogError("Error {errorCode}: Embedded schema for version {vrs} not implemented.", 504, vrs);
return Status.NotImplementedError;
}
}
Expand Down
2 changes: 1 addition & 1 deletion ids-lib/SchemaProviders/SchemaProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected static Audit.Status GetResourceSchemasByVersion(IdsVersion vrs, ILogge
break;
case IdsVersion.Invalid:
default:
ret |= IdsToolMessages.ReportInvalidSchemaVersion(logger, vrs);
ret |= IdsToolMessages.Report504NotImplementedIdsSchemaVersion(logger, vrs);
break;
}
schemas = tmpResources.Select(x => GetSchema(x)).ToList();
Expand Down
2 changes: 1 addition & 1 deletion ids-lib/ids-lib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<PackageReleaseNotes>First implementation.</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
<!-- Github actions are setup so that when this version is bumped the nuget package is uploaded -->
<AssemblyVersion>1.0.44</AssemblyVersion>
<AssemblyVersion>1.0.45</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<Version>$(AssemblyVersion)</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
Loading

0 comments on commit 62f47c9

Please sign in to comment.