/// <summary> /// Creates a documentation schema from a VEX schema /// </summary> /// <param name="schemata">The VEX schema to import</param> /// <param name="project">The documentation project where the imported schema is to be created</param> /// <returns>The imported documentation schema</returns> internal static DocSchema ImportVex(SCHEMATA schemata, DocProject docProject, bool updateDescriptions) { DocSchema docSchema = docProject.RegisterSchema(schemata.name); if (updateDescriptions && schemata.comment != null && schemata.comment.text != null) { docSchema.Documentation = schemata.comment.text.text; } docSchema.DiagramPagesHorz = schemata.settings.page.nhorizontalpages; docSchema.DiagramPagesVert = schemata.settings.page.nverticalpages; // remember current types for deletion if they no longer exist List<DocObject> existing = new List<DocObject>(); foreach (DocType doctype in docSchema.Types) { existing.Add(doctype); } foreach (DocEntity docentity in docSchema.Entities) { existing.Add(docentity); } foreach (DocFunction docfunction in docSchema.Functions) { existing.Add(docfunction); } foreach (DocGlobalRule docrule in docSchema.GlobalRules) { existing.Add(docrule); } docSchema.PageTargets.Clear(); docSchema.SchemaRefs.Clear(); docSchema.Comments.Clear(); // remember references for fixing up attributes afterwords Dictionary<object, DocDefinition> mapRefs = new Dictionary<object, DocDefinition>(); Dictionary<ATTRIBUTE_DEF, DocAttribute> mapAtts = new Dictionary<ATTRIBUTE_DEF, DocAttribute>(); //Dictionary<SELECT_DEF, DocSelectItem> mapSels = new Dictionary<SELECT_DEF, DocSelectItem>(); Dictionary<SELECT_DEF, DocLine> mapSL = new Dictionary<SELECT_DEF, DocLine>(); Dictionary<SUBTYPE_DEF, DocLine> mapSubs = new Dictionary<SUBTYPE_DEF, DocLine>(); Dictionary<PAGE_REF, DocPageTarget> mapPage = new Dictionary<PAGE_REF, DocPageTarget>(); // entities and types foreach (object obj in schemata.objects) { if (obj is ENTITIES) { ENTITIES ent = (ENTITIES)obj; // filter out orphaned entities having upper flags set if ((ent.flag & 0xFFFF0000) == 0 && (ent.interfaceto == null || ent.interfaceto.theschema == null)) { // create if doesn't exist string name = ent.name.text; string super = null; if (ent.supertypes.Count > 0 && ent.supertypes[0].the_supertype is ENTITIES) { ENTITIES superent = (ENTITIES)ent.supertypes[0].the_supertype; super = superent.name.text; } DocEntity docEntity = docSchema.RegisterEntity(name); if (existing.Contains(docEntity)) { existing.Remove(docEntity); } mapRefs.Add(obj, docEntity); // clear out existing if merging docEntity.BaseDefinition = null; foreach(DocSubtype docSub in docEntity.Subtypes) { docSub.Delete(); } docEntity.Subtypes.Clear(); foreach(DocUniqueRule docUniq in docEntity.UniqueRules) { docUniq.Delete(); } docEntity.UniqueRules.Clear(); foreach(DocLine docLine in docEntity.Tree) { docLine.Delete(); } docEntity.Tree.Clear(); if (updateDescriptions && ent.comment != null && ent.comment.text != null) { docEntity.Documentation = ent.comment.text.text; } if (ent.supertypes.Count > 0 && ent.supertypes[0].the_supertype is ENTITIES) { docEntity.BaseDefinition = ((ENTITIES)ent.supertypes[0].the_supertype).name.text; } docEntity.EntityFlags = ent.flag; if (ent.subtypes != null) { foreach (SUBTYPE_DEF sd in ent.subtypes) { // new (3.8): intermediate subtypes for diagrams DocLine docLine = new DocLine(); ImportVexLine(sd.layout, null, docLine.DiagramLine, null); docEntity.Tree.Add(docLine); OBJECT od = (OBJECT)sd.the_subtype; // tunnel through page ref if (od is PAGE_REF_TO) { od = ((PAGE_REF_TO)od).pageref; } if (od is TREE) { TREE tree = (TREE)od; foreach (OBJECT o in tree.list) { OBJECT os = o; OBJECT_LINE_LAYOUT linelayout = null; if (o is SUBTYPE_DEF) { SUBTYPE_DEF osd = (SUBTYPE_DEF)o; linelayout = osd.layout; os = ((SUBTYPE_DEF)o).the_subtype; } if (os is PAGE_REF_TO) { os = ((PAGE_REF_TO)os).pageref; } if (os is ENTITIES) { DocSubtype docSub = new DocSubtype(); docSub.DefinedType = ((ENTITIES)os).name.text; docEntity.Subtypes.Add(docSub); DocLine docSubline = new DocLine(); docLine.Tree.Add(docSubline); if (o is SUBTYPE_DEF) { mapSubs.Add((SUBTYPE_DEF)o, docSubline); } ImportVexLine(linelayout, null, docSubline.DiagramLine, null); } else { Debug.Assert(false); } } } else if (od is ENTITIES) { DocSubtype docInt = new DocSubtype(); docEntity.Subtypes.Add(docInt); docInt.DefinedType = ((ENTITIES)od).name.text; } else { Debug.Assert(false); } } } // determine EXPRESS-G page based on placement (required for generating hyperlinks) if (ent.layout != null) { ImportVexRectangle(docEntity, ent.layout.rectangle, schemata); } if (ent.attributes != null) { List<DocAttribute> existingattr = new List<DocAttribute>(); foreach (DocAttribute docAttr in docEntity.Attributes) { existingattr.Add(docAttr); } // attributes are replaced, not merged (template don't apply here) foreach (ATTRIBUTE_DEF attr in ent.attributes) { if (attr.name != null) { DocAttribute docAttr = docEntity.RegisterAttribute(attr.name.text); mapAtts.Add(attr, docAttr); if (existingattr.Contains(docAttr)) { existingattr.Remove(docAttr); } if (updateDescriptions && attr.comment != null && attr.comment.text != null) { docAttr.Documentation = attr.comment.text.text; } if (docAttr.DiagramLabel != null) { docAttr.DiagramLabel.Delete(); docAttr.DiagramLabel = null; } foreach(DocPoint docPoint in docAttr.DiagramLine) { docPoint.Delete(); } docAttr.DiagramLine.Clear(); if (attr.layout != null) { if (attr.layout.pline != null) { // intermediate lines if (attr.layout.pline.rpoint != null) { docAttr.DiagramLabel = new DocRectangle(); ImportVexLine(attr.layout, attr.name.layout, docAttr.DiagramLine, docAttr.DiagramLabel); } } } OBJECT def = attr.the_attribute; if (attr.the_attribute is PAGE_REF_TO) { PAGE_REF_TO pr = (PAGE_REF_TO)attr.the_attribute; def = pr.pageref; } if (def is DEFINED_TYPE) { DEFINED_TYPE dt = (DEFINED_TYPE)def; docAttr.DefinedType = dt.name.text; } else if (def is ENTITIES) { ENTITIES en = (ENTITIES)def; docAttr.DefinedType = en.name.text; } else if (def is ENUMERATIONS) { ENUMERATIONS en = (ENUMERATIONS)def; docAttr.DefinedType = en.name.text; } else if (def is SELECTS) { SELECTS en = (SELECTS)def; docAttr.DefinedType = en.name.text; } else if (def is PRIMITIVE_TYPE) { PRIMITIVE_TYPE en = (PRIMITIVE_TYPE)def; string length = ""; if (en.constraints > 0) { length = " (" + en.constraints.ToString() + ")"; } else if (en.constraints < 0) { int len = -en.constraints; length = " (" + len.ToString() + ") FIXED"; } docAttr.DefinedType = en.name.text + length; } else if (def is SCHEMA_REF) { SCHEMA_REF en = (SCHEMA_REF)def; docAttr.DefinedType = en.name.text; } else { Debug.Assert(false); } docAttr.AttributeFlags = attr.attributeflag; AGGREGATES vexAggregates = attr.aggregates; DocAttribute docAggregate = docAttr; while (vexAggregates != null) { // traverse nested aggregation (e.g. IfcStructuralLoadConfiguration) docAggregate.AggregationType = vexAggregates.aggrtype + 1; docAggregate.AggregationLower = vexAggregates.lower; docAggregate.AggregationUpper = vexAggregates.upper; docAggregate.AggregationFlag = vexAggregates.flag; vexAggregates = vexAggregates.next; if (vexAggregates != null) { // inner array (e.g. IfcStructuralLoadConfiguration) docAggregate.AggregationAttribute = new DocAttribute(); docAggregate = docAggregate.AggregationAttribute; } } docAttr.Derived = attr.is_derived; if (attr.user_redeclaration != null) { docAttr.Inverse = attr.user_redeclaration; } else if (attr.is_inverse is ATTRIBUTE_DEF) { ATTRIBUTE_DEF adef = (ATTRIBUTE_DEF)attr.is_inverse; docAttr.Inverse = adef.name.text; } else if (attr.is_inverse != null) { Debug.Assert(false); } } } foreach(DocAttribute docAttr in existingattr) { docEntity.Attributes.Remove(docAttr); docAttr.Delete(); } } // unique rules if (ent.uniquenes != null) { // rules are replaced, not merged (template don't apply here) //docEntity.UniqueRules = new List<DocUniqueRule>(); foreach (UNIQUE_RULE rule in ent.uniquenes) { DocUniqueRule docRule = new DocUniqueRule(); docEntity.UniqueRules.Add(docRule); docRule.Name = rule.name; docRule.Items = new List<DocUniqueRuleItem>(); foreach (ATTRIBUTE_DEF ruleitem in rule.for_attribute) { DocUniqueRuleItem item = new DocUniqueRuleItem(); item.Name = ruleitem.name.text; docRule.Items.Add(item); } } } // where rules if (ent.wheres != null) { List<DocWhereRule> existingattr = new List<DocWhereRule>(); foreach (DocWhereRule docWhere in docEntity.WhereRules) { existingattr.Add(docWhere); } foreach (WHERE_RULE where in ent.wheres) { DocWhereRule docWhere = docEntity.RegisterWhereRule(where.name); docWhere.Expression = where.rule_context; if(existingattr.Contains(docWhere)) { existingattr.Remove(docWhere); } if (updateDescriptions && where.comment != null && where.comment.text != null) { docWhere.Documentation = where.comment.text.text; } } foreach(DocWhereRule exist in existingattr) { exist.Delete(); docEntity.WhereRules.Remove(exist); } } } } else if (obj is ENUMERATIONS) { ENUMERATIONS ent = (ENUMERATIONS)obj; if (ent.interfaceto == null || ent.interfaceto.theschema == null) { if (schemata.name.Equals("IfcConstructionMgmtDomain", StringComparison.OrdinalIgnoreCase) && ent.name.text.Equals("IfcNullStyle", StringComparison.OrdinalIgnoreCase)) { // hack to workaround vex bug Debug.Assert(true); } else { DocEnumeration docEnumeration = docSchema.RegisterType<DocEnumeration>(ent.name.text); if (existing.Contains(docEnumeration)) { existing.Remove(docEnumeration); } mapRefs.Add(obj, docEnumeration); if (updateDescriptions && ent.comment != null && ent.comment.text != null) { docEnumeration.Documentation = ent.comment.text.text; } // determine EXPRESS-G page based on placement (required for generating hyperlinks) if (ent.typelayout != null && schemata.settings != null && schemata.settings.page != null) { ImportVexRectangle(docEnumeration, ent.typelayout.rectangle, schemata); } // enumeration values are replaced, not merged (template don't apply here) docEnumeration.Constants.Clear(); foreach (string s in ent.enums) { DocConstant docConstant = new DocConstant(); docEnumeration.Constants.Add(docConstant); docConstant.Name = s; } } } } else if (obj is DEFINED_TYPE) { DEFINED_TYPE ent = (DEFINED_TYPE)obj; if (ent.interfaceto == null || ent.interfaceto.theschema == null) { DocDefined docDefined = docSchema.RegisterType<DocDefined>(ent.name.text); if (existing.Contains(docDefined)) { existing.Remove(docDefined); } mapRefs.Add(obj, docDefined); if (updateDescriptions && ent.comment != null && ent.comment.text != null) { docDefined.Documentation = ent.comment.text.text; } if (ent.layout != null) { ImportVexRectangle(docDefined, ent.layout.rectangle, schemata); } if(ent.defined.object_line_layout != null) { foreach(DocPoint docPoint in docDefined.DiagramLine) { docPoint.Delete(); } docDefined.DiagramLine.Clear(); ImportVexLine(ent.defined.object_line_layout, null, docDefined.DiagramLine, null); } OBJECT os = (OBJECT)ent.defined.defined; if (os is PAGE_REF_TO) { os = ((PAGE_REF_TO)os).pageref; } if (os is PRIMITIVE_TYPE) { PRIMITIVE_TYPE pt = (PRIMITIVE_TYPE)os; docDefined.DefinedType = pt.name.text; if (pt.constraints != 0) { docDefined.Length = pt.constraints; } } else if (os is DEFINED_TYPE) { DEFINED_TYPE dt = (DEFINED_TYPE)os; docDefined.DefinedType = dt.name.text; } else if (os is ENTITIES) { ENTITIES et = (ENTITIES)os; docDefined.DefinedType = et.name.text; } else { Debug.Assert(false); } // aggregation AGGREGATES vexAggregates = ent.defined.aggregates; if (vexAggregates != null) { DocAttribute docAggregate = new DocAttribute(); docDefined.Aggregation = docAggregate; docAggregate.AggregationType = vexAggregates.aggrtype + 1; docAggregate.AggregationLower = vexAggregates.lower; docAggregate.AggregationUpper = vexAggregates.upper; docAggregate.AggregationFlag = vexAggregates.flag; } // where rules if (ent.whererules != null) { // rules are replaced, not merged (template don't apply here) foreach(DocWhereRule docWhere in docDefined.WhereRules) { docWhere.Delete(); } docDefined.WhereRules.Clear(); foreach (WHERE_RULE where in ent.whererules) { DocWhereRule docWhere = new DocWhereRule(); docDefined.WhereRules.Add(docWhere); docWhere.Name = where.name; docWhere.Expression = where.rule_context; if (where.comment != null && where.comment.text != null) { docWhere.Documentation = where.comment.text.text; } } } } } else if (obj is SELECTS) { SELECTS ent = (SELECTS)obj; if (ent.interfaceto == null || ent.interfaceto.theschema == null) { DocSelect docSelect = docSchema.RegisterType<DocSelect>(ent.name.text); if (existing.Contains(docSelect)) { existing.Remove(docSelect); } mapRefs.Add(obj, docSelect); if (updateDescriptions && ent.comment != null && ent.comment.text != null) { docSelect.Documentation = ent.comment.text.text; } // determine EXPRESS-G page based on placement (required for generating hyperlinks) if (ent.typelayout != null) { ImportVexRectangle(docSelect, ent.typelayout.rectangle, schemata); } docSelect.Selects.Clear(); docSelect.Tree.Clear(); foreach (SELECT_DEF sdef in ent.selects) { DocLine docLine = new DocLine(); docSelect.Tree.Add(docLine); ImportVexLine(sdef.layout, null, docLine.DiagramLine, null); mapSL.Add(sdef, docLine); if (sdef.def is TREE) { TREE tree = (TREE)sdef.def; foreach (OBJECT o in tree.list) { DocSelectItem dsi = new DocSelectItem(); docSelect.Selects.Add(dsi); OBJECT os = o; if (o is SELECT_DEF) { SELECT_DEF selectdef = (SELECT_DEF)o; DocLine docLineSub = new DocLine(); docLine.Tree.Add(docLineSub); ImportVexLine(selectdef.layout, null, docLineSub.DiagramLine, null); mapSL.Add(selectdef, docLineSub); os = ((SELECT_DEF)o).def; } else { Debug.Assert(false); } if (os is PAGE_REF_TO) { PAGE_REF_TO pr = (PAGE_REF_TO)os; os = pr.pageref; } if (os is DEFINITION) { dsi.Name = ((DEFINITION)os).name.text; } } } else { OBJECT os = (OBJECT)sdef.def; if (os is PAGE_REF_TO) { PAGE_REF_TO pr = (PAGE_REF_TO)os; os = pr.pageref; } DocSelectItem dsi = new DocSelectItem(); docSelect.Selects.Add(dsi); if (os is DEFINITION) { dsi.Name = ((DEFINITION)os).name.text; } } } } } else if (obj is GLOBAL_RULE) { GLOBAL_RULE func = (GLOBAL_RULE)obj; DocGlobalRule docFunction = docSchema.RegisterRule(func.name); if (existing.Contains(docFunction)) { existing.Remove(docFunction); } // clear out existing if merging docFunction.WhereRules.Clear(); if (updateDescriptions && func.comment != null && func.comment.text != null) { docFunction.Documentation = func.comment.text.text; } docFunction.Expression = func.rule_context; foreach (WHERE_RULE wr in func.where_rule) { DocWhereRule docW = new DocWhereRule(); docW.Name = wr.name; docW.Expression = wr.rule_context; if (wr.comment != null) { docW.Documentation = wr.comment.text.text; } docFunction.WhereRules.Add(docW); } if (func.for_entities.Count == 1) { docFunction.ApplicableEntity = func.for_entities[0].ToString(); } } else if (obj is USER_FUNCTION) { USER_FUNCTION func = (USER_FUNCTION)obj; DocFunction docFunction = docSchema.RegisterFunction(func.name); if (existing.Contains(docFunction)) { existing.Remove(docFunction); } if (updateDescriptions && func.comment != null && func.comment.text != null) { docFunction.Documentation = func.comment.text.text; } docFunction.Expression = func.rule_context; // NOTE: While the VEX schema can represent parameters and return values, Visual Express does not implement it! // Rather, parameter info is also included in the 'rule_context' if (func.return_value != null) { docFunction.ReturnValue = func.return_value.ToString(); } else { docFunction.ReturnValue = null; } docFunction.Parameters.Clear(); if (func.parameter_list != null) { foreach (PARAMETER par in func.parameter_list) { DocParameter docParameter = new DocParameter(); docParameter.Name = par.name; docParameter.DefinedType = par.parameter_type.ToString(); docFunction.Parameters.Add(docParameter); } } } else if (obj is PRIMITIVE_TYPE) { PRIMITIVE_TYPE prim = (PRIMITIVE_TYPE)obj; DocPrimitive docPrimitive = new DocPrimitive(); docPrimitive.Name = prim.name.text; if (prim.layout != null) { ImportVexRectangle(docPrimitive, prim.layout.rectangle, schemata); } docSchema.Primitives.Add(docPrimitive); mapRefs.Add(obj, docPrimitive); } else if (obj is COMMENT) { COMMENT comment = (COMMENT)obj; // only deal with comments that are part of EXPRESS-G layout -- ignore those referenced by definitions and old cruft left behind due to older versions of VisualE that were buggy if (comment.layout != null) { DocComment docComment = new DocComment(); docComment.Documentation = comment.text.text; ImportVexRectangle(docComment, comment.layout.rectangle, schemata); docSchema.Comments.Add(docComment); } } else if (obj is INTERFACE_SCHEMA) { INTERFACE_SCHEMA iface = (INTERFACE_SCHEMA)obj; DocSchemaRef docSchemaRef = new DocSchemaRef(); docSchema.SchemaRefs.Add(docSchemaRef); docSchemaRef.Name = iface.schema_name; foreach (object o in iface.item) { if (o is DEFINITION) { DocDefinitionRef docDefRef = new DocDefinitionRef(); docSchemaRef.Definitions.Add(docDefRef); mapRefs.Add(o, docDefRef); docDefRef.Name = ((DEFINITION)o).name.text; if (o is DEFINED_TYPE) { DEFINED_TYPE dt = (DEFINED_TYPE)o; if (dt.layout != null) { ImportVexRectangle(docDefRef, dt.layout.rectangle, schemata); } } else if (o is ENTITIES) { ENTITIES ents = (ENTITIES)o; if (ents.layout != null) // null for IfcPolyline reference in IfcGeometricModelResource { ImportVexRectangle(docDefRef, ents.layout.rectangle, schemata); } if (ents.subtypes != null) { foreach (SUBTYPE_DEF subdef in ents.subtypes) { OBJECT_LINE_LAYOUT linelayout = subdef.layout; DocLine docSub = new DocLine(); ImportVexLine(subdef.layout, null, docSub.DiagramLine, null); docDefRef.Tree.Add(docSub); if(subdef.the_subtype is TREE) { TREE tree = (TREE)subdef.the_subtype; foreach(object oo in tree.list) { if(oo is SUBTYPE_DEF) { SUBTYPE_DEF subsubdef = (SUBTYPE_DEF)oo; DocLine docSubSub = new DocLine(); docSub.Tree.Add(docSubSub); ImportVexLine(subsubdef.layout, null, docSubSub.DiagramLine, null); mapSubs.Add(subsubdef, docSubSub); } } } } } } else if (o is ENUMERATIONS) { ENUMERATIONS enums = (ENUMERATIONS)o; if (enums.typelayout != null) { ImportVexRectangle(docDefRef, enums.typelayout.rectangle, schemata); } } else if (o is SELECTS) { SELECTS sels = (SELECTS)o; if (sels.typelayout != null) { ImportVexRectangle(docDefRef, sels.typelayout.rectangle, schemata); } } else if(o is SCHEMA_REF) { SCHEMA_REF sref = (SCHEMA_REF)o; if(sref.layout != null) { ImportVexRectangle(docDefRef, sref.layout.rectangle, schemata); } } } else if (o is USER_FUNCTION) { DocDefinitionRef docDefRef = new DocDefinitionRef(); docSchemaRef.Definitions.Add(docDefRef); USER_FUNCTION uf = (USER_FUNCTION)o; docDefRef.Name = uf.name; } } } else if (obj is PAGE_REF) { PAGE_REF pageref = (PAGE_REF)obj; DocPageTarget docPageTarget = new DocPageTarget(); docSchema.PageTargets.Add(docPageTarget); docPageTarget.Name = pageref.text.text; docPageTarget.DiagramNumber = pageref.pagenr; ImportVexLine(pageref.pageline.layout, null, docPageTarget.DiagramLine, null); ImportVexRectangle(docPageTarget, pageref.layout.rectangle, schemata); foreach (PAGE_REF_TO pagerefto in pageref.pagerefto) { DocPageSource docPageSource = new DocPageSource(); docPageTarget.Sources.Add(docPageSource); docPageSource.DiagramNumber = pagerefto.pagenr; docPageSource.Name = pagerefto.text.text; ImportVexRectangle(docPageSource, pagerefto.layout.rectangle, schemata); mapRefs.Add(pagerefto, docPageSource); } mapPage.Add(pageref, docPageTarget); } } foreach (DocObject docobj in existing) { if (docobj is DocEntity) { docSchema.Entities.Remove((DocEntity)docobj); } else if (docobj is DocType) { docSchema.Types.Remove((DocType)docobj); } else if (docobj is DocFunction) { docSchema.Functions.Remove((DocFunction)docobj); } else if (docobj is DocGlobalRule) { docSchema.GlobalRules.Remove((DocGlobalRule)docobj); } docobj.Delete(); } // now fix up attributes foreach (ATTRIBUTE_DEF docAtt in mapAtts.Keys) { DocAttribute docAttr = mapAtts[docAtt]; docAttr.Definition = mapRefs[docAtt.the_attribute]; } foreach (PAGE_REF page in mapPage.Keys) { DocPageTarget docPage = mapPage[page]; docPage.Definition = mapRefs[page.pageline.pageref]; } foreach (SELECT_DEF sd in mapSL.Keys) { DocLine docLine = mapSL[sd]; if (mapRefs.ContainsKey(sd.def)) { docLine.Definition = mapRefs[sd.def]; } } foreach (SUBTYPE_DEF sd in mapSubs.Keys) { DocLine docLine = mapSubs[sd]; if (mapRefs.ContainsKey(sd.the_subtype)) { docLine.Definition = mapRefs[sd.the_subtype]; } } foreach(object o in mapRefs.Keys) { if (o is DEFINED_TYPE) { DEFINED_TYPE def = (DEFINED_TYPE)o; if (def.interfaceto == null || def.interfaceto.theschema == null) { // declared within DocDefined docDef = (DocDefined)mapRefs[o]; docDef.Definition = mapRefs[def.defined.defined]; } } } return docSchema; }