blob: 638e2964f7a9891d03ad5b61b047a314d6661cf3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sean Muir (JKM Software) - updated registry to support threading
* Dave Carlson (XMLmodeling.com) - added document class registry
* Christian W. Damus - refactored CDAResource, CDAUtil, CDARegistry on the new flexible XML resource (artf3367)
* Rama Ramakrishnan - If a template class has already been loaded, ignore further occurences (artf3397)
*
*******************************************************************************/
package org.eclipse.mdht.uml.cda.internal.resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.mdht.emf.runtime.resource.XSITypeProvider;
import org.eclipse.mdht.uml.cda.CDAPackage;
import org.eclipse.mdht.uml.cda.RegistryDelegate;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* The pluggable <tt>xsi:type</tt> provider for CDA, which finds the appropriate <tt>xsi:type</tt> {@link EClass}es by CDA Template ID.
* The CDA document {@code EClass} may optionally be specified in cases where look-up of the document type by template ID is to be averted.
*/
public class CDAXSITypeProvider implements XSITypeProvider {
protected static final String CDA_ANNOTATION_SOURCE = "http://www.openhealthtools.org/mdht/uml/cda/annotation";
protected static final String TEMPLATE_ID_ROOT = "templateId.root";
protected static final String TEMPLATE_ID_EXTENSION = "templateId.extension";
private static final String CONTEXT_DEPENDENT = "contextDependent";
private static final String REGISTRY_DELEGATE = "registryDelegate";
/** key=qualified name, value=EClass */
private final Map<String, EClass> allDocumentClasses = new java.util.HashMap<String, EClass>();
/** key=templateId, value=EClass */
private final Map<String, EClass> classes = new java.util.HashMap<String, EClass>();
private final Map<EClass, RegistryDelegate> delegates = new java.util.HashMap<EClass, RegistryDelegate>();
private final EClass documentClass;
private String preffered = null;
/**
* Initializes me without a document {@link EClass}, so that I will discover it by template ID.
*/
public CDAXSITypeProvider() {
this((EClass) null);
}
/**
* Initializes me with the {@link EClass} that I should use to instantiate the CDA document, instead of discovering it based on the template ID.
*
* @param documentClass my forced document class, or {@code null} to discover the document class by template ID
*/
public CDAXSITypeProvider(EClass documentClass) {
super();
load();
this.documentClass = documentClass;
}
/**
* Initializes me with the qualified name (e.g. <tt>ccd::ContinuityOfCareDocument</tt>) of the {@link EClass} to use
* as the document root.
*
* @param documentClassQName my forced document class's qualified name, or {@code null} to discover the document class by template ID
*/
public CDAXSITypeProvider(String documentClassQName) {
super();
load();
this.documentClass = (documentClassQName == null)
? null
: allDocumentClasses.get(documentClassQName);
}
public EClass getXSIType(Element element) {
EClass result = null;
int last = 0;
NodeList nodeList = element.getChildNodes();
ArrayList<EClass> potentialTemplates = new ArrayList<EClass>();
EClass type = getType(element);
/*
* Collect the potential templates
*/
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i) instanceof Element) {
Element e = (Element) nodeList.item(i);
if ("templateId".equals(e.getLocalName())) {
EClass eClass = getEClass(
e.getAttributeNS(null, "root"), e.getAttributeNS(null, "extension"), element);
if (eClass != null) {
potentialTemplates.add(eClass);
}
}
}
}
// if we found some
if (!potentialTemplates.isEmpty()) {
// if one - use it
if (potentialTemplates.size() == 1) {
result = potentialTemplates.get(0);
} else {
// else if we have identified an initial URI to use for the cases of mixed content
if (preffered != null) {
for (EClass potentialTemplate : potentialTemplates) {
// check if the potentialTemplate is in the uri
if (preffered.equals(potentialTemplate.getEPackage().getNsURI())) {
if ((potentialTemplate != null) && !potentialTemplate.isAbstract()
&& conformsTo(potentialTemplate, type)
&& (potentialTemplate.getEAllSuperTypes().size() > last)) {
result = potentialTemplate;
last = potentialTemplate.getEAllSuperTypes().size();
}
}
}
// if we did not find one in the preferred namespec
if (result == null) {
for (EClass potentialTemplate : potentialTemplates) {
if ((potentialTemplate != null) && !potentialTemplate.isAbstract()
&& conformsTo(potentialTemplate, type)
&& (potentialTemplate.getEAllSuperTypes().size() > last)) {
result = potentialTemplate;
last = potentialTemplate.getEAllSuperTypes().size();
}
}
}
} else {
// else check for best fit
for (EClass potentialTemplate : potentialTemplates) {
if ((potentialTemplate != null) && !potentialTemplate.isAbstract()
&& conformsTo(potentialTemplate, type)
&& (potentialTemplate.getEAllSuperTypes().size() > last)) {
result = potentialTemplate;
last = potentialTemplate.getEAllSuperTypes().size();
}
}
}
}
}
if (result != null && element.getLocalName().equals("ClinicalDocument")) {
preffered = result.getEPackage().getNsURI();
}
return result;
}
private List<String> getPath(Element element) {
LinkedList<String> result = new LinkedList<String>();
result.addFirst(element.getLocalName());
Node parent = element.getParentNode();
while (parent instanceof Element) {
Element e = (Element) parent;
result.addFirst(e.getLocalName());
parent = e.getParentNode();
}
return result;
}
protected EClass getType(Element element) {
EClass eClass = CDAPackage.Literals.DOCUMENT_ROOT;
List<String> path = getPath(element);
for (String component : path) {
EStructuralFeature feature = getEStructuralFeature(eClass, component);
if (feature instanceof EReference) {
eClass = (EClass) feature.getEType();
} else {
return null;
}
}
return eClass;
}
private EStructuralFeature getEStructuralFeature(EClass eClass, String name) {
for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) {
if (name.equals(getName(feature))) {
return feature;
}
}
return null;
}
protected boolean conformsTo(EClass eClass, EClass type) {
if (eClass == null || type == null) {
return false;
}
return type.isSuperTypeOf(eClass);
}
private String getName(ENamedElement eNamedElement) {
String result = EcoreUtil.getAnnotation(eNamedElement, ExtendedMetaData.ANNOTATION_URI, "name");
if (result != null) {
return result;
}
return eNamedElement.getName();
}
protected EClass getEClass(String templateId, String versionId, Object context) {
if (documentClass != null && context instanceof Element
&& ((Element) context).getLocalName().equals("ClinicalDocument")) {
return documentClass;
}
String key = null;
if (!isEmpty(versionId)) {
key = templateId + "v" + versionId;
} else {
key = templateId;
}
EClass eClass = classes.get(key);
if (delegates.containsKey(eClass)) {
RegistryDelegate delegate = delegates.get(eClass);
eClass = delegate.getEClass(key, context);
}
return eClass;
}
public Map<String, EClass> getAllDocumentClasses() {
return allDocumentClasses;
}
/**
* If not null, use this EClass as the document root.
*/
public EClass getDocumentClass() {
return documentClass;
}
// lifted from string utils
protected static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
private void load() {
EPackage.Registry registry = EPackage.Registry.INSTANCE;
EClass clinicalDocumentClass = (EClass) CDAPackage.eINSTANCE.getEClassifier("ClinicalDocument");
for (String key : registry.keySet().toArray(new String[registry.size()])) {
try {
EPackage ePackage = registry.getEPackage(key);
for (EClassifier eClassifier : ePackage.getEClassifiers()) {
try {
if (eClassifier instanceof EClass) {
if (clinicalDocumentClass != null
&& clinicalDocumentClass.isSuperTypeOf((EClass) eClassifier)) {
String qname = eClassifier.getEPackage().getNsPrefix() + "::" + eClassifier.getName();
allDocumentClasses.put(qname, (EClass) eClassifier);
}
String templateId = EcoreUtil.getAnnotation(
eClassifier, CDA_ANNOTATION_SOURCE, TEMPLATE_ID_ROOT);
if (!isEmpty(templateId)) {
String templateVersion = EcoreUtil.getAnnotation(
eClassifier, CDA_ANNOTATION_SOURCE, TEMPLATE_ID_EXTENSION);
if (!isEmpty(templateVersion)) {
templateId = templateId + "v" + templateVersion;
}
// Fix for artf3397 : Check if the templateId has already been resolved to an EClass
// If already exists in classes map, do not add to the map
if ((!(classes.keySet().contains(templateId)))) {
String contextDependent = EcoreUtil.getAnnotation(
eClassifier, CDA_ANNOTATION_SOURCE, CONTEXT_DEPENDENT);
if ("true".equals(contextDependent)) {
String registryDelegate = EcoreUtil.getAnnotation(
ePackage, CDA_ANNOTATION_SOURCE, REGISTRY_DELEGATE);
EClass eClass = (EClass) ePackage.getEClassifier(registryDelegate);
classes.put(templateId, eClass);
if (!delegates.containsKey(eClass)) {
delegates.put(eClass, (RegistryDelegate) EcoreUtil.create(eClass));
}
} else {
classes.put(templateId, (EClass) eClassifier);
}
}
}
}
} catch (Exception e) {
}
}
} catch (Exception e) {
}
}
}
}