public void ParseAssociation(Association asc, Dictionary<string, Package> derivationSuppliers) { // There are two scenarios // A: Two traversable connections // B: A traversable and non traversable end // Case A: Two traversable connections, this means that the connection appears in both classes if (asc.Ends[0] is AssociationEnd && asc.Ends[1] is AssociationEnd) { // Make processing a little easier AssociationEnd[] ends = new AssociationEnd[] { asc.Ends[0] as AssociationEnd, asc.Ends[1] as AssociationEnd }; // Loop so we write the code once for (int i = 0; i < 2; i++) { Property p = new Property(); p.Name = ends[i].Name; // Business Name foreach(BusinessName bn in ends[i].BusinessName ?? new List<BusinessName>()) if(bn.Language == MifCompiler.Language || bn.Language == null) p.BusinessName = bn.Name; // The multiplicity of the opposing end influences the property on this end of the // association p.MinOccurs = ends[1 - i].MinimumMultiplicity; p.MaxOccurs = ends[1 - i].MaximumMultiplicity; p.Conformance = ends[1 - i].IsMandatory ? ClassContent.ConformanceKind.Mandatory : ends[1 - i].Conformance == ConformanceKind.Required ? ClassContent.ConformanceKind.Required : ends[1 - i].MinimumMultiplicity == "1" ? ClassContent.ConformanceKind.Populated : ClassContent.ConformanceKind.Optional; // The type of this end is the type of the association p.Type = CreateTypeReference(ends[i], p); if (p.Type.Name == null) { if (p.Documentation == null) p.Documentation = new MohawkCollege.EHR.gpmr.COR.Documentation(); if (p.Documentation.Description == null) p.Documentation.Description = new List<string>(); p.Documentation.Description.Add(String.Format("GPMR: Association to type '{0}' was ignored and set as nothing as '{0}' could not be found. You should consider revising this", ends[i].ParticipantClassName)); } // Traversable association p.PropertyType = Property.PropertyTypes.TraversableAssociation; // Annotations if (asc.Annotations != null) p.Documentation = MohawkCollege.EHR.gpmr.Pipeline.Compiler.Mif20.Parsers.DocumentationParser.Parse(asc.Annotations.Documentation); // Find the class this end belongs in if (!ClassRepository.ContainsKey(string.Format("{0}.{1}", staticModel.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ends[1 - i].ParticipantClassName))) throw new Exception(string.Format("Can't bind property '{0}' to class '{1}'... Class does not exist", p.Name, ends[1 - i].ParticipantClassName)); // Set derivation p.DerivedFrom = asc; p.SortKey = asc.SortKey; try { if (ends[i].DerivedFrom != null) { p.Realization = new List<ClassContent>(); foreach (var dei in ends[i].DerivedFrom) { MohawkCollege.EHR.gpmr.COR.Feature f = null; if (!ClassRepository.TryGetValue(string.Format("{0}.{1}", derivationSuppliers[dei.StaticModelDerivationId].PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : derivationSuppliers[dei.StaticModelDerivationId].PackageLocation.ToString(MifCompiler.NAME_FORMAT), dei.ClassName), out f)) System.Diagnostics.Trace.WriteLine(String.Format("Can't find derivation class '{0}' for association '{1}'", dei.ClassName, ends[i].Name), "debug"); else { ClassContent cc = (f as MohawkCollege.EHR.gpmr.COR.Class).GetFullContent().Find(o => o.Name == dei.AssociationEndName); p.Realization.Add(cc); } } } } catch (Exception ex) { Trace.WriteLine(String.Format("Cannot append derivation information to {0} (reason:{1})", ends[i].Name, ex.ToString()), "error"); } // Add to repository if(ends[1 - i].Conformance != ConformanceKind.NotPermitted) (ClassRepository[string.Format("{0}.{1}", staticModel.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ends[1 - i].ParticipantClassName)] as MohawkCollege.EHR.gpmr.COR.Class).AddContent(p); } } else // Case 2: A traversable and non-traversable connection, { AssociationEnd ae = (asc.Ends[0] as AssociationEnd) ?? (asc.Ends[1] as AssociationEnd); NonTraversableAssociationEnd ntae = (asc.Ends[0] as NonTraversableAssociationEnd) ?? (asc.Ends[1] as NonTraversableAssociationEnd); // Start to process the property. ClassContent cc; if (ae.ChoiceItem != null && ae.ChoiceItem.Count > 0) cc = new Choice(); else cc = new Property(); cc.Name = ae.Name; // Business Name foreach(BusinessName bn in ae.BusinessName ?? new List<BusinessName>()) if(bn.Language == MifCompiler.Language || bn.Language == null) cc.BusinessName = bn.Name; // The multiplicity of the opposing end influences the property on this end of the // association cc.MinOccurs = ae.MinimumMultiplicity; cc.MaxOccurs = ae.MaximumMultiplicity; cc.Conformance = ae.IsMandatory ? ClassContent.ConformanceKind.Mandatory : ae.Conformance == ConformanceKind.Required && ae.MinimumMultiplicity == "1" ? ClassContent.ConformanceKind.Populated : ae.Conformance == ConformanceKind.Required ? ClassContent.ConformanceKind.Required : ClassContent.ConformanceKind.Optional; #region Bind To Class // Find the class on the traversable end, we'll need to append the property to it if (!ClassRepository.ContainsKey(string.Format("{0}.{1}", staticModel.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ntae.ParticipantClassName))) System.Diagnostics.Trace.WriteLine(string.Format("Can't bind property '{0}' to class '{1}'... Class does not exist", cc.Name, ntae.ParticipantClassName), "error"); //throw new Exception(string.Format("Can't bind property '{0}' to class '{1}'... Class does not exist", p.Name, ae.ParticipantClassName)); else if (ae.Conformance != ConformanceKind.NotPermitted) // Append the property to the class { MohawkCollege.EHR.gpmr.COR.Class cls = ClassRepository[string.Format("{0}.{1}", staticModel.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ntae.ParticipantClassName)] as MohawkCollege.EHR.gpmr.COR.Class; cls.AddContent(cc); // Add template parameter if (templateParameters.ContainsKey(ae.ParticipantClassName)) cls.AddTypeParameter(templateParameters[ae.ParticipantClassName]); } else return; #endregion // Choice or property? if (cc is Property) { Property p = cc as Property; p.Type = CreateTypeReference(ae, p); if (p.Type.Name == null) { if (p.Documentation == null) p.Documentation = new MohawkCollege.EHR.gpmr.COR.Documentation(); if (p.Documentation.Description == null) p.Documentation.Description = new List<string>(); p.Documentation.Description.Add(String.Format("GPMR: Association to type '{0}' was ignored and set as nothing as '{0}' could not be found. You should consider revising this", ae.ParticipantClassName)); } // Traversable association p.PropertyType = Property.PropertyTypes.TraversableAssociation; } else // Choice { Choice chc = cc as Choice; chc.MemberOf = ClassRepository; chc.Content = new List<ClassContent>(); // Get a type reference to the CMET or main type to be used TypeReference trf = CreateTypeReference(ae, cc); // Cannot find association if (trf.Name == null) { if (chc.Documentation == null) chc.Documentation = new MohawkCollege.EHR.gpmr.COR.Documentation(); if (chc.Documentation.Description == null) chc.Documentation.Description = new List<string>(); chc.Documentation.Description.Add(String.Format("GPMR: Association to type '{0}' was ignored and set as nothing as '{0}' could not be found. You should consider revising this", ae.ParticipantClassName)); } // Warn if (trf.Class == null) { throw new InvalidOperationException(String.Format("Cannot make association to class '{0}' as it was not defined!", ae.ParticipantClassName)); } if (ae.ChoiceItem.Count != trf.Class.SpecializedBy.Count) System.Diagnostics.Trace.WriteLine(string.Format("Number of choices on property does not match the number of child classes for its data type for association '{0}'", cc.Name), "warn"); chc.Content.AddRange(ProcessAssociations(ae, ae.ChoiceItem, trf, chc)); /* // Specializations List<TypeReference> specializations = new List<TypeReference>(trf.Class.SpecializedBy); // Flatten choice members for (int i = 0; i < ae.ChoiceItem.Count ; i++) if (ae.ChoiceItem[i].Specialization != null && ae.ChoiceItem[i].Specialization.Count > 0) { AssociationEndSpecialization aes = ae.ChoiceItem[i]; // Get local reference // Remove redundant data ae.ChoiceItem.RemoveAt(i); // Remove the redundant choice item i--; // redo this item // is this a cmet? if (ClassRepository.ContainsKey(aes.ClassName) && ClassRepository[aes.ClassName] is CommonTypeReference) { specializations.RemoveAll(o => o.Name == (ClassRepository[aes.ClassName] as CommonTypeReference).Class.Name); specializations.AddRange((ClassRepository[aes.ClassName] as CommonTypeReference).Class.Class.SpecializedBy); } ae.ChoiceItem.AddRange(aes.Specialization); // Now add the choices } int ip = 0; // Add choice members foreach (AssociationEndSpecialization aes in ae.ChoiceItem) { Property p = new Property(); // Now, construct the properties from the CMET entries // Try ... var rClassName = specializations.Find(o => o.Class != null && o.Class.Name.Equals(aes.ClassName)); // Determine if this is a CMET if (ClassRepository.ContainsKey(aes.ClassName) && ClassRepository[aes.ClassName] is CommonTypeReference) p.Type = (ClassRepository[aes.ClassName] as CommonTypeReference).Class; else if (rClassName != null) // Try using the inheritence method p.Type = rClassName; else if (ClassRepository.ContainsKey(String.Format("{0}.{1}", staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), aes.ClassName))) p.Type = ((ClassRepository[String.Format("{0}.{1}", staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), aes.ClassName)] as MohawkCollege.EHR.gpmr.COR.Class).CreateTypeReference()); else { System.Diagnostics.Trace.WriteLine(string.Format("Class '{2}' of CMET '{1}' could not be found or was not processed. The processing of the property '{0}' in '{3}.{4}' will NOT continue", cc.Name, aes.ClassName, ae.ParticipantClassName, staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ntae.ParticipantClassName), "error"); break; } p.PropertyType = Property.PropertyTypes.TraversableAssociation; // Fix bug with optional choice p.MinOccurs = chc.MinOccurs; p.MaxOccurs = chc.MaxOccurs; p.Conformance = chc.Conformance; p.DerivedFrom = aes; p.Documentation = p.Type.ClassDocumentation; p.MemberOf = ClassRepository; p.Container = chc; // Traversal names p.Name = aes.TraversalName; chc.Content.Add(p); ip++; } */ } // Annotations if (asc.Annotations != null) cc.Documentation = MohawkCollege.EHR.gpmr.Pipeline.Compiler.Mif20.Parsers.DocumentationParser.Parse(asc.Annotations.Documentation); // Set derivation cc.DerivedFrom = asc; cc.SortKey = ae.SortKey; try { if (ae.DerivedFrom != null) { cc.Realization = new List<ClassContent>(); foreach (var dei in ae.DerivedFrom) { MohawkCollege.EHR.gpmr.COR.Feature ss = null; Package derivationPkg = null; if (!derivationSuppliers.TryGetValue(dei.StaticModelDerivationId, out derivationPkg) || derivationPkg == null) { continue; } // Has the package been compiled? if (!ClassRepository.TryGetValue(string.Format("{0}", derivationPkg.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : derivationPkg.PackageLocation.ToString(MifCompiler.NAME_FORMAT)), out ss)) { // Attempt to parse PackageParser.Parse(derivationPkg.PackageLocation.ToString(MifCompiler.NAME_FORMAT), derivationPkg.MemberOfRepository, ClassRepository); // Ditch if still can't find if (!ClassRepository.TryGetValue(string.Format("{0}", derivationPkg.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : derivationPkg.PackageLocation.ToString(MifCompiler.NAME_FORMAT)), out ss)) System.Diagnostics.Trace.WriteLine(String.Format("Can't find derivation class '{0}' for association '{1}' (derivation supplier {2})", dei.ClassName, dei.AssociationEndName, dei.StaticModelDerivationId), "warn"); } // Feature was found var f = (ss as MohawkCollege.EHR.gpmr.COR.SubSystem).FindClass(dei.ClassName); if (f != null) { // Realized Class content ClassContent rcc = f.GetFullContent().Find(o => o.Name == dei.AssociationEndName); cc.Realization.Add(rcc); } else System.Diagnostics.Trace.WriteLine(String.Format("Can't find derivation class '{0}' for association '{1}' (derivation supplier {2})", dei.ClassName, dei.AssociationEndName, dei.StaticModelDerivationId), "warn"); } } } catch (Exception ex) { Trace.WriteLine(String.Format("Cannot append derivation information to {0} (reason:{1})", ae.Name, ex.ToString()), "error"); } (cc.Container as MohawkCollege.EHR.gpmr.COR.Class).Content.Sort(new ClassContent.Comparator()); } }
/// <summary> /// Process Associations /// </summary> private List<ClassContent> ProcessAssociations(AssociationEnd assoc, List<AssociationEndSpecialization> choiceItems, TypeReference typeReference, Choice container) { // Return value List<ClassContent> retVal = new List<ClassContent>(); int nInher = 0; // Process the choice items foreach (var choice in choiceItems) { // Attempt to find a class var candidateType = typeReference.Class.SpecializedBy.FindAll(o => o.Class.Name.Equals(choice.ClassName)); Property p = new Property(); // Did the candidate type find? // Found exactly one candidate type with name if (candidateType.Count == 1) p.Type = candidateType[0]; // Sometimes we find more than one class with the same name that are specializations // in this case we need to correlate the numeric value else if (typeReference.Class.SpecializedBy.Count > nInher && typeReference.Class.SpecializedBy[nInher].Class.Name == choice.ClassName) p.Type = typeReference.Class.SpecializedBy[nInher]; // Otherwise we are referencing a CMET in which case we need to find it else if (ClassRepository.ContainsKey(choice.ClassName) && ClassRepository[choice.ClassName] is CommonTypeReference) // The candidate type is a CMET p.Type = (ClassRepository[choice.ClassName] as CommonTypeReference).Class; // Last ditch attempt is to look for a local class with the same name else if (ClassRepository.ContainsKey(String.Format("{0}.{1}", staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), choice.ClassName))) // Local class reference p.Type = ((ClassRepository[String.Format("{0}.{1}", staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), choice.ClassName)] as MohawkCollege.EHR.gpmr.COR.Class).CreateTypeReference()); else { System.Diagnostics.Trace.WriteLine(string.Format("Association class '{0}' in traversal '{1}' is not a sub-class of '{2}'. The association will NOT be processed", choice.ClassName, assoc.Name, typeReference), "error"); break; } nInher++; p.PropertyType = Property.PropertyTypes.TraversableAssociation; // Fix bug with optional choice p.MinOccurs = container.MinOccurs; p.MaxOccurs = container.MaxOccurs; p.Conformance = container.Conformance; p.DerivedFrom = choice; p.Documentation = p.Type.ClassDocumentation; p.MemberOf = ClassRepository; p.Container = container; // Traversal names p.Name = choice.TraversalName; retVal.Add(p); // Specializations if (choice.Specialization != null && choice.Specialization.Count > 0) { retVal.AddRange(ProcessAssociations(assoc, choice.Specialization, p.Type, container)); } } return retVal; }
private TypeReference CreateTypeReference(AssociationEnd ae, ClassContent cc) { // Resolve CMET ClassElement cel = staticModel.OwnedClass.Find(o => (o.Choice is CommonModelElementRef) && (o.Choice as CommonModelElementRef).Name == ae.ParticipantClassName); if (cel != null) // It is a CMET - Note: This is where late binding may occur.. ae.ParticipantClassName = (cel.Choice as CommonModelElementRef).CmetName ?? (cel.Choice as CommonModelElementRef).Name; // Resolve to CMET // The type of this end is the type of the association TypeReference tr = new TypeReference(); tr.Container = cc; tr.MemberOf = ClassRepository; #region Type Reference // IS the traversable association referencing a CMET type? // if it is, then we require some extra processing to de-reference the CMET if (ClassRepository.ContainsKey(ae.ParticipantClassName) && ClassRepository[ae.ParticipantClassName] is CommonTypeReference) { // Get the CMET references (which is a CTR in COR) CommonTypeReference ctr = (ClassRepository[ae.ParticipantClassName] as CommonTypeReference); tr = ctr.Class; // Assign the class type reference tr.MemberOf = ClassRepository; // Process this class? if (!ClassRepository.ContainsKey(tr.Name)) PackageParser.ParseClassFromPackage(tr.Name, repository, ClassRepository); if (cc is Property) { (cc as Property).FixedValue = ctr.ClassifierCode; // Assign a fixed classifier code (cc as Property).Documentation = ctr.Documentation; // Assign the documentation } } else if (cel != null) // Bad CMET ref { // JF - Bug processing payload models //// the --ignore-cmet flag if (MifCompiler.hostContext.Mode == Pipeline.OperationModeType.Quirks) { System.Diagnostics.Trace.WriteLine(string.Format("can't make type reference to CMET '{0}' as it wasn't found in the classes. The user has specified the --quirks flag so this error won't be classified as fatal...", (cel.Choice as CommonModelElementRef).CmetName), "quirks"); tr.Name = null; } else throw new InvalidOperationException(string.Format("can't make type reference to CMET '{0}' as it wasn't found anywhere in the repository ({1}).", (cel.Choice as CommonModelElementRef).CmetName, staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT))); } else { if (templateParameters.ContainsKey(ae.ParticipantClassName)) tr.Name = ae.ParticipantClassName; else tr.Name = string.Format("{0}.{1}", staticModel.PackageLocation.Artifact == ArtifactKind.RIM ? "RIM" : staticModel.PackageLocation.ToString(MifCompiler.NAME_FORMAT), ae.ParticipantClassName); } #endregion return tr; }