379 lines
14 KiB
C#
379 lines
14 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
|
|||
|
|
namespace MvdLiteSnoop.MvdLite;
|
|||
|
|
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
|
|||
|
|
/// <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; } = [];
|
|||
|
|
|
|||
|
|
/// <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 = '型号规格',key:PropName,Value:型号规格,Key:Range,Value:>=0
|
|||
|
|
/// 固定只有两个,一个是属性名,一个取值范围
|
|||
|
|
/// </summary>
|
|||
|
|
public Dictionary<string, string> Properties { get; set; } = [];
|
|||
|
|
public string ParameterType
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (ValueRange.Contains("实数值"))
|
|||
|
|
{
|
|||
|
|
return "Number";
|
|||
|
|
}
|
|||
|
|
return "Text";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
public string[] Values
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (ValueRange.Contains("值类型"))
|
|||
|
|
{
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
return [.. Regex.Matches(ValueRange, @"\\""([^""]*)\\""").OfType<Match>().Select(m => m.Groups[1].Value)];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string PropName => Properties.ContainsKey("PropName") ? Properties["PropName"] : string.Empty;
|
|||
|
|
public string ValueRange => Properties.ContainsKey("Range") ? Properties["Range"] : string.Empty;
|
|||
|
|
public override string ToString() => $"Constraint: {PropName} => {ValueRange}";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 一个静态工具类,用于将MVDLite格式的文本数据解析为结构化的C#对象。
|
|||
|
|
/// </summary>
|
|||
|
|
public class MvdLiteAssists
|
|||
|
|
{
|
|||
|
|
private enum ParserState
|
|||
|
|
{
|
|||
|
|
Idle, // 用于解析元数据和别名
|
|||
|
|
InDefinition,
|
|||
|
|
InConstraint
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 不重名属性
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="mvdData"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static HashSet<string> GetDistinctProperties(MvdData mvdData)
|
|||
|
|
{
|
|||
|
|
HashSet<string> 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 = $"{cons.PropName},{cons.ParameterType},{cons.ValueRange}";
|
|||
|
|
//var property = new AfcaArchiProperty
|
|||
|
|
//{
|
|||
|
|
// DefinitionName = entity.Name,
|
|||
|
|
// Name = cons.PropName,
|
|||
|
|
// ValueConstraints = cons.Values,
|
|||
|
|
// IsChecked = false
|
|||
|
|
//};
|
|||
|
|
properties.Add(property);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return properties;
|
|||
|
|
}
|
|||
|
|
/// <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.Properties[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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static List<EntityDefinition> Lookup(MvdData mvdData, string entityName)
|
|||
|
|
{
|
|||
|
|
// 查找所有匹配的实体定义
|
|||
|
|
return mvdData.EntityDefinitions
|
|||
|
|
.Where(e => e.Name.Equals(entityName, StringComparison.OrdinalIgnoreCase))
|
|||
|
|
.ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 查找所有在其类型标签列表中包含指定名称的实体定义。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="mvdData">要搜索的数据源。</param>
|
|||
|
|
/// <param name="labelToFind">要查找的类型标签名称。</param>
|
|||
|
|
/// <param name="comparisonType">一个枚举值,指定比较字符串时是否区分大小写。默认为区分大小写。</param>
|
|||
|
|
/// <returns>一个包含所有匹配的实体定义的列表。</returns>
|
|||
|
|
public static List<EntityDefinition> FindByTypeLabel(
|
|||
|
|
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)))];
|
|||
|
|
}
|
|||
|
|
}
|