| /******************************************************************************* |
| * 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) { |
| } |
| } |
| } |
| } |