/// <summary> /// Create a pascal cased name /// </summary> private static string CreatePascalCasedName(Choice cc) { // Get the type reference var tr = (cc.Content[0] as Property).Type.Class.BaseClass; List<String> methods = new List<string>(), methodDeclarations = new List<string>(); foreach (Property p in cc.Content) if (p.Type.Class.BaseClass != null && tr != null && p.Type.Class.BaseClass.Name != tr.Name) tr = null; // If no type reference is agreed to, then the type reference is Object! string dt = tr == null ? "System.Object" : CreateDatatypeRef(tr, cc.Content[0] as Property); return Util.Util.PascalCase(cc.Name) == dt ? Util.Util.MakeFriendly(cc.Name) : cc.Container != null && (Util.Util.PascalCase(cc.Name) == cc.Container.Name || Util.Util.PascalCase(cc.Name) == Util.Util.PascalCase(cc.Container.Name)) ? Util.Util.PascalCase(cc.Name) + "Choice" : Util.Util.PascalCase(cc.Name); }
/// <summary> /// Write an inline condition for a when expand /// </summary> private bool WriteInlineChooseWhenExpand(XmlWriter xw, Choice c, List<TypeReference> genericSupplier, string interactionName) { Property rimName = null; if (c.Realization != null && c.Realization.Count > 0) rimName = c.Realization[0] as Property; else { xw.WriteComment(String.Format("TODO: Can't realize property '{0}' in the RIM as no realization data was found", c.Name)); //rimName = p; return false; } List<Property> properties = FlattenPropertyHierarchy(c.Content); // Write the for-each xw.WriteStartElement("for-each", NS_XSLT); string s = String.Format("./*[local-name() = '{0}' {1}]", rimName.Name, GenerateConditionString(rimName, ResolveTypeReference(rimName.Type.Name, genericSupplier), ".", genericSupplier)); xw.WriteAttributeString("select", s); xw.WriteStartElement("choose", NS_XSLT); foreach (var property in properties) { xw.WriteStartElement("when", NS_XSLT); TypeReference tr = property.Type; if (ResolveTypeReference(tr.Name, genericSupplier) != null) tr = ResolveTypeReference(property.Type.Name, genericSupplier); // Find any ambiguous test conditions s = GenerateDeepConditionString(property, tr, new List<string>(), genericSupplier); xw.WriteAttributeString("test", String.Format("{0}", s)); // Create transform if (property.PropertyType == Property.PropertyTypes.Structural) WriteInlineAttributeMapExpand(xw, property); else WriteInlineElementMapExpand(xw, property, tr, genericSupplier, interactionName); xw.WriteEndElement(); // when } xw.WriteEndElement(); // end choose xw.WriteEndElement(); // end for-each return true; }
/// <summary> /// Create choice content /// </summary> private List<XmlSchemaElement> CreateChoice(Choice chc, List<TypeReference> genericParameters, string prefix, List<string> includedModels, List<TypeReference> additionalCompileTargets) { List<XmlSchemaElement> retVal = new List<XmlSchemaElement>(); // Iterate through the child classes foreach (ClassContent cc in chc.Content) { if (cc is Property) retVal.Add(CreateElement(cc as Property, genericParameters, prefix, includedModels, additionalCompileTargets)); else if (cc is Choice) retVal.AddRange(CreateChoice(cc as Choice, genericParameters, prefix, includedModels, additionalCompileTargets)); } return retVal; }
/// <summary> /// Render property attribute /// </summary> private String RenderPropertyAttribute(ClassContent cc, string ownerPackage, int propertySort) { StringWriter retBuilder = new StringWriter(); // Represent as an option Choice options = cc as Choice; if(options == null) options = new Choice() { Content = new List<ClassContent>() { cc as Property } }; // Traversal names already rendered List<string> alreadyRendered = new List<string>(); // Iterate through choices foreach (Property property in options.Content) { // Enumerator for alt-traversals List<Property.AlternateTraversalData> altTraversal = new List<Property.AlternateTraversalData>(); altTraversal.Add(new Property.AlternateTraversalData() { CaseWhen = property.Type, TraversalName = property.Name }); // Alternatives if (property.AlternateTraversalNames != null) foreach (Property.AlternateTraversalData kv in property.AlternateTraversalNames) altTraversal.Add(kv); // Write properties foreach (Property.AlternateTraversalData kv in altTraversal) { // Datatype TypeReference tr = Datatypes.MapDatatype(kv.CaseWhen); string key = string.Format("{0}.{1}.{2}.{3}", ownerPackage, tr.Name, kv.TraversalName, kv.InteractionOwner != null ? kv.InteractionOwner.Name : ""); // Already rendered if (!alreadyRendered.Contains(key)) { retBuilder.Write("\t@Property(name = \"{0}\", conformance = ConformanceType.{1}, propertyType = PropertyType.{2}, sortKey = {3}", kv.TraversalName, cc.Conformance.ToString().ToUpper(), (options.Content[0] as Property).PropertyType.ToString().ToUpper(), propertySort); // Now a type hint if (tr.Class != null && (property.Container is Choice || property.AlternateTraversalNames != null)) retBuilder.Write(", type = {0}.class", CreateDatatypeRef(tr, property, ownerPackage)); // Now for an interaction hint if (tr.Class != null && (property.Container is Choice || property.AlternateTraversalNames != null) && kv.InteractionOwner != null) retBuilder.Write(", interactionOwner = {0}.interaction.{1}.class", ownerPackage, kv.InteractionOwner.Name); // Impose a flavor? if (tr.Flavor != null) retBuilder.Write(", imposeFlavorId = \"{0}\"", tr.Flavor); // Is this a set? if (property.MaxOccurs != "1") retBuilder.Write(", minOccurs = {0}, maxOccurs = {1}", property.MinOccurs, property.MaxOccurs == "*" ? "-1" : property.MaxOccurs); if (property.MinLength != null) retBuilder.Write(", minLength = {0}", property.MinLength); if (property.MaxLength != null) retBuilder.Write(", maxLength = {0}", property.MaxLength); // Is there an update mode //if (property.UpdateMode != null) // retBuilder.Write(", defaultUpdateMode = UpdateMode.{0}", property.UpdateMode); // Is there a supplier domain? if (property.SupplierDomain != null && property.SupplierStrength == MohawkCollege.EHR.gpmr.COR.Property.CodingStrengthKind.CodedNoExtensions) retBuilder.Write(", supplierDomain = \"{0}\"", (property.SupplierDomain.ContentOid)); // Fixed value if (!String.IsNullOrEmpty(property.FixedValue)) retBuilder.Write(", fixedValue = \"{0}\"", property.FixedValue); // generic supplier if (property.Type != null && property.Type.GenericSupplier != null && property.Type.GenericSupplier.Count > 0) { retBuilder.Write(", genericSupplier = {"); foreach (var genSupp in property.Type.GenericSupplier) if((property.Container as Class).TypeParameters == null || !(property.Container as Class).TypeParameters.Exists(o=>o.Name == genSupp.Name)) retBuilder.Write("{0}.class{1}", CreateDatatypeRef(genSupp, property, ownerPackage, false), genSupp == property.Type.GenericSupplier.Last() ? "" : ","); retBuilder.Write("}"); } else if (property.MaxOccurs != "1" && !Datatypes.IsCollectionType(property.Type)) // Array list so we still need to put this in { retBuilder.Write(", genericSupplier = {"); CreateDatatypeRef(property.Type, property, ownerPackage); retBuilder.Write("}"); } retBuilder.WriteLine("),"); alreadyRendered.Add(key); } } } string retVal = retBuilder.ToString(); retVal = retVal.Substring(0, retVal.LastIndexOf(",")); retVal += "\r\n"; if (alreadyRendered.Count > 1) { if (!s_imports.Contains("org.marc.everest.annotations.Properties")) s_imports.Add("org.marc.everest.annotations.Properties"); return String.Format("\t@Properties( value = {{\r\n{0}\t }})\r\n", retVal); } else return retVal; }
/// <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; }
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> /// Query the conformance for a choice /// </summary> private bool QueryConformance(Choice chc, TypeReference typeReference, ClassContent.ConformanceKind p) { bool retVal = true; foreach (ClassContent cc in chc.Content) if (cc is Property) retVal &= ((cc.Conformance & p) == cc.Conformance); else if (cc is Choice) retVal &= QueryConformance(cc as Choice, typeReference, p); return retVal; }