| /******************************************************************************* |
| * Copyright (c) 2009, 2012, 2017 David A Carlson and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * David A Carlson (XMLmodeling.com) - initial API and implementation |
| * John T.E. Timm (IBM Corporation) - added isEntry |
| * Christian W. Damus - discriminate multiple property constraints (artf3185) |
| * - support nested datatype subclasses (artf3350) |
| * Dan Brown (Audacious Inquiry) - modified XML binding messages based on mandatory property |
| * as part of artf3549, artf3577, errata 156 and errata 72 |
| * - changed output from 'data type CD' to '@xsi:type="CD"' as per errata 177 |
| * - added message support for errata 384 as per artf3818 No Information Section Fix |
| * - support templateId extension attribute value in generalization and association messages |
| * Sarp Kaya (NEHTA) |
| * Vadim Peretokin (NEHTA) - added handling of SHOULD + 0..0 multiplicity to produce publication text "SHALL NOT" |
| * Sean Muir (HL7) Added methods to support Acceleo reports |
| * $Id$ |
| *******************************************************************************/ |
| package org.eclipse.mdht.uml.cda.core.util; |
| |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.Enumerator; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.mdht.uml.cda.core.profile.EntryRelationship; |
| import org.eclipse.mdht.uml.cda.core.profile.EntryRelationshipKind; |
| import org.eclipse.mdht.uml.cda.core.profile.Inline; |
| import org.eclipse.mdht.uml.cda.core.profile.LogicalConstraint; |
| import org.eclipse.mdht.uml.cda.core.profile.LogicalOperator; |
| import org.eclipse.mdht.uml.cda.core.profile.SeverityKind; |
| import org.eclipse.mdht.uml.cda.core.profile.Validation; |
| import org.eclipse.mdht.uml.cda.core.profile.ValidationKind; |
| import org.eclipse.mdht.uml.cda.core.profile.ValueSetConstraint; |
| import org.eclipse.mdht.uml.common.util.NamedElementUtil; |
| import org.eclipse.mdht.uml.common.util.PropertyList; |
| import org.eclipse.mdht.uml.common.util.UMLUtil; |
| import org.eclipse.mdht.uml.term.core.profile.BindingKind; |
| import org.eclipse.mdht.uml.term.core.profile.CodeSystemVersion; |
| import org.eclipse.mdht.uml.term.core.profile.ValueSetVersion; |
| import org.eclipse.mdht.uml.term.core.util.ITermProfileConstants; |
| import org.eclipse.mdht.uml.term.core.util.TermProfileUtil; |
| import org.eclipse.uml2.common.util.UML2Util; |
| import org.eclipse.uml2.uml.Association; |
| import org.eclipse.uml2.uml.Class; |
| import org.eclipse.uml2.uml.Classifier; |
| import org.eclipse.uml2.uml.Comment; |
| import org.eclipse.uml2.uml.Constraint; |
| import org.eclipse.uml2.uml.Element; |
| import org.eclipse.uml2.uml.Enumeration; |
| import org.eclipse.uml2.uml.EnumerationLiteral; |
| import org.eclipse.uml2.uml.Generalization; |
| import org.eclipse.uml2.uml.NamedElement; |
| import org.eclipse.uml2.uml.OpaqueExpression; |
| import org.eclipse.uml2.uml.Package; |
| import org.eclipse.uml2.uml.Property; |
| import org.eclipse.uml2.uml.Stereotype; |
| import org.eclipse.uml2.uml.Type; |
| import org.eclipse.uml2.uml.ValueSpecification; |
| import org.eclipse.uml2.uml.util.UMLSwitch; |
| |
| public class CDAModelUtil { |
| |
| public static final String CDA_PACKAGE_NAME = "cda"; |
| |
| public static final String DATATYPES_NS_URI = "http://www.openhealthtools.org/mdht/uml/hl7/datatypes"; |
| |
| /** This base URL may be set from preferences or Ant task options. */ |
| public static String INFOCENTER_URL = "http://www.cdatools.org/infocenter"; |
| |
| public static final String SEVERITY_ERROR = "ERROR"; |
| |
| public static final String SEVERITY_WARNING = "WARNING"; |
| |
| public static final String SEVERITY_INFO = "INFO"; |
| |
| private static final String EPACKAGE = "Ecore::EPackage"; |
| |
| private static final String EREFERENCE = "Ecore::EReference"; |
| |
| public static final String XMLNAMESPACE = "xmlNamespace"; |
| |
| private static final String NSPREFIX = "nsPrefix"; |
| |
| private static final String NSURI = "nsURI"; |
| |
| // This message may change in the future to specify certain nullFlavor Types (such as the implementation, NI) |
| private static final String NULLFLAVOR_SECTION_MESSAGE = "If section/@nullFlavor is not present, "; |
| |
| public static boolean cardinalityAfterElement = false; |
| |
| public static boolean disablePdfGeneration = false; |
| |
| public static boolean isAppendConformanceRules = false; |
| |
| public static Class getCDAClass(Classifier templateClass) { |
| Class cdaClass = null; |
| |
| // if the provided class is from CDA and not a template |
| if (isCDAModel(templateClass) && templateClass instanceof Class) { |
| return (Class) templateClass; |
| } |
| |
| for (Classifier parent : templateClass.allParents()) { |
| // nearest package may be null if CDA model is not available |
| if (parent.getNearestPackage() != null) { |
| if (isCDAModel(parent) && parent instanceof Class) { |
| cdaClass = (Class) parent; |
| break; |
| } |
| } |
| } |
| |
| return cdaClass; |
| } |
| |
| public static Property getCDAProperty(Property templateProperty) { |
| if (templateProperty.getClass_() == null) { |
| return null; |
| } |
| |
| // if the provided property is from a CDA class/datatype and not a template |
| if (isCDAModel(templateProperty) || isDatatypeModel(templateProperty)) { |
| return templateProperty; |
| } |
| |
| for (Classifier parent : templateProperty.getClass_().allParents()) { |
| for (Property inherited : parent.getAttributes()) { |
| Property cdaProperty = transformToCDAProperty(templateProperty); |
| if (cdaProperty == null) { |
| continue; |
| } |
| if (getCDAName(inherited) != null && getCDAName(inherited).equals(getCDAName(cdaProperty)) && |
| (isCDAModel(inherited) || isDatatypeModel(inherited))) { |
| return inherited; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the nearest inherited property with the same name, or null if not found. |
| * |
| * @deprecated Use the {@link UMLUtil#getInheritedProperty(Property)} API, instead. |
| */ |
| @Deprecated |
| public static Property getInheritedProperty(Property templateProperty) { |
| // for CDA, we restrict to Classes, not other classifiers |
| if (templateProperty.getClass_() == null) { |
| return null; |
| } |
| |
| return UMLUtil.getInheritedProperty(templateProperty); |
| } |
| |
| public static boolean isDatatypeModel(Element element) { |
| if (element != null && element.getNearestPackage() != null) { |
| Stereotype ePackage = element.getNearestPackage().getAppliedStereotype("Ecore::EPackage"); |
| if (ePackage != null) { |
| return DATATYPES_NS_URI.equals(element.getNearestPackage().getValue(ePackage, "nsURI")); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * isCDAModel - use get top package to support nested uml packages within CDA model |
| * primarily used for extensions |
| * |
| */ |
| public static boolean isCDAModel(Element element) { |
| if (element != null) { |
| Package neareastPackage = element.getNearestPackage(); |
| if (neareastPackage != null) { |
| Package topPackage = org.eclipse.mdht.uml.common.util.UMLUtil.getTopPackage(neareastPackage); |
| return CDA_PACKAGE_NAME.equals( |
| (topPackage != null) |
| ? topPackage.getName() |
| : ""); |
| } |
| |
| } |
| |
| return false; |
| } |
| |
| public static Class getCDADatatype(Classifier datatype) { |
| Class result = null; |
| |
| // if the provided class is from CDA datatypes |
| if (isDatatypeModel(datatype) && (datatype instanceof Class)) { |
| result = (Class) datatype; |
| } else { |
| for (Classifier parent : datatype.allParents()) { |
| // nearest package may be null if CDA datatypes model is not available |
| if (parent.getNearestPackage() != null) { |
| if (isDatatypeModel(parent) && (parent instanceof Class)) { |
| result = (Class) parent; |
| break; |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| public static boolean isCDAType(Type templateClass, String typeName) { |
| if (templateClass instanceof Class && typeName != null) { |
| Class cdaClass = getCDAClass((Class) templateClass); |
| if (cdaClass != null && typeName.equals(cdaClass.getName())) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isClinicalDocument(Type templateClass) { |
| return isCDAType(templateClass, "ClinicalDocument"); |
| } |
| |
| public static boolean isSection(Type templateClass) { |
| return isCDAType(templateClass, "Section"); |
| } |
| |
| public static boolean isOrganizer(Type templateClass) { |
| return isCDAType(templateClass, "Organizer"); |
| } |
| |
| public static boolean isEntry(Type templateClass) { |
| return isCDAType(templateClass, "Entry"); |
| } |
| |
| public static boolean isClinicalStatement(Type templateClass) { |
| if (templateClass instanceof Class) { |
| Class cdaClass = getCDAClass((Class) templateClass); |
| String cdaName = cdaClass == null |
| ? null |
| : cdaClass.getName(); |
| if (cdaClass != null && ("Act".equals(cdaName) || "Encounter".equals(cdaName) || |
| "Observation".equals(cdaName) || "ObservationMedia".equals(cdaName) || |
| "Organizer".equals(cdaName) || "Procedure".equals(cdaName) || "RegionOfInterest".equals(cdaName) || |
| "SubstanceAdministration".equals(cdaName) || "Supply".equals(cdaName))) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static void composeAllConformanceMessages(Element element, final PrintStream stream, final boolean markup) { |
| final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(Collections.singletonList(element)); |
| while (iterator != null && iterator.hasNext()) { |
| EObject child = iterator.next(); |
| |
| UMLSwitch<Object> umlSwitch = new UMLSwitch<Object>() { |
| |
| @Override |
| public Object caseAssociation(Association association) { |
| iterator.prune(); |
| return association; |
| } |
| |
| @Override |
| public Object caseClass(Class umlClass) { |
| String message = computeConformanceMessage(umlClass, markup); |
| stream.println(message); |
| |
| return umlClass; |
| } |
| |
| @Override |
| public Object caseGeneralization(Generalization generalization) { |
| String message = computeConformanceMessage(generalization, markup); |
| if (message.length() > 0) { |
| stream.println(message); |
| } |
| return generalization; |
| } |
| |
| @Override |
| public Object caseProperty(Property property) { |
| String message = computeConformanceMessage(property, markup); |
| if (message.length() > 0) { |
| stream.println(message); |
| } |
| return property; |
| } |
| |
| @Override |
| public Object caseConstraint(Constraint constraint) { |
| String message = computeConformanceMessage(constraint, markup); |
| if (message.length() > 0) { |
| stream.println(message); |
| } |
| return constraint; |
| } |
| }; |
| umlSwitch.doSwitch(child); |
| } |
| } |
| |
| public static String getValidationMessage(Element element) { |
| return getValidationMessage(element, ICDAProfileConstants.VALIDATION); |
| } |
| |
| /** |
| * Obtains the user-specified validation message recorded in the given stereotype, or else |
| * {@linkplain #computeConformanceMessage(Element, boolean) computes} a suitable conformance message if none. |
| * |
| * @param element |
| * an element on which a validation constraint stereotype is defined |
| * @param validationStereotypeName |
| * the stereotype name (may be the abstract {@linkplain ICDAProfileConstants#VALIDATION Validation} stereotype) |
| * |
| * @return the most appropriate validation/conformance message |
| * |
| * @see #computeConformanceMessage(Element, boolean) |
| */ |
| public static String getValidationMessage(Element element, String validationStereotypeName) { |
| String message = null; |
| |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype(element, validationStereotypeName); |
| if (validationSupport != null) { |
| message = (String) element.getValue(validationSupport, ICDAProfileConstants.VALIDATION_MESSAGE); |
| } |
| if (message == null || message.length() == 0) { |
| message = computeConformanceMessage(element, false); |
| } |
| |
| return message; |
| } |
| |
| public static String computeConformanceMessage(Element element, final boolean markup) { |
| |
| UMLSwitch<Object> umlSwitch = new UMLSwitch<Object>() { |
| |
| @Override |
| public Object caseAssociation(Association association) { |
| String message = null; |
| Property property = getNavigableEnd(association); |
| if (property != null) { |
| message = computeConformanceMessage(property, false); |
| } |
| return message; |
| } |
| |
| @Override |
| public Object caseClass(Class umlClass) { |
| return computeConformanceMessage(umlClass, markup); |
| } |
| |
| @Override |
| public Object caseGeneralization(Generalization generalization) { |
| return computeConformanceMessage(generalization, markup); |
| } |
| |
| @Override |
| public Object caseProperty(Property property) { |
| return computeConformanceMessage(property, markup); |
| } |
| |
| @Override |
| public Object caseConstraint(Constraint constraint) { |
| return computeConformanceMessage(constraint, markup); |
| } |
| }; |
| |
| return (String) umlSwitch.doSwitch(element); |
| } |
| |
| public static String computeConformanceMessage(Class template, final boolean markup) { |
| |
| String templateId = getTemplateId(template); |
| String templateVersion = getTemplateVersion(template); |
| String ruleIds = getConformanceRuleIds(template); |
| if (templateId == null) { |
| templateId = ""; |
| } |
| |
| String templateMultiplicity = CDATemplateComputeBuilder.getMultiplicityRange(getMultiplicityRange(template)); |
| final String templateIdAsBusinessName = "=\"" + templateId + "\""; |
| final String templateVersionAsBusinessName = "=\"" + templateVersion + "\""; |
| final String multiplicityRange = templateMultiplicity.isEmpty() |
| ? "" |
| : " [" + templateMultiplicity + "]"; |
| CDATemplateComputeBuilder cdaTemplater = new CDATemplateComputeBuilder() { |
| @Override |
| public String addTemplateIdMultiplicity() { |
| return multiplicityElementToggle(markup, "templateId", multiplicityRange, ""); |
| } |
| |
| @Override |
| public String addRootMultiplicity() { |
| return multiplicityElementToggle(markup, "@root", " [1..1]", templateIdAsBusinessName); |
| } |
| |
| @Override |
| public String addTemplateVersion() { |
| return multiplicityElementToggle(markup, "@extension", " [1..1]", templateVersionAsBusinessName); |
| } |
| }; |
| return cdaTemplater.setRequireMarkup(markup).setRuleIds(ruleIds).setTemplateVersion( |
| templateVersion).setMultiplicity(multiplicityRange).compute().toString(); |
| } |
| |
| public static String computeConformanceMessage(Generalization generalization, boolean markup) { |
| return computeConformanceMessage(generalization, markup, UMLUtil.getTopPackage(generalization)); |
| } |
| |
| public static String computeConformanceMessage(Generalization generalization, boolean markup, Package xrefSource) { |
| Class general = (Class) generalization.getGeneral(); |
| StringBuffer message = new StringBuffer(computeGeneralizationConformanceMessage(general, markup, xrefSource)); |
| |
| appendConformanceRuleIds(generalization, message, markup); |
| |
| return message.toString(); |
| } |
| |
| public static String computeGeneralizationConformanceMessage(Class general, boolean markup, Package xrefSource) { |
| StringBuffer message = new StringBuffer(); |
| |
| String prefix = !UMLUtil.isSameModel(xrefSource, general) |
| ? getModelPrefix(general) + " " |
| : ""; |
| String xref = computeXref(xrefSource, general); |
| boolean showXref = markup && (xref != null); |
| String format = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| |
| Class cdaGeneral = getCDAClass(general); |
| |
| if (cdaGeneral != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append("SHALL"); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(" conform to "); |
| } else { |
| message.append("Extends "); |
| } |
| |
| message.append( |
| showXref |
| ? "<xref " + format + "href=\"" + xref + "\">" |
| : ""); |
| message.append(prefix).append(UMLUtil.splitName(general)); |
| message.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| |
| String templateId = getTemplateId(general); |
| String templateVersion = getTemplateVersion(general); |
| |
| if (templateId != null) { |
| message.append(" template (templateId: "); |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| message.append(templateId); |
| |
| // if there is an extension, add a colon followed by its value |
| if (!StringUtils.isEmpty(templateVersion)) { |
| message.append(":" + templateVersion); |
| } |
| |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| message.append(")"); |
| } |
| |
| return message.toString(); |
| } |
| |
| private static String getBusinessName(NamedElement property) { |
| String businessName = NamedElementUtil.getBusinessName(property); |
| if (!property.getName().equals(businessName)) { |
| return (" (" + businessName + ")"); |
| } |
| return ""; |
| } |
| |
| private static StringBuffer multiplicityElementToggle(Property property, boolean markup, String elementName) { |
| StringBuffer message = new StringBuffer(); |
| message.append( |
| multiplicityElementToggle(markup, elementName, getMultiplicityRange(property), getBusinessName(property))); |
| return message; |
| } |
| |
| private static String multiplicityElementToggle(boolean markup, String elementName, String multiplicityRange, |
| String businessName) { |
| StringBuffer message = new StringBuffer(); |
| if (!cardinalityAfterElement) { |
| message.append(multiplicityRange); |
| } |
| |
| message.append(" "); |
| message.append( |
| markup |
| ? "<tt><b>" |
| : ""); |
| message.append(elementName); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(businessName); |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| if (cardinalityAfterElement) { |
| message.append(multiplicityRange); |
| } |
| return message.toString(); |
| } |
| |
| public static String computeAssociationConformanceMessage(Property property, boolean markup, Package xrefSource, |
| boolean appendNestedConformanceRules) { |
| |
| Class endType = (property.getType() instanceof Class) |
| ? (Class) property.getType() |
| : null; |
| |
| if (!isInlineClass(endType) && getTemplateId(property.getClass_()) != null) { |
| return computeTemplateAssociationConformanceMessage( |
| property, markup, xrefSource, appendNestedConformanceRules); |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| Association association = property.getAssociation(); |
| |
| if (!markup) { |
| message.append(getPrefixedSplitName(property.getClass_())).append(" "); |
| } |
| |
| if (getTemplateId(property.getClass_()) != null) { |
| appendNullflavorSectionMessage(property, message); |
| } |
| |
| String keyword = getValidationKeywordWithPropertyRange(association, property); |
| if (keyword != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(" contain "); |
| if (property.getUpper() == 0 && isClosed(property)) { |
| message.append("any "); |
| } |
| } else { |
| |
| if (property.getUpper() < 0 || property.getUpper() > 1) { |
| message.append("contains "); |
| } else { |
| message.append("contain "); |
| } |
| if (property.getUpper() == 0 && isClosed(property)) { |
| message.append("any "); |
| } |
| |
| } |
| |
| String elementName = resolveCdaPropertyName(property); |
| |
| String propertyPrefix = getNameSpacePrefix( |
| UMLUtil.getInheritedProperty(property) != null |
| ? UMLUtil.getInheritedProperty(property) |
| : property); |
| if (propertyPrefix != null && !elementName.contains(":")) { |
| elementName = propertyPrefix + ":" + elementName; |
| } |
| |
| message.append(getMultiplicityText(property)); |
| message.append(multiplicityElementToggle(property, markup, elementName)); |
| |
| // appendSubsetsNotation(property, message, markup, xrefSource); |
| |
| if (appendNestedConformanceRules && endType != null) { |
| |
| if (markup && isDisplayInline(endType)) { |
| StringBuilder sb = new StringBuilder(); |
| |
| message.append(openOrClosed(property)); |
| |
| // message.append(", where its type is "); |
| |
| appendConformanceRuleIds(association, message, markup); |
| |
| appendPropertyComments(sb, property, markup); |
| |
| // // (property.getUpper() == 1,? "This " |
| // : "Such ") + |
| // (property.getUpper() == 1 |
| // ? elementName |
| // : NameUtilities.pluralize(elementName)) + |
| // " " |
| appendConformanceRules(sb, endType, "", markup); |
| message.append(" " + sb + " "); |
| |
| } else { |
| |
| message.append(", where its type is "); |
| |
| String prefix = !UMLUtil.isSameModel(xrefSource, endType) |
| ? getModelPrefix(endType) + " " |
| : ""; |
| String xref = computeXref(xrefSource, endType); |
| boolean showXref = markup && (xref != null); |
| String format = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| |
| message.append( |
| showXref |
| ? "<xref " + format + "href=\"" + xref + "\">" |
| : ""); |
| message.append(prefix).append(UMLUtil.getAnchorText(endType)); |
| message.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| |
| appendConformanceRuleIds(association, message, markup); |
| } |
| } else { |
| appendConformanceRuleIds(association, message, markup); |
| } |
| |
| return message.toString(); |
| } |
| |
| private static String computeTemplateAssociationConformanceMessage(Property property, boolean markup, |
| Package xrefSource, boolean appendNestedConformanceRules) { |
| StringBuffer message = new StringBuffer(); |
| Association association = property.getAssociation(); |
| |
| String elementName = resolveCdaPropertyName(property); |
| |
| if (!markup) { |
| message.append(getPrefixedSplitName(property.getClass_())).append(" "); |
| } |
| |
| appendNullflavorSectionMessage(property, message); |
| |
| String keyword = getValidationKeyword(association); |
| if (keyword != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(" contain "); |
| } else { |
| if (property.getUpper() < 0 || property.getUpper() > 1) { |
| message.append("contains "); |
| } else { |
| message.append("contain "); |
| } |
| } |
| |
| message.append(multiplicityElementToggle(property, markup, elementName)); |
| |
| message.append(openOrClosed(property)); |
| |
| appendConformanceRuleIds(association, message, markup); |
| |
| if (appendNestedConformanceRules && property.getType() instanceof Class) { |
| Class inlinedClass = (Class) property.getType(); |
| |
| if (markup && isInlineClass(inlinedClass)) { |
| StringBuilder sb = new StringBuilder(); |
| |
| appendPropertyComments(sb, property, markup); |
| |
| appendConformanceRules( |
| sb, inlinedClass, (property.getUpper() == 1 |
| ? "This " |
| : "Such ") + |
| (property.getUpper() == 1 |
| ? elementName |
| : NameUtilities.pluralize(elementName)) + |
| " ", |
| markup); |
| message.append(" " + sb); |
| |
| } |
| } |
| |
| if (!markup) { |
| String assocConstraints = computeAssociationConstraints(property, markup); |
| if (assocConstraints.length() > 0) { |
| message.append(assocConstraints); |
| } |
| } |
| |
| return message.toString(); |
| } |
| |
| /** |
| * Adds a null flavor condition message under certain circumstances |
| * |
| * @param property |
| * @param message |
| */ |
| private static void appendNullflavorSectionMessage(Property property, StringBuffer message) { |
| |
| Association association = property.getAssociation(); |
| |
| // errata 384 message support: if the class owner is a section, and it has an association |
| // to either a clinical statement or an Entry (Act, Observation, etc.), append the message |
| for (Property p : association.getMemberEnds()) { |
| if ((p.getName() != null && !p.getName().isEmpty()) && (p.getOwner() != null && p.getType() != null) && |
| (isSection((Class) p.getOwner()) && isClinicalStatement(p.getType()) || isEntry(p.getType()))) { |
| if (property.eContainer() instanceof Class) { |
| Class class1 = (Class) property.eContainer(); |
| Property nullFlavor = CDACommonUtils.findAttribute(class1, "nullFlavor"); |
| if (nullFlavor != null && nullFlavor.upperBound() == 0) { |
| // if nullFlavor is forbidden, don't print this message |
| continue; |
| } |
| } |
| message.append(NULLFLAVOR_SECTION_MESSAGE); |
| } |
| } |
| } |
| |
| public static String computeAssociationConstraints(Property property, boolean markup) { |
| StringBuffer message = new StringBuffer(); |
| |
| Association association = property.getAssociation(); |
| Package xrefSource = UMLUtil.getTopPackage(property); |
| |
| EntryRelationship entryRelationship = CDAProfileUtil.getEntryRelationship(association); |
| EntryRelationshipKind typeCode = entryRelationship != null |
| ? entryRelationship.getTypeCode() |
| : null; |
| |
| Class endType = (property.getType() instanceof Class) |
| ? (Class) property.getType() |
| : null; |
| |
| if (typeCode != null) { |
| message.append( |
| markup |
| ? "\n<li>" |
| : " "); |
| message.append("Contains "); |
| message.append( |
| markup |
| ? "<tt><b>" |
| : "").append("@typeCode=\"").append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(typeCode).append("\" "); |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| message.append( |
| markup |
| ? "<i>" |
| : ""); |
| message.append(typeCode.getLiteral()); |
| message.append( |
| markup |
| ? "</i>" |
| : ""); |
| message.append( |
| markup |
| ? "</li>" |
| : ", and"); |
| } |
| |
| // TODO: what I should really do is test for an *implied* ActRelationship or Participation association |
| if (endType != null && getCDAClass(endType) != null && !(isInlineClass(endType)) && |
| !isInlineClass(property.getClass_())) { |
| message.append( |
| markup |
| ? "\n<li>" |
| : " "); |
| message.append("Conforms to "); |
| |
| String prefix = ""; |
| String xref = computeXref(xrefSource, endType); |
| boolean showXref = markup && (xref != null); |
| String format = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| |
| message.append( |
| showXref |
| ? "<xref " + format + "href=\"" + xref + "\">" |
| : ""); |
| message.append(prefix).append(UMLUtil.splitName(endType)); |
| message.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| |
| String templateId = getTemplateId(endType); |
| String templateVersion = getTemplateVersion(endType); |
| |
| if (templateId != null) { |
| message.append(" (templateId: "); |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| message.append(templateId); |
| |
| // if there is an extension, add a colon followed by its value |
| if (!StringUtils.isEmpty(templateVersion)) { |
| message.append(":" + templateVersion); |
| } |
| |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| message.append(")"); |
| } |
| |
| if (!UMLUtil.isSameModel(xrefSource, endType)) { |
| message.append(" from " + getModelPrefix(endType)); |
| } |
| |
| message.append( |
| markup |
| ? "</li>" |
| : ""); |
| } |
| |
| return message.toString(); |
| } |
| |
| public static String computeConformanceMessage(Property property, boolean markup) { |
| return computeConformanceMessage(property, markup, UMLUtil.getTopPackage(property)); |
| } |
| |
| private static String getNameSpacePrefix(Property property) { |
| |
| Property cdaBaseProperty = CDAModelUtil.getCDAProperty(property); |
| String nameSpacePrefix = null; |
| if (cdaBaseProperty != null) { |
| Stereotype eReferenceStereoetype = cdaBaseProperty.getAppliedStereotype(CDAModelUtil.EREFERENCE); |
| if (eReferenceStereoetype != null) { |
| String nameSpace = (String) cdaBaseProperty.getValue(eReferenceStereoetype, CDAModelUtil.XMLNAMESPACE); |
| if (!StringUtils.isEmpty(nameSpace)) { |
| Package topPackage = org.eclipse.mdht.uml.common.util.UMLUtil.getTopPackage( |
| cdaBaseProperty.getNearestPackage()); |
| Stereotype ePackageStereoetype = topPackage.getApplicableStereotype(CDAModelUtil.EPACKAGE); |
| if (ePackageStereoetype != null) { |
| if (nameSpace.equals(topPackage.getValue(ePackageStereoetype, CDAModelUtil.NSURI))) { |
| nameSpacePrefix = (String) topPackage.getValue(ePackageStereoetype, CDAModelUtil.NSPREFIX); |
| } else { |
| for (Package nestedPackage : topPackage.getNestedPackages()) { |
| if (nameSpace.equals(nestedPackage.getValue(ePackageStereoetype, CDAModelUtil.NSURI))) { |
| nameSpacePrefix = (String) nestedPackage.getValue( |
| ePackageStereoetype, CDAModelUtil.NSPREFIX); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return nameSpacePrefix; |
| } |
| |
| public static String computeConformanceMessage(Property property, boolean markup, Package xrefSource) { |
| return computeConformanceMessage(property, markup, xrefSource, true); |
| } |
| |
| private static String openOrClosed(Property property) { |
| if (isClosed(property)) { |
| return " "; |
| } else { |
| return " such that it "; |
| } |
| } |
| |
| private static boolean isClosed(Property property) { |
| Validation validation = org.eclipse.uml2.uml.util.UMLUtil.getStereotypeApplication( |
| (property.getAssociation() != null |
| ? property.getAssociation() |
| : property), |
| Validation.class); |
| if (validation != null && validation.getKind().equals(ValidationKind.CLOSED)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public static String computeConformanceMessage(Property property, boolean markup, Package xrefSource, |
| boolean appendNestedConformanceRules) { |
| |
| if (property.getType() == null) { |
| System.out.println("Property has null type: " + property.getQualifiedName()); |
| } |
| if (property.getAssociation() != null && property.isNavigable()) { |
| return computeAssociationConformanceMessage(property, markup, xrefSource, appendNestedConformanceRules); |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| |
| if (!markup) { |
| message.append(getPrefixedSplitName(property.getClass_())).append(" "); |
| } |
| |
| String keyword = getValidationKeywordWithPropertyRange(property); |
| if (keyword != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(" contain "); |
| if (property.getUpper() == 0 && isClosed(property)) { |
| message.append("any "); |
| } |
| } else { |
| if (property.getUpper() < 0 || property.getUpper() > 1) { |
| message.append("contains "); |
| } else { |
| message.append("contain "); |
| } |
| if (property.getUpper() == 0 && isClosed(property)) { |
| message.append("any "); |
| } |
| } |
| |
| message.append(getMultiplicityText(property)); |
| if (!cardinalityAfterElement) { |
| message.append(getMultiplicityRange(property)); |
| } |
| message.append(" "); |
| |
| message.append( |
| markup |
| ? "<tt><b>" |
| : ""); |
| // classCode/moodCode |
| if (isXMLAttribute(property)) { |
| message.append("@"); |
| } |
| |
| String propertyPrefix = getNameSpacePrefix(property); |
| |
| String cdaPropertyName = resolveCdaPropertyName(property); |
| |
| message.append( |
| propertyPrefix != null |
| ? propertyPrefix + ":" + cdaPropertyName |
| : cdaPropertyName); |
| |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| message.append(getBusinessName(property)); |
| |
| if (property.getDefault() != null) { |
| message.append("=\"").append(property.getDefault()).append("\" "); |
| } |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| if (cardinalityAfterElement) { |
| message.append(getMultiplicityRange(property)); |
| } |
| |
| Stereotype nullFlavorSpecification = CDAProfileUtil.getAppliedCDAStereotype( |
| property, ICDAProfileConstants.NULL_FLAVOR); |
| Stereotype textValue = CDAProfileUtil.getAppliedCDAStereotype(property, ICDAProfileConstants.TEXT_VALUE); |
| |
| if (nullFlavorSpecification != null) { |
| String nullFlavor = getLiteralValue( |
| property, nullFlavorSpecification, ICDAProfileConstants.NULL_FLAVOR_NULL_FLAVOR); |
| Enumeration profileEnum = (Enumeration) nullFlavorSpecification.getProfile().getOwnedType( |
| ICDAProfileConstants.NULL_FLAVOR_KIND); |
| String nullFlavorLabel = getLiteralValueLabel( |
| property, nullFlavorSpecification, ICDAProfileConstants.NULL_FLAVOR_NULL_FLAVOR, profileEnum); |
| |
| if (nullFlavor != null) { |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| message.append("/@nullFlavor"); |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| message.append(" = \"").append(nullFlavor).append("\" "); |
| message.append( |
| markup |
| ? "<i>" |
| : ""); |
| message.append(nullFlavorLabel); |
| message.append( |
| markup |
| ? "</i>" |
| : ""); |
| } |
| } |
| if (textValue != null) { |
| String value = (String) property.getValue(textValue, ICDAProfileConstants.TEXT_VALUE_VALUE); |
| if (value != null && value.length() > 0) { |
| SeverityKind level = (SeverityKind) property.getValue( |
| textValue, ICDAProfileConstants.VALIDATION_SEVERITY); |
| message.append(" which ").append( |
| markup |
| ? "<b>" |
| : "").append( |
| level != null |
| ? getValidationKeyword(level.getLiteral()) |
| : keyword).append( |
| markup |
| ? "</b>" |
| : "").append(" equal \"").append(value).append("\""); |
| } |
| } |
| |
| Property cdaProperty = transformToCDAProperty(property); |
| if (property.getType() instanceof Classifier && cdaProperty != null && |
| cdaProperty.getType() instanceof Classifier) { |
| Classifier propertyType = (Classifier) property.getType(); |
| Classifier cdaPropertyType = (Classifier) cdaProperty.getType(); |
| Class propertyCdaType = CDAModelUtil.getCDAClass(propertyType); |
| if (propertyCdaType == null) { |
| propertyCdaType = CDAModelUtil.getCDADatatype(propertyType); |
| } |
| // if the datatype is not different from the immediate parent, then the xsi:type shouldn't be printed |
| if (propertyCdaType != null && cdaPropertyType != null && propertyCdaType != cdaPropertyType && |
| propertyCdaType.getName() != null && !propertyCdaType.getName().isEmpty()) { |
| message.append(" with "); |
| message.append( |
| markup |
| ? "<tt><b>" |
| : ""); |
| message.append("@xsi:type"); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append("=\""); |
| |
| message.append(propertyCdaType.getName()); |
| message.append("\""); |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| } |
| } |
| |
| // for vocab properties, put rule ID at end, use terminology constraint if specified |
| if (isHL7VocabAttribute(property)) { |
| String ruleIds = getTerminologyConformanceRuleIds(property); |
| // if there are terminology rule IDs, then include property rule IDs here |
| if (ruleIds.length() > 0) { |
| appendConformanceRuleIds(property, message, markup); |
| } |
| } else { |
| // PropertyConstraint stereotype ruleIds, if specified |
| appendConformanceRuleIds(property, message, markup); |
| } |
| |
| Stereotype codeSystemConstraint = TermProfileUtil.getAppliedStereotype( |
| property, ITermProfileConstants.CODE_SYSTEM_CONSTRAINT); |
| Stereotype valueSetConstraint = TermProfileUtil.getAppliedStereotype( |
| property, ITermProfileConstants.VALUE_SET_CONSTRAINT); |
| |
| if (codeSystemConstraint != null) { |
| String vocab = computeCodeSystemMessage(property, markup); |
| message.append(vocab); |
| } else if (valueSetConstraint != null) { |
| String vocab = computeValueSetMessage(property, markup, xrefSource); |
| message.append(vocab); |
| } else if (isHL7VocabAttribute(property) && property.getDefault() != null) { |
| String vocab = computeHL7VocabAttributeMessage(property, markup); |
| message.append(vocab); |
| } |
| |
| // for vocab properties, put rule ID at end, use terminology constraint if specified |
| if (isHL7VocabAttribute(property)) { |
| String ruleIds = getTerminologyConformanceRuleIds(property); |
| if (ruleIds.length() > 0) { |
| appendTerminologyConformanceRuleIds(property, message, markup); |
| } else { |
| appendConformanceRuleIds(property, message, markup); |
| } |
| } else { |
| // rule IDs for the terminology constraint |
| appendTerminologyConformanceRuleIds(property, message, markup); |
| } |
| |
| if (property.getType() != null && appendNestedConformanceRules && property.getType() instanceof Class) { |
| if (isInlineClass((Class) property.getType())) { |
| |
| if (isPublishSeperately((Class) property.getType())) { |
| |
| String xref = (property.getType() instanceof Classifier && |
| UMLUtil.isSameProject(property, property.getType())) |
| ? computeXref(xrefSource, (Classifier) property.getType()) |
| : null; |
| boolean showXref = markup && (xref != null); |
| |
| if (showXref) { |
| String format = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| message.append( |
| showXref |
| ? "<xref " + format + "href=\"" + xref + "\">" |
| : ""); |
| message.append(UMLUtil.splitName(property.getType())); |
| message.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| } |
| |
| } else { |
| StringBuilder sb = new StringBuilder(); |
| boolean hadSideEffect = appendPropertyComments(sb, property, markup); |
| if (isAppendConformanceRules) { |
| int len = sb.length(); |
| |
| appendConformanceRules(sb, (Class) property.getType(), "", markup); |
| |
| hadSideEffect |= sb.length() > len; |
| } |
| if (hadSideEffect) { |
| |
| if (cdaProperty.upperBound() != 1 && property.getType() instanceof Class && |
| CDAModelUtil.isInlineClass((Class) property.getType())) { |
| message.append(openOrClosed(property) + " " + sb); |
| } else { |
| message.append(" " + sb); |
| } |
| |
| } |
| } |
| |
| } |
| |
| } |
| |
| return message.toString(); |
| } |
| |
| /** |
| * Fully resolve the CDA Name of a property. |
| * |
| * Note that this may not necessarily be the UML name of the property |
| * as in redefined and subsets properties, so go looking for the correct name |
| * |
| * @param property |
| * the name to be resolved |
| * @return string property name as it would appear in CDA |
| */ |
| private static String resolveCdaPropertyName(Property property) { |
| Property cdaProperty = transformToCDAProperty(property); |
| |
| String propertyCdaName = null; |
| if (cdaProperty != null) { |
| propertyCdaName = getCDAName(cdaProperty); |
| } else { |
| propertyCdaName = getCDAElementName(property); |
| } |
| return propertyCdaName; |
| } |
| |
| /** |
| * transformToCDAProperty uses the Extension-point trick |
| * to access attributes on downstream projects without creating |
| * a dependency. |
| * |
| * In this case it uses CDABaseModelReflection |
| * |
| * @see also TransformSupplier and org.openhealthtools.mdht.uml.cda.core.TransformProvider |
| * |
| * Used when resolving CDAName as opposed to UML name |
| * |
| * @param property |
| * a CDA Property |
| * @return a transformed view of that property or null |
| */ |
| private static Property transformToCDAProperty(Property property) { |
| // Get the extension point |
| IExtensionRegistry reg = Platform.getExtensionRegistry(); |
| IExtensionPoint ep = reg.getExtensionPoint("org.eclipse.mdht.uml.cda.core.TransformProvider"); |
| IExtension[] extensions = ep.getExtensions(); |
| TransformProvider newContributor = null; |
| Property cdaProperty = null; |
| |
| // then apply the transform |
| try { |
| newContributor = (TransformProvider) extensions[0].getConfigurationElements()[0].createExecutableExtension( |
| "transform-class"); |
| cdaProperty = newContributor.GetTransform(property); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| return cdaProperty; |
| } |
| |
| /** |
| * getCDAName |
| * |
| * recursively, depth first, check the object graph for a CDA Name using the root of the "redefined" |
| * property if it exists. |
| * |
| * Also handle special cases like sectionId which has a cdaName of ID |
| * |
| * @param cdaProperty |
| * an MDHT property |
| * @return string |
| * the calculated CDA name |
| */ |
| private static String getCDAName(Property cdaProperty) { |
| EList<Property> redefines = cdaProperty.getRedefinedProperties(); |
| |
| // if there is a stereotype name, use it |
| String name = getStereotypeName(cdaProperty); |
| if (name != null && !name.isEmpty()) { |
| return name; |
| } |
| |
| // if there are redefines, check for more but only along the first branch (0) |
| if (redefines != null && redefines.size() > 0) { |
| return getCDAName(redefines.get(0)); |
| } |
| |
| // eventually return the property Name of the root redefined element; |
| |
| return cdaProperty.getName(); |
| } |
| |
| /** |
| * Get the CDA name from a stereotype if it exists |
| * |
| * @param cdaProperty |
| * a Property |
| * @return the xmlName or null if no stereotype exists |
| */ |
| private static String getStereotypeName(Property cdaProperty) { |
| Stereotype eAttribute = cdaProperty.getAppliedStereotype("Ecore::EAttribute"); |
| String name = null; |
| if (eAttribute != null) { |
| name = (String) cdaProperty.getValue(eAttribute, "xmlName"); |
| } |
| return name; |
| } |
| |
| private static void appendSubsetsNotation(Property property, StringBuffer message, boolean markup, |
| Package xrefSource) { |
| StringBuffer notation = new StringBuffer(); |
| for (Property subsets : property.getSubsettedProperties()) { |
| if (subsets.getClass_() == null) { |
| // eliminate NPE when publishing stereotype references to UML metamodel |
| continue; |
| } |
| if (notation.length() == 0) { |
| notation.append(" {subsets "); |
| } else { |
| notation.append(", "); |
| } |
| |
| String xref = computeXref(xrefSource, subsets.getClass_()); |
| boolean showXref = markup && (xref != null); |
| String format = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| notation.append( |
| showXref |
| ? " <xref " + format + "href=\"" + xref + "\">" |
| : " "); |
| notation.append(UMLUtil.splitName(subsets.getClass_())); |
| notation.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| |
| notation.append("::" + subsets.getName()); |
| } |
| if (notation.length() > 0) { |
| notation.append("}"); |
| } |
| |
| message.append(notation); |
| } |
| |
| private static final String[] OL = { "<ol>", "</ol>" }; |
| |
| private static final String[] LI = { "<li>", "</li>" }; |
| |
| private static final String[] NOOL = { "", " " }; |
| |
| private static final String[] NOLI = { "", " " }; |
| |
| static public void appendConformanceRules(StringBuilder appendB, Class umlClass, String prefix, boolean markup) { |
| |
| String[] ol = markup |
| ? OL |
| : NOOL; |
| String[] li = markup |
| ? LI |
| : NOLI; |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| boolean hasRules = false; |
| |
| if (!CDAModelUtil.isInlineClass(umlClass)) { |
| for (Generalization generalization : umlClass.getGeneralizations()) { |
| Classifier general = generalization.getGeneral(); |
| if (!RIMModelUtil.isRIMModel(general) && !CDAModelUtil.isCDAModel(general) && |
| !CDAModelUtil.isDatatypeModel(general)) { |
| String message = CDAModelUtil.computeConformanceMessage(generalization, markup); |
| if (message.length() > 0) { |
| hasRules = true; |
| sb.append(li[0] + prefix + message + li[1]); |
| } |
| } |
| } |
| } |
| |
| // categorize constraints by constrainedElement name |
| List<Constraint> unprocessedConstraints = new ArrayList<Constraint>(); |
| // propertyName -> constraints |
| Map<String, List<Constraint>> constraintMap = new HashMap<String, List<Constraint>>(); |
| // constraint -> sub-constraints |
| Map<Constraint, List<Constraint>> subConstraintMap = new HashMap<Constraint, List<Constraint>>(); |
| |
| for (Constraint constraint : umlClass.getOwnedRules()) { |
| unprocessedConstraints.add(constraint); |
| |
| // Do not associate logical constraints with a property because they are a class and not a property constraint |
| if (CDAProfileUtil.getLogicalConstraint(constraint) == null) { |
| for (Element element : constraint.getConstrainedElements()) { |
| if (element instanceof Property) { |
| String name = ((Property) element).getName(); |
| List<Constraint> rules = constraintMap.get(name); |
| if (rules == null) { |
| rules = new ArrayList<Constraint>(); |
| constraintMap.put(name, rules); |
| } |
| rules.add(constraint); |
| } else if (element instanceof Constraint) { |
| Constraint subConstraint = (Constraint) element; |
| List<Constraint> rules = subConstraintMap.get(subConstraint); |
| if (rules == null) { |
| rules = new ArrayList<Constraint>(); |
| subConstraintMap.put(subConstraint, rules); |
| } |
| rules.add(constraint); |
| } |
| } |
| } |
| } |
| |
| PropertyList propertyList = new PropertyList(umlClass, CDAModelUtil.isInlineClass(umlClass)); |
| |
| // XML attributes |
| for (Property property : propertyList.getAttributes()) { |
| if (!CDAModelUtil.hasOwnPDFSection(property)) { |
| continue; |
| } |
| hasRules = hasRules | appendPropertyList( |
| umlClass, property, markup, ol, sb, prefix, li, constraintMap, unprocessedConstraints, |
| subConstraintMap); |
| } |
| // XML elements |
| for (Property property : propertyList.getAssociationEnds()) { |
| hasRules = hasRules | appendPropertyList( |
| umlClass, property, markup, ol, sb, prefix, li, constraintMap, unprocessedConstraints, |
| subConstraintMap); |
| } |
| |
| for (Constraint constraint : unprocessedConstraints) { |
| hasRules = true; |
| sb.append(li[0] + prefix + CDAModelUtil.computeConformanceMessage(constraint, markup) + li[1]); |
| } |
| |
| if (hasRules) { |
| appendB.append(ol[0]); |
| appendB.append(sb); |
| appendB.append(ol[1]); |
| } |
| } |
| |
| /** |
| * @param property |
| * @return whether the given property has an independent section in the PDF file (<code>true</code>) or is only printed in context of a |
| * logical constraint (<code>false</code>) |
| */ |
| public static boolean hasOwnPDFSection(Property property) { |
| Class clazz = (Class) property.eContainer(); |
| for (Constraint constraint : clazz.getOwnedRules()) { |
| if (constraint.getConstrainedElements().contains(property)) { |
| LogicalConstraint logicConstraint = CDAProfileUtil.getLogicalConstraint(constraint); |
| if (logicConstraint != null) { |
| if (logicConstraint.getOperation() == LogicalOperator.IFTHEN && |
| constraint.getConstrainedElements().indexOf(property) == 1) { |
| // for a (IF A THEN B) constraint, B hasn't its own section |
| Validation cv = CDAProfileUtil.getValidation(property); |
| if (cv != null) { |
| // print if severity is given |
| return true; |
| } |
| return false; |
| } |
| if (logicConstraint.getOperation() == LogicalOperator.XOR) { |
| // a XOR constraint has no own section |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| private static boolean appendPropertyList(Element umlClass, Property property, boolean markup, String[] ol, |
| StringBuilder sb, String prefix, String[] li, Map<String, List<Constraint>> constraintMap, |
| List<Constraint> unprocessedConstraints, Map<Constraint, List<Constraint>> subConstraintMap) { |
| boolean result = false; |
| if (!CDAModelUtil.isCDAModel(umlClass) && !CDAModelUtil.isCDAModel(property) && |
| !CDAModelUtil.isDatatypeModel(property)) { |
| result = true; |
| String ccm = CDAModelUtil.computeConformanceMessage(property, markup); |
| boolean order = ccm.trim().endsWith(ol[1]); |
| boolean currentlyItem = false; |
| if (order) { |
| int olIndex = ccm.lastIndexOf(ol[1]); |
| ccm = ccm.substring(0, olIndex); |
| currentlyItem = ccm.trim().endsWith(li[1]); |
| } |
| sb.append(li[0] + prefix + ccm); |
| StringBuilder propertyComments = new StringBuilder(); |
| currentlyItem &= appendPropertyComments(propertyComments, property, markup); |
| if (currentlyItem) { |
| int pos = ccm.indexOf(propertyComments.toString()); |
| // append property comments only if they are not already in the conformance message already (to be precise, before the first list item |
| // of the conformance message) |
| if (!(pos >= 0 && pos < ccm.indexOf(li[0]))) { |
| sb.append(li[0]).append(propertyComments).append(li[1]); |
| } |
| } else { |
| sb.append(propertyComments); |
| } |
| |
| appendPropertyRules(sb, property, constraintMap, subConstraintMap, unprocessedConstraints, markup, !order); |
| |
| if (order) { |
| sb.append(ol[1]); |
| } |
| |
| sb.append(li[1]); |
| } |
| return result; |
| } |
| |
| private static boolean appendPropertyComments(StringBuilder sb, Property property, boolean markup) { |
| |
| // INLINE |
| |
| Association association = property.getAssociation(); |
| |
| int startingStrLength = sb.length(); |
| |
| if (association != null && association.getOwnedComments().size() > 0) { |
| if (markup) { |
| sb.append("<p><lines><i>"); |
| } |
| for (Comment comment : association.getOwnedComments()) { |
| sb.append(CDAModelUtil.fixNonXMLCharacters(comment.getBody())); |
| } |
| if (markup) { |
| sb.append("</i></lines></p>"); |
| } |
| } |
| |
| if (property.getOwnedComments().size() > 0) { |
| if (markup) { |
| sb.append("<p><lines><i>"); |
| } |
| for (Comment comment : property.getOwnedComments()) { |
| sb.append(CDAModelUtil.fixNonXMLCharacters(comment.getBody())); |
| } |
| if (markup) { |
| sb.append("</i></lines></p>"); |
| } |
| } |
| |
| return sb.length() > startingStrLength; |
| } |
| |
| private static void appendPropertyRules(StringBuilder sb, Property property, |
| Map<String, List<Constraint>> constraintMap, Map<Constraint, List<Constraint>> subConstraintMap, |
| List<Constraint> unprocessedConstraints, boolean markup, boolean newOrder) { |
| |
| String[] ol = markup && newOrder |
| ? OL |
| : NOOL; |
| String[] li = markup |
| ? LI |
| : NOLI; |
| |
| // association typeCode and property type |
| String assocConstraints = ""; |
| if (property.getAssociation() != null) { |
| assocConstraints = CDAModelUtil.computeAssociationConstraints(property, markup); |
| } |
| |
| StringBuffer ruleConstraints = new StringBuffer(); |
| List<Constraint> rules = constraintMap.get(property.getName()); |
| if (rules != null && !rules.isEmpty()) { |
| for (Constraint constraint : rules) { |
| unprocessedConstraints.remove(constraint); |
| ruleConstraints.append(li[0] + CDAModelUtil.computeConformanceMessage(constraint, markup)); |
| appendSubConstraintRules(ruleConstraints, constraint, subConstraintMap, unprocessedConstraints, markup); |
| ruleConstraints.append(li[1]); |
| } |
| } |
| |
| if (assocConstraints.length() > 0 || ruleConstraints.length() > 0) { |
| sb.append(ol[0]); |
| sb.append(assocConstraints); |
| sb.append(ruleConstraints); |
| sb.append(ol[1]); |
| } |
| } |
| |
| private static void appendSubConstraintRules(StringBuffer ruleConstraints, Constraint constraint, |
| Map<Constraint, List<Constraint>> subConstraintMap, List<Constraint> unprocessedConstraints, |
| boolean markup) { |
| |
| String[] ol; |
| String[] li; |
| |
| if (markup) { |
| ol = OL; |
| li = LI; |
| } else { |
| ol = NOOL; |
| li = NOLI; |
| } |
| |
| List<Constraint> subConstraints = subConstraintMap.get(constraint); |
| if (subConstraints != null && subConstraints.size() > 0) { |
| ruleConstraints.append(ol[0]); |
| for (Constraint subConstraint : subConstraints) { |
| unprocessedConstraints.remove(subConstraint); |
| ruleConstraints.append(li[0] + CDAModelUtil.computeConformanceMessage(subConstraint, markup)); |
| appendSubConstraintRules( |
| ruleConstraints, subConstraint, subConstraintMap, unprocessedConstraints, markup); |
| ruleConstraints.append(li[1]); |
| } |
| ruleConstraints.append(ol[1]); |
| } |
| } |
| |
| private static boolean isHL7VocabAttribute(Property property) { |
| String name = property.getName(); |
| return "classCode".equals(name) || "moodCode".equals(name) || "typeCode".equals(name); |
| } |
| |
| private static String computeHL7VocabAttributeMessage(Property property, boolean markup) { |
| StringBuffer message = new StringBuffer(); |
| Class rimClass = RIMModelUtil.getRIMClass(property.getClass_()); |
| String code = property.getDefault(); |
| String displayName = null; |
| String codeSystemId = null; |
| String codeSystemName = null; |
| |
| if (rimClass != null) { |
| if ("Act".equals(rimClass.getName())) { |
| if ("classCode".equals(property.getName())) { |
| codeSystemName = "HL7ActClass"; |
| codeSystemId = "2.16.840.1.113883.5.6"; |
| |
| if ("ACT".equals(code)) { |
| displayName = "Act"; |
| } else if ("OBS".equals(code)) { |
| displayName = "Observation"; |
| } |
| } else if ("moodCode".equals(property.getName())) { |
| codeSystemName = "HL7ActMood"; |
| codeSystemId = "2.16.840.1.113883.5.1001"; |
| |
| if ("EVN".equals(code)) { |
| displayName = "Event"; |
| } |
| } |
| } |
| } |
| |
| if (displayName != null) { |
| message.append( |
| markup |
| ? "<i>" |
| : ""); |
| message.append(displayName); |
| message.append( |
| markup |
| ? "</i>" |
| : ""); |
| } |
| |
| if (codeSystemId != null || codeSystemName != null) { |
| message.append(" (CodeSystem:"); |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| if (codeSystemId != null) { |
| message.append(" ").append(codeSystemId); |
| } |
| if (codeSystemName != null) { |
| message.append(" ").append(codeSystemName); |
| } |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| message.append(")"); |
| } |
| |
| return message.toString(); |
| } |
| |
| private static String computeCodeSystemMessage(Property property, boolean markup) { |
| Stereotype codeSystemConstraintStereotype = TermProfileUtil.getAppliedStereotype( |
| property, ITermProfileConstants.CODE_SYSTEM_CONSTRAINT); |
| org.eclipse.mdht.uml.term.core.profile.CodeSystemConstraint codeSystemConstraint = TermProfileUtil.getCodeSystemConstraint( |
| property); |
| |
| String keyword = getValidationKeyword(property, codeSystemConstraintStereotype); |
| String id = null; |
| String name = null; |
| String code = null; |
| String displayName = null; |
| |
| if (codeSystemConstraint != null) { |
| if (codeSystemConstraint.getReference() != null) { |
| CodeSystemVersion codeSystemVersion = codeSystemConstraint.getReference(); |
| id = codeSystemVersion.getIdentifier(); |
| name = codeSystemVersion.getEnumerationName(); |
| codeSystemVersion.getVersion(); |
| } else { |
| id = codeSystemConstraint.getIdentifier(); |
| name = codeSystemConstraint.getName(); |
| codeSystemConstraint.getVersion(); |
| } |
| |
| codeSystemConstraint.getBinding(); |
| code = codeSystemConstraint.getCode(); |
| displayName = codeSystemConstraint.getDisplayName(); |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| if (code != null) { |
| message.append( |
| markup |
| ? "<tt><b>" |
| : ""); |
| // single value binding |
| message.append("/@code"); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| message.append("=\"").append(code).append("\" "); |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| if (displayName != null) { |
| message.append( |
| markup |
| ? "<i>" |
| : ""); |
| message.append(displayName); |
| message.append( |
| markup |
| ? "</i>" |
| : ""); |
| } |
| |
| } else { |
| // capture and return proper xml binding message based on mandatory or not |
| message.append(mandatoryOrNotMessage(property)); |
| |
| if (keyword != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| message.append(" be"); |
| } else { |
| message.append("is"); |
| } |
| message.append(" selected from"); |
| } |
| |
| if (id != null || name != null) { |
| message.append(" (CodeSystem:"); |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| if (id != null) { |
| message.append(" ").append(id); |
| } |
| if (name != null) { |
| message.append(" ").append(name); |
| } |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| message.append(")"); |
| } |
| |
| return message.toString(); |
| } |
| |
| private static String computeValueSetMessage(Property property, boolean markup, Package xrefSource) { |
| Stereotype valueSetConstraintStereotype = TermProfileUtil.getAppliedStereotype( |
| property, ITermProfileConstants.VALUE_SET_CONSTRAINT); |
| org.eclipse.mdht.uml.term.core.profile.ValueSetConstraint valueSetConstraint = TermProfileUtil.getValueSetConstraint( |
| property); |
| |
| String keyword = getValidationKeyword(property, valueSetConstraintStereotype); |
| String id = null; |
| String name = null; |
| String version = null; |
| BindingKind binding = null; |
| |
| String xref = null; |
| String xrefFormat = ""; |
| boolean showXref = false; |
| |
| if (valueSetConstraint != null) { |
| if (valueSetConstraint.getReference() != null) { |
| ValueSetVersion valueSetVersion = valueSetConstraint.getReference(); |
| id = valueSetVersion.getIdentifier(); |
| name = valueSetVersion.getEnumerationName(); |
| version = valueSetVersion.getVersion(); |
| binding = valueSetVersion.getBinding(); |
| |
| if (valueSetVersion.getBase_Enumeration() != null) { |
| xref = computeTerminologyXref(property.getClass_(), valueSetVersion.getBase_Enumeration()); |
| } |
| showXref = markup && (xref != null); |
| xrefFormat = showXref && xref.endsWith(".html") |
| ? "format=\"html\" " |
| : ""; |
| } else { |
| id = valueSetConstraint.getIdentifier(); |
| name = valueSetConstraint.getName(); |
| version = valueSetConstraint.getVersion(); |
| binding = valueSetConstraint.getBinding(); |
| } |
| } |
| |
| StringBuffer message = new StringBuffer(); |
| |
| // capture and return proper xml binding message based on mandatory or not |
| message.append(mandatoryOrNotMessage(property)); |
| |
| if (keyword != null) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| message.append(" be"); |
| } else { |
| message.append("is"); |
| } |
| message.append(" selected from ValueSet"); |
| |
| message.append( |
| markup |
| ? "<tt>" |
| : ""); |
| if (name != null) { |
| message.append(" "); |
| message.append( |
| showXref |
| ? "<xref " + xrefFormat + "href=\"" + xref + "\">" |
| : ""); |
| message.append(name); |
| message.append( |
| showXref |
| ? "</xref>" |
| : ""); |
| } |
| if (id != null) { |
| message.append(" ").append(id); |
| } |
| message.append( |
| markup |
| ? "</tt>" |
| : ""); |
| |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(" ").append(binding.getName().toUpperCase()); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| if (BindingKind.STATIC == binding && version != null) { |
| message.append(" ").append(version); |
| } |
| |
| return message.toString(); |
| } |
| |
| public static String computeConformanceMessage(Constraint constraint, boolean markup) { |
| |
| LogicalConstraint logicConstraint = CDAProfileUtil.getLogicalConstraint(constraint); |
| if (logicConstraint != null) { |
| return computeLogicalConformanceMessage(constraint, logicConstraint, markup); |
| } else { |
| return computeCustomConformanceMessage(constraint, markup); |
| } |
| |
| } |
| |
| private static String computeCustomConformanceMessage(Constraint constraint, boolean markup) { |
| StringBuffer message = new StringBuffer(); |
| String strucTextBody = null; |
| String analysisBody = null; |
| Map<String, String> langBodyMap = new HashMap<String, String>(); |
| |
| CDAProfileUtil.getLogicalConstraint(constraint); |
| |
| ValueSpecification spec = constraint.getSpecification(); |
| if (spec instanceof OpaqueExpression) { |
| for (int i = 0; i < ((OpaqueExpression) spec).getLanguages().size(); i++) { |
| String lang = ((OpaqueExpression) spec).getLanguages().get(i); |
| String body = ((OpaqueExpression) spec).getBodies().get(i); |
| |
| if ("StrucText".equals(lang)) { |
| strucTextBody = body; |
| } else if ("Analysis".equals(lang)) { |
| analysisBody = body; |
| } else { |
| langBodyMap.put(lang, body); |
| } |
| } |
| } |
| |
| String displayBody = null; |
| if (strucTextBody != null && strucTextBody.trim().length() > 0) { |
| // TODO if markup, parse strucTextBody and insert DITA markup |
| displayBody = strucTextBody; |
| } else if (analysisBody != null && analysisBody.trim().length() > 0) { |
| Boolean ditaEnabled = false; |
| try { |
| Stereotype stereotype = CDAProfileUtil.getAppliedCDAStereotype( |
| constraint, ICDAProfileConstants.CONSTRAINT_VALIDATION); |
| ditaEnabled = (Boolean) constraint.getValue(stereotype, ICDAProfileConstants.CONSTRAINT_DITA_ENABLED); |
| } catch (IllegalArgumentException e) { /* Swallow this */ |
| } |
| if (markup && !ditaEnabled) { |
| // escape non-dita markup in analysis text |
| displayBody = escapeMarkupCharacters(analysisBody); |
| // change severity words to bold text |
| displayBody = replaceSeverityWithBold(displayBody); |
| } else { |
| displayBody = analysisBody; |
| } |
| } |
| |
| if (displayBody == null) { |
| List<Stereotype> stereotypes = constraint.getAppliedStereotypes(); |
| if (stereotypes.isEmpty()) { |
| // This should never happen but in case it does we deal with it appropriately |
| // by bypassing custom constraint message additions |
| return ""; |
| } |
| } |
| |
| if (!markup) { |
| message.append(getPrefixedSplitName(constraint.getContext())).append(" "); |
| } |
| |
| if (displayBody == null || !containsSeverityWord(displayBody)) { |
| String keyword = getValidationKeyword(constraint); |
| if (keyword == null) { |
| keyword = "SHALL"; |
| } |
| |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| message.append(" satisfy: "); |
| } |
| |
| if (displayBody == null) { |
| message.append(constraint.getName()); |
| } else { |
| message.append(displayBody); |
| } |
| appendConformanceRuleIds(constraint, message, markup); |
| |
| if (!markup) { |
| // remove line feeds |
| int index; |
| while ((index = message.indexOf("\r")) >= 0) { |
| message.deleteCharAt(index); |
| } |
| while ((index = message.indexOf("\n")) >= 0) { |
| message.deleteCharAt(index); |
| if (message.charAt(index) != ' ') { |
| message.insert(index, " "); |
| } |
| } |
| } |
| |
| return message.toString(); |
| } |
| |
| private static String computeLogicalConformanceMessage(Constraint constraint, LogicalConstraint logicConstraint, |
| boolean markup) { |
| StringBuffer message = new StringBuffer(); |
| |
| logicConstraint.getMessage(); |
| |
| String keyword = getValidationKeyword(constraint); |
| if (keyword == null) { |
| keyword = "SHALL"; |
| } |
| |
| // Wording for IFTHEN - IF xxx then SHALL yyy |
| if (!logicConstraint.getOperation().equals(LogicalOperator.IFTHEN)) { |
| message.append( |
| markup |
| ? "<b>" |
| : ""); |
| message.append(keyword); |
| message.append( |
| markup |
| ? "</b>" |
| : ""); |
| |
| } |
| |
| switch (logicConstraint.getOperation()) { |
| case XOR: |
| message.append(" contain one and only one of the following "); |
| break; |
| case AND: |
| message.append(" contain all of the following "); |
| break; |
| case OR: |
| message.append(" contain one or more of the following "); |
| case IFTHEN: |
| message.append("if "); |
| break; |
| case NOTBOTH: |
| message.append(" contain zero or one of the following but not both "); |
| break; |
| default: |
| message.append(" satisfy the following "); |
| break; |
| } |
| |
| if (logicConstraint.getOperation().equals(LogicalOperator.IFTHEN) && |
| constraint.getConstrainedElements().size() == 2) { |
| |
| String propertyKeyword = getValidationKeyword(constraint.getConstrainedElements().get(0)); |
| |
| String conformanceMessage = computeConformanceMessage(constraint.getConstrainedElements().get(0), markup); |
| if (propertyKeyword != null) { |
| conformanceMessage = conformanceMessage.replace( |
| "<b>" + propertyKeyword + "</b> contain", "there is contained"); |
| conformanceMessage = conformanceMessage.replace(propertyKeyword, ""); |
| message.append(conformanceMessage); |
| } else { |
| message.append(conformanceMessage); |
| } |
| |
| message.append(" then it ").append( |
| markup |
| ? "<lines>" |
| : "").append( |
| markup |
| ? "<b>" |
| : "").append(keyword).append( |
| markup |
| ? "</b> " |
| : " "); |
| |
| message.append(computeConformanceMessage(constraint.getConstrainedElements().get(1), markup)); |
| message.append( |
| markup |
| ? "</lines>" |
| : ""); |
| |
| } else { |
| if (markup) { |
| message.append("<ol>"); |
| } |
| for (Element element : constraint.getConstrainedElements()) { |
| message.append(LI[0]); |
| message.append(computeConformanceMessage(element, markup)); |
| String propertyRules = ""; |
| if (element instanceof Property) { |
| Property property = (Property) element; |
| propertyRules = appendPropertyRules(message, property); |
| } |
| if (propertyRules.length() > 0) { |
| message.append(propertyRules); |
| } else if (element instanceof Property) { |
| String computeAssociationConstraintsMsg = computeAssociationConstraints((Property) element, markup); |
| if (!computeAssociationConstraintsMsg.isEmpty()) { |
| message.append("<ol>"); |
| message.append(computeAssociationConstraintsMsg); |
| message.append("</ol>"); |
| } |
| } |
| message.append(LI[1]); |
| } |
| if (markup) { |
| message.append("</ol>"); |
| } |
| } |
| |
| appendConformanceRuleIds(constraint, message, markup); |
| |
| return message.toString(); |
| } |
| |
| /** |
| * @param message |
| * @param property |
| */ |
| private static String appendPropertyRules(StringBuffer message, Property property) { |
| Class umlClass = property.getClass_(); |
| |
| // categorize constraints by constrainedElement name |
| List<Constraint> unprocessedConstraints = new ArrayList<Constraint>(); |
| // propertyName -> constraints |
| Map<String, List<Constraint>> constraintMap = new HashMap<String, List<Constraint>>(); |
| // constraint -> sub-constraints |
| Map<Constraint, List<Constraint>> subConstraintMap = new HashMap<Constraint, List<Constraint>>(); |
| |
| for (Constraint constraint : umlClass.getOwnedRules()) { |
| unprocessedConstraints.add(constraint); |
| |
| for (Element element : constraint.getConstrainedElements()) { |
| if (CDAProfileUtil.getLogicalConstraint(constraint) == null) { |
| if (element instanceof Property) { |
| String name = ((Property) element).getName(); |
| List<Constraint> rules = constraintMap.get(name); |
| if (rules == null) { |
| rules = new ArrayList<Constraint>(); |
| constraintMap.put(name, rules); |
| } |
| rules.add(constraint); |
| } else if (element instanceof Constraint) { |
| Constraint subConstraint = (Constraint) element; |
| List<Constraint> rules = subConstraintMap.get(subConstraint); |
| if (rules == null) { |
| rules = new ArrayList<Constraint>(); |
| subConstraintMap.put(subConstraint, rules); |
| } |
| rules.add(constraint); |
| } |
| } |
| } |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| appendPropertyRules(sb, property, constraintMap, subConstraintMap, unprocessedConstraints, true, true); |
| return sb.toString(); |
| } |
| |
| private static boolean containsSeverityWord(String text) { |
| return text.indexOf("SHALL") >= 0 || text.indexOf("SHOULD") >= 0 || text.indexOf("MAY") >= 0; |
| } |
| |
| private static String replaceSeverityWithBold(String input) { |
| String output; |
| output = input.replaceAll("SHALL", "<b>SHALL</b>"); |
| output = output.replaceAll("SHOULD", "<b>SHOULD</b>"); |
| output = output.replaceAll("MAY", "<b>MAY</b>"); |
| |
| output = output.replaceAll("\\<b>SHALL\\</b> NOT", "<b>SHALL NOT</b>"); |
| output = output.replaceAll("\\<b>SHOULD\\</b> NOT", "<b>SHOULD NOT</b>"); |
| |
| return output; |
| } |
| |
| /** |
| * FindResourcesByNameVisitor searches the resource for resources of a particular name |
| * You would think there was a method for this already but i could not find it |
| * |
| * @author seanmuir |
| * |
| */ |
| public static class FindResourcesByNameVisitor implements IResourceVisitor { |
| |
| private String resourceName; |
| |
| private ArrayList<IResource> resources = new ArrayList<IResource>(); |
| |
| /** |
| * @return the resources |
| */ |
| public ArrayList<IResource> getResources() { |
| return resources; |
| } |
| |
| /** |
| * @param resourceName |
| */ |
| public FindResourcesByNameVisitor(String resourceName) { |
| super(); |
| this.resourceName = resourceName; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.resources.IResourceVisitor#visit(org.eclipse.core.resources.IResource) |
| */ |
| public boolean visit(IResource arg0) throws CoreException { |
| |
| if (resourceName != null && resourceName.equals(arg0.getName())) { |
| resources.add(arg0); |
| } |
| return true; |
| } |
| |
| } |
| |
| public static IProject getElementModelProject(Element element) { |
| try { |
| Package elementPackage = UMLUtil.getTopPackage(element); |
| if (elementPackage != null && elementPackage.eResource() != null) { |
| FindResourcesByNameVisitor visitor = new FindResourcesByNameVisitor( |
| elementPackage.eResource().getURI().lastSegment()); |
| |
| IWorkspace iw = org.eclipse.core.resources.ResourcesPlugin.getWorkspace(); |
| |
| iw.getRoot().accept(visitor); |
| |
| if (!visitor.getResources().isEmpty()) { |
| return visitor.getResources().get(0).getProject(); |
| } |
| } |
| } catch (CoreException e) { |
| // If there is an issue with the workspace - return null |
| } |
| return null; |
| } |
| |
| public static IProject getModelDocProject(IProject modelProject) { |
| |
| if (modelProject != null && modelProject.exists()) { |
| return org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot().getProject( |
| modelProject.getName().replace(".model", ".doc")); |
| } |
| return null; |
| |
| } |
| |
| /** |
| * computeXref returns the XREF for DITA publication |
| * |
| * TODO Refactor and move out of model util |
| * |
| * @param source |
| * @param target |
| * @return |
| */ |
| public static String computeXref(Element source, Classifier target) { |
| if (target == null) { |
| return null; |
| } |
| if (target instanceof Enumeration) { |
| return computeTerminologyXref(source, (Enumeration) target); |
| } |
| |
| if (UMLUtil.isSameProject(source, target)) { |
| return "../" + normalizeCodeName(target.getName()) + ".dita"; |
| } |
| |
| if (target instanceof Class && CDAModelUtil.isPublishSeperately((Class) target)) { |
| return "../" + normalizeCodeName(target.getName()) + ".dita"; |
| } |
| |
| // If the model project is available (should be) and the dita content is part of the doc project |
| if (!isCDAModel(target)) { |
| IProject sourceProject = getElementModelProject(source); |
| |
| sourceProject = getModelDocProject(sourceProject); |
| |
| IProject targetProject = getElementModelProject(target); |
| |
| targetProject = getModelDocProject(targetProject); |
| |
| if (targetProject != null && sourceProject != null) { |
| |
| IPath projectPath = new Path("/dita/classes/" + targetProject.getName()); |
| |
| IFolder referenceDitaFolder = sourceProject.getFolder(projectPath); |
| if (referenceDitaFolder.exists()) { |
| return "../" + targetProject.getName() + "/classes/" + normalizeCodeName(target.getName()) + |
| ".dita"; |
| } |
| } |
| |
| String pathFolder = "classes"; |
| String basePackage = ""; |
| String prefix = ""; |
| |
| String packageName = target.getNearestPackage() != null |
| ? target.getNearestPackage().getName() |
| : ""; |
| if (RIMModelUtil.RIM_PACKAGE_NAME.equals(packageName)) { |
| basePackage = "org.openhealthtools.mdht.uml.hl7.rim"; |
| } else if (CDA_PACKAGE_NAME.equals(packageName)) { |
| basePackage = "org.openhealthtools.mdht.uml.cda"; |
| } else { |
| basePackage = getModelBasePackage(target); |
| prefix = getModelNamespacePrefix(target); |
| } |
| |
| if (basePackage == null || basePackage.trim().length() == 0) { |
| basePackage = "org.openhealthtools.mdht.uml.cda"; |
| } |
| if (prefix != null && prefix.trim().length() > 0) { |
| prefix += "."; |
| } |
| |
| return INFOCENTER_URL + "/topic/" + basePackage + "." + prefix + "doc/" + pathFolder + "/" + |
| normalizeCodeName(target.getName()) + ".html"; |
| } |
| |
| return null; |
| } |
| |
| protected static String computeTerminologyXref(Element source, Enumeration target) { |
| String href = null; |
| if (UMLUtil.isSameProject(source, target)) { |
| href = "../../terminology/" + normalizeCodeName(target.getName()) + ".dita"; |
| } |
| return href; |
| } |
| |
| public static Property getNavigableEnd(Association association) { |
| Property navigableEnd = null; |
| for (Property end : association.getMemberEnds()) { |
| if (end.isNavigable()) { |
| if (navigableEnd != null) { |
| return null; // multiple navigable ends |
| } |
| navigableEnd = end; |
| } |
| } |
| |
| return navigableEnd; |
| } |
| |
| /** |
| * getExtensionNamespace returns the name space from a extension package in the CDA model |
| * |
| * @param type |
| * @return |
| */ |
| private static String getExtensionNamespace(Type type) { |
| String nameSpace = null; |
| |
| if (type != null && type.getNearestPackage() != null && |
| !CDA_PACKAGE_NAME.equals(type.getNearestPackage().getName())) { |
| Stereotype ecoreStereotype = type.getNearestPackage().getAppliedStereotype(EPACKAGE); |
| if (ecoreStereotype != null) { |
| Object object = type.getNearestPackage().getValue(ecoreStereotype, NSPREFIX); |
| if (object instanceof String) { |
| nameSpace = (String) object; |
| } |
| } |
| } |
| return nameSpace; |
| } |
| |
| public static String getNameSpacePrefix(Class cdaSourceClass) { |
| if (cdaSourceClass != null && cdaSourceClass.getPackage() != null && |
| !CDA_PACKAGE_NAME.equals(cdaSourceClass.getPackage().getName())) { |
| Stereotype ecoreStereotype = cdaSourceClass.getPackage().getAppliedStereotype(EPACKAGE); |
| if (ecoreStereotype != null) { |
| Object object = cdaSourceClass.getPackage().getValue(ecoreStereotype, NSPREFIX); |
| if (object instanceof String) { |
| return (String) object; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * getCDAElementName - Returns the CDA Element name as a string |
| * |
| * @TODO Refactor to use org.openhealthtools.mdht.uml.transform.ecore.TransformAbstract.getInitialProperty(Property) |
| * |
| * Currently walk the redefines to see if we can match the CDA property using the name and type |
| * If none found - for backwards compatibility we look for a property in the base class with a matching type which is potential error prone |
| * If none still - leverage the getassociation |
| * |
| * @param property |
| * @return |
| */ |
| public static String getCDAElementName(Property property) { |
| String elementName = null; |
| |
| if (property.getType() instanceof Class) { |
| Class cdaSourceClass = getCDAClass(property.getClass_()); |
| if (cdaSourceClass != null) { |
| |
| // First check for definitions |
| for (Property redefinedProperty : property.getRedefinedProperties()) { |
| // This will never succeed for associations, does not include ActRelationship |
| |
| if (redefinedProperty.getType() != null) { |
| |
| Property cdaProperty = cdaSourceClass.getOwnedAttribute( |
| redefinedProperty.getName(), getCDAClass((Classifier) redefinedProperty.getType())); |
| if (cdaProperty != null && cdaProperty.getName() != null) { |
| String modelPrefix = getExtensionNamespace(cdaProperty.getType()); |
| elementName = !StringUtils.isEmpty(modelPrefix) |
| ? modelPrefix + ":" + cdaProperty.getName() |
| : cdaProperty.getName(); |
| break; |
| } |
| } |
| |
| } |
| |
| // Next check using property type and name |
| if (elementName == null) { |
| Property cdaProperty = cdaSourceClass.getOwnedAttribute( |
| property.getName(), getCDAClass((Classifier) property.getType())); |
| if (cdaProperty != null && cdaProperty.getName() != null) { |
| String modelPrefix = getExtensionNamespace(cdaProperty.getType()); |
| elementName = !StringUtils.isEmpty(modelPrefix) |
| ? modelPrefix + ":" + cdaProperty.getName() |
| : cdaProperty.getName(); |
| } |
| |
| } |
| |
| // Ultimately use original logic for backwards compatibility |
| if (elementName == null) { |
| Property cdaProperty = cdaSourceClass.getOwnedAttribute( |
| null, getCDAClass((Classifier) property.getType())); |
| if (cdaProperty != null && cdaProperty.getName() != null) { |
| String modelPrefix = getExtensionNamespace(cdaProperty.getType()); |
| elementName = !StringUtils.isEmpty(modelPrefix) |
| ? modelPrefix + ":" + cdaProperty.getName() |
| : cdaProperty.getName(); |
| } |
| |
| } |
| |
| } |
| } |
| |
| // look for CDA association class element name, e.g. "component" |
| if (elementName == null) { |
| elementName = getCDAAssociationElementName(property); |
| } |
| |
| if (elementName == null) { |
| elementName = property.getName(); |
| } |
| |
| return elementName; |
| } |
| |
| public static String getCDAAssociationElementName(Property property) { |
| Class cdaSourceClass = getCDAClass(property.getClass_()); |
| Class endType = (property.getType() instanceof Class) |
| ? (Class) property.getType() |
| : null; |
| Class cdaTargetClass = endType != null |
| ? getCDAClass(endType) |
| : null; |
| |
| // This is incomplete determination of XML element name, but same logic as used in model transform |
| String elementName = null; |
| if (cdaSourceClass == null) { |
| elementName = property.getName(); |
| } else if ("ClinicalDocument".equals(cdaSourceClass.getName()) && |
| (CDAModelUtil.isSection(cdaTargetClass) || CDAModelUtil.isClinicalStatement(cdaTargetClass))) { |
| elementName = "component"; |
| } else if (CDAModelUtil.isSection(cdaSourceClass) && (CDAModelUtil.isSection(cdaTargetClass))) { |
| elementName = "component"; |
| } else if (CDAModelUtil.isSection(cdaSourceClass) && |
| (CDAModelUtil.isClinicalStatement(cdaTargetClass) || CDAModelUtil.isEntry(cdaTargetClass))) { |
| elementName = "entry"; |
| } else if (CDAModelUtil.isOrganizer(cdaSourceClass) && CDAModelUtil.isClinicalStatement(cdaTargetClass)) { |
| elementName = "component"; |
| } else if (CDAModelUtil.isClinicalStatement(cdaSourceClass) && |
| CDAModelUtil.isClinicalStatement(cdaTargetClass)) { |
| elementName = "entryRelationship"; |
| } else if (CDAModelUtil.isClinicalStatement(cdaSourceClass) && cdaTargetClass != null && |
| "ParticipantRole".equals(cdaTargetClass.getName())) { |
| elementName = "participant"; |
| } else if (CDAModelUtil.isClinicalStatement(cdaSourceClass) && cdaTargetClass != null && |
| "AssignedEntity".equals(cdaTargetClass.getName())) { |
| elementName = "performer"; |
| } |
| |
| return elementName; |
| } |
| |
| private static String getMultiplicityRange(Property property) { |
| |
| StringBuffer message = new StringBuffer(); |
| String lower = Integer.toString(property.getLower()); |
| String upper = property.getUpper() == -1 |
| ? "*" |
| : Integer.toString(property.getUpper()); |
| message.append(" [").append(lower).append("..").append(upper).append("]"); |
| return message.toString(); |
| } |
| |
| /* |
| * |
| * · 0..1 zero or one |
| * |
| * · 1..1 exactly one |
| * |
| * · 1..* at least one |
| * |
| * · 0..* zero or more |
| * |
| * · 1..n at least one and not more than n |
| */ |
| |
| private static String getMultiplicityText(Property property) { |
| |
| StringBuffer message = new StringBuffer(); |
| |
| if (property.getLower() == property.getUpper()) { |
| // Upper and lower equal and not zero |
| if (property.getLower() != 0) { |
| message.append("exactly ").append(convertNumberToWords(property.getUpper())); |
| } |
| } else if (property.getLower() == 0) { |
| // Lower is zero |
| if (property.getUpper() == 0) { |
| |
| } else if (property.getUpper() == 1) { |
| message.append("zero or one"); |
| } else if (property.getUpper() == -1) { |
| message.append("zero or more"); |
| } else { |
| message.append("not more than " + convertNumberToWords(property.getUpper())); |
| } |
| } else if (property.getLower() == 1) { |
| // Lower is one |
| if (property.getUpper() == -1) { |
| message.append("at least one"); |
| } else { |
| message.append( |
| "at least " + convertNumberToWords(property.getLower()) + " and not more than " + |
| convertNumberToWords(property.getUpper())); |
| } |
| } else { |
| // Lower is greater then 1 |
| message.append("at least " + convertNumberToWords(property.getLower())); |
| if (property.getUpper() != -1) { |
| message.append(" and not more than " + convertNumberToWords(property.getUpper())); |
| } |
| } |
| return message.toString(); |
| } |
| |
| // This snippet may be used freely, as long as the authorship note remains in the source code. |
| |
| private static final String[] lowNames = { |
| "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", |
| "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; |
| |
| private static final String[] tensNames = { |
| "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; |
| |
| private static final String[] bigNames = { "thousand", "million", "billion" }; |
| |
| /** |
| * Converts an integer number into words (american english). |
| * |
| * @author Christian d'Heureuse, Inventec Informatik AG, Switzerland, www.source-code.biz |
| **/ |
| private static String convertNumberToWords(int n) { |
| if (n < 0) { |
| return "minus " + convertNumberToWords(-n); |
| } |
| if (n <= 999) { |
| return convert999(n); |
| } |
| String s = null; |
| int t = 0; |
| while (n > 0) { |
| if (n % 1000 != 0) { |
| String s2 = convert999(n % 1000); |
| if (t > 0) { |
| s2 = s2 + " " + bigNames[t - 1]; |
| } |
| if (s == null) { |
| s = s2; |
| } else { |
| s = s2 + ", " + s; |
| } |
| } |
| n /= 1000; |
| t++; |
| } |
| return s; |
| } |
| |
| // Range 0 to 999. |
| private static String convert999(int n) { |
| String s1 = lowNames[n / 100] + " hundred"; |
| String s2 = convert99(n % 100); |
| if (n <= 99) { |
| return s2; |
| } else if (n % 100 == 0) { |
| return s1; |
| } else { |
| return s1 + " " + s2; |
| } |
| } |
| |
| // Range 0 to 99. |
| private static String convert99(int n) { |
| if (n < 20) { |
| return lowNames[n]; |
| } |
| String s = tensNames[n / 10 - 2]; |
| if (n % 10 == 0) { |
| return s; |
| } |
| return s + "-" + lowNames[n % 10]; |
| } |
| |
| public static boolean isXMLAttribute(Property property) { |
| Property cdaProperty = getCDAProperty(property); |
| if (cdaProperty != null) { |
| Stereotype eAttribute = cdaProperty.getAppliedStereotype("Ecore::EAttribute"); |
| if (eAttribute != null) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private static String getMultiplicityRange(Class template) { |
| String templateId = null; |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype(template, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null && template.hasValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_MULTIPLICITY)) { |
| templateId = (String) template.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_MULTIPLICITY); |
| } else { |
| for (Classifier parent : template.getGenerals()) { |
| templateId = getMultiplicityRange((Class) parent); |
| if (templateId != null) { |
| break; |
| } |
| } |
| } |
| |
| return templateId; |
| } |
| |
| public String getPath(Class baseSourceClass, Class targetClass, Property sourceProperty) { |
| Property property = null; |
| for (Property rededfinedProperty : sourceProperty.getRedefinedProperties()) { |
| property = baseSourceClass.getOwnedAttribute( |
| rededfinedProperty.getName(), rededfinedProperty.getType(), true, null, false); |
| } |
| |
| if (property == null) { |
| property = baseSourceClass.getOwnedAttribute(null, targetClass, true, null, false); |
| } |
| // |
| // If not - walk the hierarchy and check for properties |
| if (property == null) { |
| for (Classifier c : targetClass.allParents()) { |
| property = baseSourceClass.getOwnedAttribute(null, c, true, null, false); |
| // isBaseModel(targetClass, c) |
| if ((property != null) || CDAModelUtil.isCDAModel(c)) { |
| break; |
| } |
| |
| } |
| } |
| |
| // If we still can not find - use the name - this is not optimal but CDA has some hop scotch hierarchies such as consumable and |
| // manufactured product |
| if (property == null) { |
| property = baseSourceClass.getOwnedAttribute(sourceProperty.getName(), null, true, null, false); |
| } |
| |
| if (property != null) { |
| return property.getName(); |
| } else { |
| return ""; |
| } |
| } |
| |
| public static String getQualifiedName(ValueSetConstraint vsc) { |
| |
| if (vsc.getBase_Property() != null) { |
| // String qname = vsc.getBase_Property().getQualifiedName(); |
| |
| // Stack<Element> owners = new Stack<Element>(); |
| Stack<Property> properties = new Stack<Property>(); |
| properties.push(vsc.getBase_Property()); |
| |
| Element owner = vsc.getBase_Property().getOwner(); |
| if (owner == null) { |
| return "INVALID " + EcoreUtil.getID(vsc); |
| } |
| while (owner.getOwner() instanceof Class) { |
| |
| Class c = (Class) owner.getOwner(); |
| properties.push(c.getAttribute(null, (Type) owner)); |
| |
| if (owner.getOwner() == null) { |
| break; |
| } |
| owner = owner.getOwner(); |
| // owners.push(owner); |
| } |
| |
| ; |
| |
| StringBuffer qualifiedName = new StringBuffer(); |
| for (Property p : properties) { |
| if (p != null) { |
| qualifiedName.insert(0, "/"); |
| qualifiedName.insert(0, p.getName()); |
| } else { |
| qualifiedName.insert(0, "/"); |
| qualifiedName.insert(0, "NULL PROPERTY"); |
| } |
| } |
| qualifiedName.insert(0, "/"); |
| qualifiedName.insert(0, CDAModelUtil.getCDAClass((Classifier) owner).getName()); |
| |
| return qualifiedName.toString(); |
| // for (Namespace ns : vsc.getBase_Property().allNamespaces()) { |
| // |
| // ns.get |
| // |
| // } |
| // |
| // String[] qnames = qname.split(""); |
| // |
| // Stack<String> aStack = new Stack<String>(); |
| |
| } |
| // if (vsc.getBase_Property() != null) { |
| // Property cdaProperty = CDAModelUtil.getCDAProperty(vsc.getBase_Property()); |
| // if (cdaProperty != null) { |
| // return cdaProperty.getQualifiedName().replaceFirst("cda::", "").replaceAll("::", "/"); |
| // } |
| // return vsc.getBase_Property().getQualifiedName(); |
| // } |
| |
| return "INVALID " + EcoreUtil.getID(vsc); |
| } |
| |
| public static String getValueSetName(ValueSetConstraint vsc) { |
| if (vsc.getReference() != null) { |
| if (!StringUtils.isEmpty(vsc.getReference().getFullName())) { |
| return vsc.getReference().getFullName(); |
| } else { |
| return vsc.getReference().getEnumerationName(); |
| } |
| } |
| return "INVALID"; |
| |
| } |
| |
| public static String getTemplateClassName(ValueSetConstraint vsc) { |
| |
| if (vsc.getBase_Property() != null && vsc.getBase_Property().getOwner() != null) { |
| Element owner = vsc.getBase_Property().getOwner(); |
| while (owner.getOwner() instanceof Class) { |
| owner = owner.getOwner(); |
| } |
| |
| return ((NamedElement) owner).getName(); |
| } |
| return "INVALID " + vsc.getRuleId().toString(); |
| } |
| |
| public static String getValidationKeyword(ValueSetConstraint vsc) { |
| String templateId = ""; |
| if (vsc.getBase_Property() != null) { |
| templateId = getValidationKeyword(vsc.getBase_Property()); |
| } |
| return templateId; |
| } |
| |
| public Boolean fileExists(String filepath) throws IOException { |
| java.nio.file.Path p = Paths.get(filepath); |
| p.normalize(); |
| File f = new File(filepath); |
| return f.exists(); |
| // if (!f.exists()) { |
| // f.createNewFile(); |
| // BufferedWriter bw = null; |
| // FileWriter fw = null; |
| // fw = new FileWriter(f.getAbsoluteFile(), true); |
| // bw = new BufferedWriter(fw); |
| // bw.write("Template ID Template Name Path OID Name Severity Combined1 Combined2"); |
| // bw.close(); |
| // |
| // } |
| // return ""; |
| } |
| |
| public String createHeader(String filepath) throws IOException { |
| java.nio.file.Path p = Paths.get(filepath); |
| p.normalize(); |
| File f = new File(filepath); |
| if (!f.exists()) { |
| f.createNewFile(); |
| BufferedWriter bw = null; |
| FileWriter fw = null; |
| fw = new FileWriter(f.getAbsoluteFile(), true); |
| bw = new BufferedWriter(fw); |
| bw.write("Template ID Template Name Path OID Name Severity Combined1 Combined2"); |
| bw.close(); |
| |
| } |
| return ""; |
| } |
| |
| // getTemplateId2 |
| |
| public static String getTemplateId2(ValueSetConstraint vsc) { |
| |
| // Class template |
| // System.out.println(template.getQualifiedName()); |
| // String templateId = ""; |
| if (vsc.getBase_Property() != null) { |
| |
| Element owner = vsc.getBase_Property().getOwner(); |
| while (owner instanceof Class) { |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype( |
| owner, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null) { |
| String result = (String) owner.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_TEMPLATE_ID); |
| String version = (String) owner.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_VERSION); |
| return result + (StringUtils.isEmpty(version) |
| ? "" |
| : "v" + version); |
| } |
| owner = owner.getOwner(); |
| } |
| |
| } |
| return "MISSING"; |
| } |
| |
| public static String getVersion(ValueSetConstraint vsc) { |
| |
| // Class template |
| // System.out.println(template.getQualifiedName()); |
| // String templateId = ""; |
| if (vsc.getBase_Property() != null) { |
| |
| Element owner = vsc.getBase_Property().getOwner(); |
| while (owner instanceof Class) { |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype( |
| owner, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null) { |
| // String result = (String) owner.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_TEMPLATE_ID); |
| String version = (String) owner.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_VERSION); |
| if (StringUtils.isEmpty(version)) { |
| return "1.2"; |
| } else { |
| return "2.1"; |
| } |
| // return result + (StringUtils.isEmpty(version) |
| // ? "" |
| // : "v" + version); |
| } |
| owner = owner.getOwner(); |
| } |
| |
| } |
| return "MISSING"; |
| } |
| |
| public static String getTemplateId(Class template) { |
| String templateId = null; |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype(template, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null) { |
| templateId = (String) template.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_TEMPLATE_ID); |
| } else { |
| for (Classifier parent : template.getGenerals()) { |
| templateId = getTemplateId((Class) parent); |
| if (templateId != null) { |
| break; |
| } |
| } |
| } |
| |
| return templateId; |
| } |
| |
| public static String getTemplateVersion(Class template) { |
| String templateVersion = null; |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype(template, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null) { |
| templateVersion = (String) template.getValue(hl7Template, ICDAProfileConstants.CDA_TEMPLATE_VERSION); |
| } else { |
| for (Classifier parent : template.getGenerals()) { |
| templateVersion = getTemplateId((Class) parent); |
| if (templateVersion != null) { |
| break; |
| } |
| } |
| } |
| |
| return templateVersion; |
| } |
| |
| public static String getAssigningAuthorityName(Class template) { |
| String assigningAuthorityName = null; |
| Stereotype hl7Template = CDAProfileUtil.getAppliedCDAStereotype(template, ICDAProfileConstants.CDA_TEMPLATE); |
| if (hl7Template != null) { |
| assigningAuthorityName = (String) template.getValue( |
| hl7Template, ICDAProfileConstants.CDA_TEMPLATE_ASSIGNING_AUTHORITY_NAME); |
| } else { |
| for (Classifier parent : template.getGenerals()) { |
| assigningAuthorityName = getAssigningAuthorityName((Class) parent); |
| if (assigningAuthorityName != null) { |
| break; |
| } |
| } |
| } |
| |
| return assigningAuthorityName; |
| } |
| |
| public static String getModelPrefix(Element element) { |
| String prefix = null; |
| Package thePackage = UMLUtil.getTopPackage(element); |
| if (thePackage != null) { |
| Stereotype codegenSupport = CDAProfileUtil.getAppliedCDAStereotype( |
| thePackage, ICDAProfileConstants.CODEGEN_SUPPORT); |
| if (codegenSupport != null) { |
| prefix = (String) thePackage.getValue(codegenSupport, ICDAProfileConstants.CODEGEN_SUPPORT_PREFIX); |
| } else if (CDA_PACKAGE_NAME.equals(thePackage.getName())) { |
| prefix = "CDA"; |
| } else if (RIMModelUtil.RIM_PACKAGE_NAME.equals(thePackage.getName())) { |
| prefix = "RIM"; |
| } |
| } |
| return prefix != null |
| ? prefix |
| : ""; |
| } |
| |
| public static String getModelNamespacePrefix(Element element) { |
| String prefix = null; |
| Package model = UMLUtil.getTopPackage(element); |
| Stereotype codegenSupport = CDAProfileUtil.getAppliedCDAStereotype(model, ICDAProfileConstants.CODEGEN_SUPPORT); |
| if (codegenSupport != null) { |
| prefix = (String) model.getValue(codegenSupport, ICDAProfileConstants.CODEGEN_SUPPORT_NS_PREFIX); |
| } |
| |
| return prefix; |
| } |
| |
| public static String getModelBasePackage(Element element) { |
| String basePackage = null; |
| Package model = UMLUtil.getTopPackage(element); |
| Stereotype codegenSupport = CDAProfileUtil.getAppliedCDAStereotype(model, ICDAProfileConstants.CODEGEN_SUPPORT); |
| if (codegenSupport != null) { |
| basePackage = (String) model.getValue(codegenSupport, ICDAProfileConstants.CODEGEN_SUPPORT_BASE_PACKAGE); |
| } |
| |
| return basePackage; |
| } |
| |
| public static String getEcorePackageURI(Element element) { |
| String nsURI = null; |
| Package model = UMLUtil.getTopPackage(element); |
| Stereotype codegenSupport = CDAProfileUtil.getAppliedCDAStereotype(model, ICDAProfileConstants.CODEGEN_SUPPORT); |
| if (codegenSupport != null) { |
| nsURI = (String) model.getValue(codegenSupport, ICDAProfileConstants.CODEGEN_SUPPORT_NS_URI); |
| } |
| if (nsURI == null) { |
| // for base models without codegenSupport |
| if (model.getName().equals("cda")) { |
| nsURI = "urn:hl7-org:v3"; |
| } else if (model.getName().equals("datatypes")) { |
| nsURI = "http://www.openhealthtools.org/mdht/uml/hl7/datatypes"; |
| } else if (model.getName().equals("vocab")) { |
| nsURI = "http://www.openhealthtools.org/mdht/uml/hl7/vocab"; |
| } |
| } |
| |
| return nsURI; |
| } |
| |
| public static String getPrefixedSplitName(NamedElement element) { |
| StringBuffer buffer = new StringBuffer(); |
| String modelPrefix = getModelPrefix(element); |
| if (modelPrefix != null && modelPrefix.length() > 0) { |
| buffer.append(modelPrefix).append(" "); |
| } |
| buffer.append(UMLUtil.splitName(element)); |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Returns a list conformance rule IDs. |
| */ |
| public static List<String> getConformanceRuleIdList(Element element) { |
| List<String> ruleIds = new ArrayList<String>(); |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype(element, ICDAProfileConstants.VALIDATION); |
| if (validationSupport != null) { |
| Validation validation = (Validation) element.getStereotypeApplication(validationSupport); |
| for (String ruleId : validation.getRuleId()) { |
| ruleIds.add(ruleId); |
| } |
| } |
| |
| return ruleIds; |
| } |
| |
| protected static void appendTerminologyConformanceRuleIds(Property property, StringBuffer message, boolean markup) { |
| String ruleIds = getTerminologyConformanceRuleIds(property); |
| if (ruleIds.length() > 0) { |
| message.append(" ("); |
| message.append(ruleIds); |
| message.append(")"); |
| } |
| } |
| |
| protected static void appendConformanceRuleIds(Property property, StringBuffer message, boolean markup) { |
| String ruleIds = getConformanceRuleIds(property); |
| if (ruleIds.length() > 0) { |
| message.append(" ("); |
| message.append(ruleIds); |
| message.append(")"); |
| } |
| } |
| |
| protected static void appendConformanceRuleIds(Association association, StringBuffer message, boolean markup) { |
| String ruleIds = getConformanceRuleIds(association); |
| if (ruleIds.length() > 0) { |
| message.append(" ("); |
| message.append(ruleIds); |
| message.append(")"); |
| } |
| } |
| |
| protected static void appendConformanceRuleIds(Element element, StringBuffer message, boolean markup) { |
| String ruleIds = getConformanceRuleIds(element); |
| if (ruleIds.length() > 0) { |
| message.append(" ("); |
| message.append(ruleIds); |
| message.append(")"); |
| } |
| } |
| |
| protected static void appendConformanceRuleIds(Element element, Stereotype stereotype, StringBuffer message, |
| boolean markup) { |
| String ruleIds = getConformanceRuleIds(element, stereotype); |
| if (ruleIds.length() > 0) { |
| message.append(" ("); |
| message.append(ruleIds); |
| message.append(")"); |
| } |
| } |
| |
| /** |
| * Returns a comma separated list of conformance rule IDs, or an empty string if no IDs. |
| */ |
| public static String getConformanceRuleIds(Property property) { |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype( |
| property, ICDAProfileConstants.PROPERTY_VALIDATION); |
| return getConformanceRuleIds(property, validationSupport); |
| } |
| |
| /** |
| * Returns a comma separated list of conformance rule IDs, or an empty string if no IDs. |
| */ |
| public static String getTerminologyConformanceRuleIds(Property property) { |
| Stereotype terminologyConstraint = getTerminologyConstraint(property); |
| return getConformanceRuleIds(property, terminologyConstraint); |
| } |
| |
| /** |
| * Returns a comma separated list of conformance rule IDs, or an empty string if no IDs. |
| */ |
| public static String getConformanceRuleIds(Association association) { |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype( |
| association, ICDAProfileConstants.ASSOCIATION_VALIDATION); |
| return getConformanceRuleIds(association, validationSupport); |
| } |
| |
| /** |
| * Returns a comma separated list of conformance rule IDs, or an empty string if no IDs. |
| */ |
| public static String getConformanceRuleIds(Element element) { |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype(element, ICDAProfileConstants.VALIDATION); |
| return getConformanceRuleIds(element, validationSupport); |
| } |
| |
| public static String getConformanceRuleIds(Element element, Stereotype validationSupport) { |
| StringBuffer ruleIdDisplay = new StringBuffer(); |
| if (validationSupport != null) { |
| Validation validation = (Validation) element.getStereotypeApplication(validationSupport); |
| for (String ruleId : validation.getRuleId()) { |
| if (ruleIdDisplay.length() > 0) { |
| ruleIdDisplay.append(", "); |
| } |
| ruleIdDisplay.append(ruleId); |
| } |
| } |
| |
| return ruleIdDisplay.toString(); |
| } |
| |
| public static Stereotype getTerminologyConstraint(Element element) { |
| Stereotype stereotype = CDAProfileUtil.getAppliedCDAStereotype( |
| element, ITermProfileConstants.CONCEPT_DOMAIN_CONSTRAINT); |
| if (stereotype == null) { |
| stereotype = CDAProfileUtil.getAppliedCDAStereotype(element, ITermProfileConstants.CODE_SYSTEM_CONSTRAINT); |
| } |
| if (stereotype == null) { |
| stereotype = CDAProfileUtil.getAppliedCDAStereotype(element, ITermProfileConstants.VALUE_SET_CONSTRAINT); |
| } |
| |
| return stereotype; |
| } |
| |
| public static boolean hasValidationSupport(Element element) { |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype(element, ICDAProfileConstants.VALIDATION); |
| return validationSupport != null; |
| } |
| |
| /** |
| * @deprecated Use {@link #getValidationSeverity(Property, String)} to get the severity for a specific validation stereotype. |
| * If necessary, this can be the abstract {@link ICDAProfileConstants#VALIDATION Validation} stereotype to get any available |
| * validation severity. |
| */ |
| @Deprecated |
| public static String getValidationSeverity(Property property) { |
| return getValidationSeverity(property, ICDAProfileConstants.PROPERTY_VALIDATION); |
| } |
| |
| public static String getValidationSeverity(Property property, String validationStereotypeName) { |
| Stereotype validationStereotype = CDAProfileUtil.getAppliedCDAStereotype(property, validationStereotypeName); |
| return getValidationSeverity(property, validationStereotype); |
| } |
| |
| public static String getValidationSeverity(Element element) { |
| // use first available validation stereotype |
| return getValidationSeverity(element, ICDAProfileConstants.VALIDATION); |
| } |
| |
| public static String getValidationSeverity(Element element, String validationStereotypeName) { |
| Stereotype validationStereotype = CDAProfileUtil.getAppliedCDAStereotype(element, validationStereotypeName); |
| return getValidationSeverity(element, validationStereotype); |
| } |
| |
| public static String getValidationSeverity(Element element, Stereotype validationStereotype) { |
| String severity = null; |
| |
| if ((validationStereotype != null) && CDAProfileUtil.isValidationStereotype(validationStereotype)) { |
| Object value = element.getValue(validationStereotype, ICDAProfileConstants.VALIDATION_SEVERITY); |
| if (value instanceof EnumerationLiteral) { |
| severity = ((EnumerationLiteral) value).getName(); |
| } else if (value instanceof Enumerator) { |
| severity = ((Enumerator) value).getName(); |
| } |
| } |
| |
| return severity; |
| } |
| |
| /** |
| * Get the value of the Negation Indicator, for the given element or return false |
| * |
| * @param element |
| * the element to check |
| * @param validationStereotype |
| * the applied stereotype |
| * |
| * @return true if the Negation indicator exists and is set to true, false for all other circumstances |
| */ |
| public static Boolean getValidationNegation(Element element, Stereotype validationStereotype) { |
| Boolean negation = false; |
| |
| if ((validationStereotype != null) && CDAProfileUtil.isValidationStereotype(validationStereotype)) { |
| |
| Object value = element.getValue(validationStereotype, ICDAProfileConstants.VALIDATION_NEGATION_INDICATOR); |
| if (value instanceof Boolean) { |
| negation = (Boolean) value; |
| } |
| |
| } |
| |
| return negation; |
| } |
| |
| public static String getValidationKeywordWithPropertyRange(Property property) { |
| String keyword = getValidationKeyword(property); |
| return addShallNot(keyword, property); |
| } |
| |
| public static String getValidationKeyword(Property property) { |
| String severity = getValidationSeverity(property, ICDAProfileConstants.PROPERTY_VALIDATION); |
| Stereotype validationStereotype = CDAProfileUtil.getAppliedCDAStereotype( |
| property, ICDAProfileConstants.PROPERTY_VALIDATION); |
| |
| // check if the severity has been negated, as in SHOULD NOT |
| boolean isNegated = getValidationNegation(property, validationStereotype); |
| |
| if (severity == null) { |
| // get other validation stereotype, usually for terminology |
| severity = getValidationSeverity((Element) property); |
| } |
| |
| String keyword = getValidationKeyword(severity); |
| if (isNegated) { |
| keyword += " NOT"; |
| } |
| |
| return keyword; |
| } |
| |
| public static String getValidationKeywordWithPropertyRange(Element element, Property property) { |
| // use first available validation stereotype |
| String keyword = getValidationKeyword(element); |
| keyword = addShallNot(keyword, property); |
| return keyword; |
| } |
| |
| private static String addShallNot(String keyword, Property property) { |
| if (property.getLower() == 0 && property.getUpper() == 0 && ("SHALL".equals(keyword))) { |
| keyword += " NOT"; |
| } |
| return keyword; |
| } |
| |
| public static String getValidationKeyword(Element element) { |
| // use first available validation stereotype |
| String severity = getValidationSeverity(element); |
| |
| return getValidationKeyword(severity); |
| } |
| |
| public static String getValidationKeyword(Element element, Stereotype validationStereotype) { |
| String severity = getValidationSeverity(element, validationStereotype); |
| return getValidationKeyword(severity); |
| } |
| |
| private static String getValidationKeyword(String severity) { |
| String keyword = null; |
| if (SEVERITY_INFO.equals(severity)) { |
| keyword = "MAY"; |
| } else if (SEVERITY_WARNING.equals(severity)) { |
| keyword = "SHOULD"; |
| } else if (SEVERITY_ERROR.equals(severity)) { |
| keyword = "SHALL"; |
| } |
| |
| return keyword; |
| } |
| |
| public static void setValidationMessage(Element constrainedElement, String message) { |
| Stereotype validationSupport = CDAProfileUtil.getAppliedCDAStereotype( |
| constrainedElement, ICDAProfileConstants.VALIDATION); |
| if (validationSupport != null) { |
| constrainedElement.setValue(validationSupport, ICDAProfileConstants.VALIDATION_MESSAGE, message); |
| } |
| } |
| |
| protected static String getLiteralValue(Element element, Stereotype stereotype, String propertyName) { |
| Object value = element.getValue(stereotype, propertyName); |
| String name = null; |
| if (value instanceof EnumerationLiteral) { |
| name = ((EnumerationLiteral) value).getName(); |
| } else if (value instanceof Enumerator) { |
| name = ((Enumerator) value).getName(); |
| } |
| |
| return name; |
| } |
| |
| protected static String getLiteralValueLabel(Element element, Stereotype stereotype, String propertyName, |
| Enumeration umlEnumeration) { |
| Object value = element.getValue(stereotype, propertyName); |
| String name = null; |
| if (value instanceof EnumerationLiteral) { |
| name = ((EnumerationLiteral) value).getLabel(); |
| } else if (value instanceof Enumerator) { |
| name = ((Enumerator) value).getName(); |
| |
| if (umlEnumeration != null) { |
| name = umlEnumeration.getOwnedLiteral(name).getLabel(); |
| } |
| } |
| |
| return name; |
| } |
| |
| public static String fixNonXMLCharacters(String text) { |
| if (text == null) { |
| return null; |
| } |
| |
| StringBuffer newText = new StringBuffer(); |
| for (int i = 0; i < text.length(); i++) { |
| // test for unicode characters from copy/paste of MS Word text |
| if (text.charAt(i) == '\u201D') { |
| newText.append("\""); |
| } else if (text.charAt(i) == '\u201C') { |
| newText.append("\""); |
| } else if (text.charAt(i) == '\u2019') { |
| newText.append("'"); |
| } else if (text.charAt(i) == '\u2018') { |
| newText.append("'"); |
| } else { |
| newText.append(text.charAt(i)); |
| } |
| } |
| /* |
| * 
 |
| * |
| */ |
| |
| return newText.toString().replace("</p>", "").replace("<p>", "").replace("<p>", "").replace("</p>", ""); |
| } |
| |
| public static String escapeMarkupCharacters(String text) { |
| if (text == null) { |
| return null; |
| } |
| text = fixNonXMLCharacters(text); |
| |
| StringBuffer newText = new StringBuffer(); |
| for (int i = 0; i < text.length(); i++) { |
| if (text.charAt(i) == '<') { |
| newText.append("<"); |
| } else if (text.charAt(i) == '>') { |
| newText.append(">"); |
| } else { |
| newText.append(text.charAt(i)); |
| } |
| } |
| |
| return newText.toString(); |
| } |
| |
| public static String normalizeCodeName(String name) { |
| if (StringUtils.isEmpty(name)) { |
| return "ERROR"; |
| } |
| String result = ""; |
| |
| String[] parts = name.split(" "); |
| for (String part : parts) { |
| result += part.substring(0, 1).toUpperCase() + part.substring(1); |
| } |
| result = UML2Util.getValidJavaIdentifier(result); |
| return result; |
| } |
| |
| private static String mandatoryOrNotMessage(Property curProperty) { |
| // capture if allows nullFlavor or not |
| boolean mandatory = CDAProfileUtil.isMandatory(curProperty); |
| // mandatory implies nullFlavor is NOT allowed ", where the @code " |
| // non-mandatory implies nullFlavor is allowed ", which " |
| // return the proper message based on mandatory or not |
| return mandatory |
| ? ", where the @code " |
| : ", which "; |
| } |
| |
| public static boolean isInlineClass(Class _class) { |
| Inline inline = CDAProfileUtil.getInline(_class); |
| |
| if (inline != null) { |
| return true; |
| } |
| |
| if (_class.getOwner() instanceof Class) { |
| return true; |
| } |
| |
| for (Comment comment : _class.getOwnedComments()) { |
| if (comment.getBody().startsWith("INLINE")) { |
| return true; |
| } |
| } |
| |
| return false; |
| |
| } |
| |
| /** |
| * For any given property, correctly check all Inline stereotypes and negation indicators |
| * to determine if this property should display inline. |
| * |
| * @param endType |
| * any UML Class |
| * @return true iff the property has been correctly stereotyped to display inline, false otherwise |
| */ |
| public static boolean isDisplayInline(Class endType) { |
| if (endType == null) { |
| return false; |
| } |
| |
| if (CDAModelUtil.isInlineClass(endType) && !CDAModelUtil.isPublishSeperately(endType)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * A convenience wrapper around isDisplayInline(Class endType) for properties. |
| * |
| * For any given property, correctly check all Inline stereotypes and negation indicators |
| * to determine if this property should display inline. |
| * |
| * @param clazz |
| * any UML Class |
| * @return true iff the property has been correctly stereotyped to display inline, false otherwise |
| */ |
| public static boolean isDisplayInline(Property property) { |
| Class endType = (property.getType() instanceof Class) |
| ? (Class) property.getType() |
| : null; |
| |
| return isDisplayInline(endType); |
| } |
| |
| public static String getInlineFilter(Class inlineClass) { |
| |
| Inline inline = CDAProfileUtil.getInline(inlineClass); |
| |
| if (inline != null) { |
| return inline.getFilter() != null |
| ? inline.getFilter() |
| : ""; |
| |
| } |
| |
| String filter = ""; |
| for (Comment comment : inlineClass.getOwnedComments()) { |
| if (comment.getBody().startsWith("INLINE&")) { |
| String[] temp = comment.getBody().split("&"); |
| if (temp.length == 2) { |
| filter = String.format("->select(%s)", temp[1]); |
| } |
| break; |
| } |
| } |
| |
| if ("".equals(filter)) { |
| // search hierarchy |
| for (Classifier next : inlineClass.getGenerals()) { |
| if (next instanceof Class) { |
| filter = getInlineFilter((Class) next); |
| if (!"".equals(filter)) { |
| break; |
| } |
| } |
| } |
| } |
| |
| return filter; |
| } |
| |
| public static boolean isPublishSeperately(Class _class) { |
| |
| boolean publish = false; |
| Stereotype stereotype = CDAProfileUtil.getAppliedCDAStereotype(_class, ICDAProfileConstants.INLINE); |
| |
| if (stereotype != null) { |
| Boolean result = (Boolean) _class.getValue(stereotype, "publishSeperately"); |
| publish = result.booleanValue(); |
| } |
| |
| return publish; |
| |
| } |
| |
| } |