Files
SzmediTools/Szmedi.RevitToolkit.Approval/Assists/MvdLiteAssist.cs
2025-09-16 16:06:41 +08:00

504 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Szmedi.RevitToolkit.Approval.Assists;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Windows.Markup;
using Autodesk.Revit.DB.ExtensibleStorage;
using Newtonsoft.Json;
using Szmedi.RevitToolkit.Approval.Models;
using static Dapper.SqlMapper;
/// <summary>
/// 用于保存从MVDLite文本中解析出的结构化数据的类。
/// </summary>
public class MvdData
{
/// <summary>
/// 文件头部的元数据。
/// 例如:# MVDLite_Version = '2.5.0.0'
/// </summary>
public Dictionary<string, string> Metadata { get; set; } = [];
/// <summary>
/// 路径别名定义列表。
/// 例如: "hasAssignments as (IfcObjectDefinition)->HasAssignments->RelatedObjectsType"
/// </summary>
public List<Alias> Aliases { get; set; } = new List<Alias>();
/// <summary>
/// 实体定义列表。
/// </summary>
public List<EntityDefinition> EntityDefinitions { get; set; } = [];
}
/// <summary>
/// 代表一个规则定义
/// </summary>
public class Alias
{
public string Name { get; set; }
public string SourceType { get; set; }
public string Path { get; set; }
public override string ToString() => $"{Name} as ({SourceType}){Path}";
}
/// <summary>
/// 代表一个在 "定义" 块中定义的实体。
/// </summary>
public class EntityDefinition
{
/// <summary>
/// 继承那一行的名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 定义后的第一行描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 继承自的名称
/// </summary>
public string InheritsFrom { get; set; }
/// <summary>
/// 新增:专门存放“类型标签”的值列表
/// </summary>
public List<string> TypeLabels { get; set; } = [];
/// <summary>
/// 类型判断
/// </summary>
public List<string> Conditions { get; set; } = [];
/// <summary>
/// 约束,每个约束就是一个属性条目
/// </summary>
public List<Constraint> Constraints { get; set; } = [];
public string IfcMap { get; set; } // 来自 "# IFC_MAP = 'IfcElement'" 这样的注释
public override string ToString() => $"Entity: {Name}" + (string.IsNullOrEmpty(InheritsFrom) ? "" : $" inherits {InheritsFrom}");
}
/// <summary>
/// 代表一个在 "约束" 块中定义的规则,即一个属性条目
/// </summary>
public class Constraint
{
/// <summary>
/// 描述约束规则,如:'预制剪力墙 的属性 \"编号\" 应满足值类型约束规则'
/// </summary>
public string Description { get; set; }
/// <summary>
/// 表达式,如:预制剪力墙->任意属性集->任意属性('编号')->属性值[Type]>='STRING'
/// </summary>
public string Expression { get; set; }
/// <summary>
///用于解析注释中的额外信息,例如: # PropName = '型号规格'keyPropNameDefaultValue型号规格KeyRangeDefaultValue>=0
/// 固定只有两个,一个是属性名,一个取值范围
/// </summary>
public Dictionary<string, string> PropertyDictionary { get; set; } = [];
/// <summary>
/// 参数值类型
/// </summary>
public string ParameterType
{
get
{
if (ValueRange.Contains("实数值"))
{
return "Number";
}
return "Text";
}
}
/// <summary>
/// 参数的取值范围
/// </summary>
public string[] Values
{
get
{
var range = ValueRange.Replace(" ", "");
if (range.Contains("实数值") || range.Contains(">=0"))
{
return ["0"];
}
else if (range.Contains(">0"))
{
if (range.Contains("<=1"))
{
return ["1"];
}
return ["100"];
}
else if (range.Contains("字符串值"))
{
return [""];
}
return [.. Regex.Matches(range, @"\\""([^""]*)\\""").OfType<Match>().Select(m => m.Groups[1].Value)];
}
}
/// <summary>
/// 属性名
/// </summary>
public string PropName => PropertyDictionary.ContainsKey("PropName") ? PropertyDictionary["PropName"] : string.Empty;
/// <summary>
/// 取值范围
/// </summary>
public string ValueRange => PropertyDictionary.ContainsKey("Range") ? PropertyDictionary["Range"] : string.Empty;
public override string ToString() => $"Constraint: {PropName} => {ValueRange}";
}
/// <summary>
/// 一个静态工具类用于将MVDLite格式的文本数据解析为结构化的C#对象。
/// </summary>
public class MvdLiteAssist
{
public static List<string> GetSigns(Major major, string entityName, string inheritsFrom)
{
var archi = IOAssists.GetMvdLiteContent(major);
var archiData = MvdLiteAssist.Parse(archi);
var archiDefinition = MvdLiteAssist.GetDefinitionByName(archiData, entityName, inheritsFrom);
var archiExpression = archiDefinition.Constraints.FirstOrDefault().Expression;
var archiSigns = Regex.Matches(archiExpression, @"'([^']*)'").OfType<Match>().Select(m => m.Groups[1].Value);
return [.. archiSigns];
}
public static List<string> GetSigns(string majorName, string entityName, string inheritsFrom)
{
var archi = IOAssists.GetMvdLiteContent(majorName);
var archiData = MvdLiteAssist.Parse(archi);
var archiDefinition = MvdLiteAssist.GetDefinitionByName(archiData, entityName, inheritsFrom);
var archiExpression = archiDefinition.Constraints.FirstOrDefault().Expression;
var archiSigns = Regex.Matches(archiExpression, @"'([^']*)'").OfType<Match>().Select(m => m.Groups[1].Value);
return [.. archiSigns];
}
private enum ParserState
{
Idle, // 用于解析元数据和别名
InDefinition,
InConstraint
}
/// <summary>
/// 获取所有直接包含属性的集合(不包含子类继承父类的属性)
/// </summary>
/// <param name="mvdData"></param>
/// <returns></returns>
public static List<AfcaArchiProperty> GetProperties(MvdData mvdData)
{
List<AfcaArchiProperty> properties = [];
List<EntityDefinition> definitions = mvdData.EntityDefinitions;
foreach (var entity in definitions)
{
var constraints = entity.Constraints;
foreach (var cons in constraints)
{
if (string.IsNullOrEmpty(cons.PropName) || string.IsNullOrEmpty(cons.Description))
{
continue;
}
var property = new AfcaArchiProperty
{
DefinitionName = entity.Name,
Name = cons.PropName,
ValueConstraints = cons.Values,
ValueRange = cons.ValueRange,
};
properties.Add(property);
//sb.AppendLine($"Entity: {entity.Name}, Property: {cons.PropName}, DefaultValue: {cons.ValueRange}");
}
}
return properties;
}
/// <summary>
/// 获取所有构件标识列表
/// </summary>
/// <param name="mvdData"></param>
/// <returns></returns>
public static HashSet<string> GetElementSigns(MvdData mvdData, string ifcClass = "IfcElement")
{
HashSet<string> signs = [];
List<EntityDefinition> definitions = mvdData.EntityDefinitions;
foreach (var entity in definitions)
{
if (!string.IsNullOrEmpty(entity.Description) && entity.IfcMap == ifcClass)
{
foreach (var label in entity.TypeLabels)
{
signs.Add(label);
}
}
}
return signs;
}
/// <summary>
/// 解析包含MVDLite数据的文本字符串。
/// </summary>
/// <param name="textContent">要解析的文本内容。</param>
/// <returns>一个包含所有解析后数据的 MvdData 对象。</returns>
public static MvdData Parse(string textContent)
{
var mvdData = new MvdData();
if (string.IsNullOrWhiteSpace(textContent)) return mvdData;
var lines = textContent.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
ParserState state = ParserState.Idle;
EntityDefinition currentEntity = null;
Constraint currentConstraint = null;
// 用于匹配不同类型行的正则表达式
var metaRegex = new Regex(@"^#\s*([^=]+?)\s*=\s*'(.*)'");
var aliasRegex = new Regex(@"^(\S+)\s+as\s+\((.*?)\)(.*)");
var entityInheritRegex = new Regex(@"^(\S+)\s+继承\s+(\S+)");
var descriptionCommentRegex = new Regex(@"^#\s*'(.*)'");
var ifcMapCommentRegex = new Regex(@"^#\s*IFC_MAP\s*=\s*'(.*)'");
var propCommentRegex = new Regex(@"^#\s*(\w+)\s*=\s*'(.*)'");
// 新增:专门用于匹配“类型标签”的正则表达式
var typeLabelRegex = new Regex(@"^(\S+)->类型标签\s*=\s*(.*)$");
foreach (var line in lines)
{
var trimmedLine = line.Trim();
if (trimmedLine.StartsWith("定义"))
{
state = ParserState.InDefinition;
currentEntity = new EntityDefinition();
mvdData.EntityDefinitions.Add(currentEntity);
continue;
}
if (trimmedLine.StartsWith("约束"))
{
state = ParserState.InConstraint;
currentConstraint = null;
continue;
}
if (trimmedLine.StartsWith("#BLOCK") || string.IsNullOrWhiteSpace(trimmedLine))
{
if (state == ParserState.InConstraint && currentEntity != null && currentConstraint != null && !string.IsNullOrEmpty(currentConstraint.Expression))
{
currentEntity.Constraints.Add(currentConstraint);
}
currentConstraint = null;
if (string.IsNullOrWhiteSpace(trimmedLine)) continue;
if (trimmedLine.StartsWith("#BLOCK"))
{
state = ParserState.Idle;
currentEntity = null;
continue;
}
}
switch (state)
{
case ParserState.Idle:
Match metaMatch = metaRegex.Match(trimmedLine);
if (metaMatch.Success)
{
mvdData.Metadata[metaMatch.Groups[1].Value.Trim()] = metaMatch.Groups[2].Value;
break;
}
Match aliasMatch = aliasRegex.Match(trimmedLine);
if (aliasMatch.Success)
{
mvdData.Aliases.Add(new Alias
{
Name = aliasMatch.Groups[1].Value,
SourceType = aliasMatch.Groups[2].Value,
Path = aliasMatch.Groups[3].Value.Trim()
});
}
break;
case ParserState.InDefinition:
Match descMatch = descriptionCommentRegex.Match(trimmedLine);
if (descMatch.Success)
{
currentEntity.Description = descMatch.Groups[1].Value;
break;
}
Match ifcMapMatch = ifcMapCommentRegex.Match(trimmedLine);
if (ifcMapMatch.Success)
{
currentEntity.IfcMap = ifcMapMatch.Groups[1].Value;
break;
}
// --- 新增逻辑:优先处理类型标签 ---
Match typeLabelMatch = typeLabelRegex.Match(trimmedLine);
if (typeLabelMatch.Success)
{
string allLabelsRaw = typeLabelMatch.Groups[2].Value;
// 使用正则表达式从长字符串中提取所有被单引号包裹的值
var labelMatches = Regex.Matches(allLabelsRaw, @"'([^']*)'");
var labels = labelMatches.Cast<Match>()
.Select(m => m.Groups[1].Value)
.ToList();
currentEntity.TypeLabels.AddRange(labels);
break; // 处理完毕,跳过此行后续逻辑
}
// --- 结束新增逻辑 ---
Match entityInheritMatch = entityInheritRegex.Match(trimmedLine);
if (entityInheritMatch.Success)
{
currentEntity.Name = entityInheritMatch.Groups[1].Value;
currentEntity.InheritsFrom = entityInheritMatch.Groups[2].Value;
}
else if (!trimmedLine.StartsWith("#"))
{
// 其他非注释行、非类型标签行,被视为普通条件
currentEntity.Conditions.Add(trimmedLine);
}
break;
case ParserState.InConstraint:
if (trimmedLine.StartsWith("#"))
{
Match constraintDescMatch = descriptionCommentRegex.Match(trimmedLine);
if (constraintDescMatch.Success)
{
if (currentEntity != null && currentConstraint != null && !string.IsNullOrEmpty(currentConstraint.Expression))
{
currentEntity.Constraints.Add(currentConstraint);
}
currentConstraint = new Constraint { Description = constraintDescMatch.Groups[1].Value };
}
else
{
Match propMatch = propCommentRegex.Match(trimmedLine);
if (propMatch.Success)
{
if (currentConstraint == null) currentConstraint = new Constraint();
currentConstraint.PropertyDictionary[propMatch.Groups[1].Value] = propMatch.Groups[2].Value;
}
}
}
else
{
if (currentConstraint == null)
{
currentConstraint = new Constraint();
}
currentConstraint.Expression = trimmedLine;
if (currentEntity != null)
{
currentEntity.Constraints.Add(currentConstraint);
}
currentConstraint = null;
}
break;
}
}
if (currentEntity != null && currentConstraint != null && !string.IsNullOrEmpty(currentConstraint.Expression))
{
currentEntity.Constraints.Add(currentConstraint);
}
return mvdData;
}
/// <summary>
/// 获取任意标识需要的所有属性
/// </summary>
/// <param name="data"></param>
/// <param name="labelName"></param>
/// <returns></returns>
public static List<Constraint> GetConstraintsByLabel(MvdData data, string labelName)
{
var result = new List<Constraint>();
var definitions = FindDefinitionsByTypeLabel(data, labelName);
foreach (var entity in definitions)
{
var constraints = entity.Constraints;
foreach (var cons in constraints)
{
if (string.IsNullOrEmpty(cons.PropName) || string.IsNullOrEmpty(cons.Description))
{
continue;
}
result.Add(cons);
//sb.AppendLine($"Entity: {entity.Name}, Property: {cons.PropName}, DefaultValue: {cons.ValueRange}");
}
}
return result;
}
/// <summary>
/// 按定义名称查找定义
/// </summary>
/// <param name="mvdData"></param>
/// <param name="name"></param>
/// <returns></returns>
public static EntityDefinition GetDefinitionByName(MvdData mvdData, string name)
{
if (mvdData == null || mvdData.EntityDefinitions == null || string.IsNullOrEmpty(name))
{
return null;
}
return mvdData.EntityDefinitions.FirstOrDefault(def => def.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// 按定义名称查找定义
/// </summary>
/// <param name="mvdData"></param>
/// <param name="name"></param>
/// <returns></returns>
public static EntityDefinition GetDefinitionByName(MvdData mvdData, string name, string inheritsFrom)
{
if (mvdData == null || mvdData.EntityDefinitions == null || string.IsNullOrEmpty(name))
{
return null;
}
return mvdData.EntityDefinitions.FirstOrDefault(def => def.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && def.InheritsFrom.ToLower() == inheritsFrom.ToLower());
}
/// <summary>
/// 查找所有在其类型标签列表中包含指定名称的实体定义。
/// </summary>
/// <param name="mvdData">要搜索的数据源。</param>
/// <param name="labelToFind">要查找的类型标签名称。</param>
/// <param name="comparisonType">一个枚举值,指定比较字符串时是否区分大小写。默认为区分大小写。</param>
/// <returns>一个包含所有匹配的实体定义的列表。</returns>
public static List<EntityDefinition> FindDefinitionsByTypeLabel(
MvdData mvdData,
string labelToFind,
StringComparison comparisonType = StringComparison.Ordinal)
{
var allDefinitions = mvdData.EntityDefinitions;
// 如果输入为空,则返回一个空列表
if (allDefinitions == null || string.IsNullOrEmpty(labelToFind) || mvdData.EntityDefinitions.Count == 0)
{
return [];
}
// 使用 LINQ 进行高效查询:
// 遍历所有实体定义def
// 并在每个实体的 TypeLabels 列表中查找Any是否存在一个标签label
// 与要查找的标签labelToFind相等。
return [.. allDefinitions.Where(
def => def.TypeLabels.Any(label => label.Equals(labelToFind, comparisonType)))];
}
}