All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openhealthtools.mdht.uml.cda.util.CDAUtil Maven / Gradle / Ivy

/*******************************************************************************
 * 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) - package loading, snippet generation
 *     David A Carlson (XMLmodeling.com) - various helper methods
 *     Christian W. Damus - flexible, pluggable instance initializers (artf3272)
 *     Rama Ramakrishnan - Added loadAs() convenience method for forcing to load 
 *                          with a predefined document type
 *     
 *******************************************************************************/
package org.openhealthtools.mdht.uml.cda.util;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import org.eclipse.emf.ecore.xmi.DOMHandler;
import org.eclipse.emf.ecore.xmi.DOMHelper;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xml.type.AnyType;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.ecore.OCLExpression;
import org.openhealthtools.mdht.emf.runtime.resource.DOMDocumentHandlerImpl;
import org.openhealthtools.mdht.emf.runtime.resource.FleXMLResource;
import org.openhealthtools.mdht.emf.runtime.resource.FleXMLResourceSet;
import org.openhealthtools.mdht.emf.runtime.resource.XSITypeProvider;
import org.openhealthtools.mdht.emf.runtime.util.Initializer;
import org.openhealthtools.mdht.uml.cda.Act;
import org.openhealthtools.mdht.uml.cda.CDAFactory;
import org.openhealthtools.mdht.uml.cda.CDAPackage;
import org.openhealthtools.mdht.uml.cda.ClinicalDocument;
import org.openhealthtools.mdht.uml.cda.ClinicalStatement;
import org.openhealthtools.mdht.uml.cda.Component2;
import org.openhealthtools.mdht.uml.cda.Component3;
import org.openhealthtools.mdht.uml.cda.Component4;
import org.openhealthtools.mdht.uml.cda.Component5;
import org.openhealthtools.mdht.uml.cda.DocumentRoot;
import org.openhealthtools.mdht.uml.cda.Encounter;
import org.openhealthtools.mdht.uml.cda.Entry;
import org.openhealthtools.mdht.uml.cda.EntryRelationship;
import org.openhealthtools.mdht.uml.cda.Observation;
import org.openhealthtools.mdht.uml.cda.ObservationMedia;
import org.openhealthtools.mdht.uml.cda.Organizer;
import org.openhealthtools.mdht.uml.cda.Procedure;
import org.openhealthtools.mdht.uml.cda.RegionOfInterest;
import org.openhealthtools.mdht.uml.cda.Section;
import org.openhealthtools.mdht.uml.cda.StructuredBody;
import org.openhealthtools.mdht.uml.cda.SubstanceAdministration;
import org.openhealthtools.mdht.uml.cda.Supply;
import org.openhealthtools.mdht.uml.cda.internal.resource.CDAResource;
import org.openhealthtools.mdht.uml.cda.internal.resource.CDAResourceFactoryImpl;
import org.openhealthtools.mdht.uml.cda.internal.resource.CDAXSITypeProvider;
import org.openhealthtools.mdht.uml.hl7.datatypes.II;
import org.openhealthtools.mdht.uml.hl7.rim.InfrastructureRoot;
import org.openhealthtools.mdht.uml.hl7.vocab.x_ActRelationshipEntry;
import org.openhealthtools.mdht.uml.hl7.vocab.x_ActRelationshipEntryRelationship;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class CDAUtil {
	public static final String CDA_ANNOTATION_SOURCE = "http://www.openhealthtools.org/mdht/uml/cda/annotation";

	private static final Pattern COMPONENT_PATTERN = Pattern.compile("(^[A-Za-z0-9]+)(\\[([1-9]+[0-9]*)\\])?");

	static {
		// configure the CDA resource implementation
		CDAResourceFactoryImpl.init();
	}

	/**
	 * List of all template classes derived from ClinicalDocument.
	 */
	public static Map getAllDocumentClasses() {
		Map result;

		XSITypeProvider provider = XSITypeProvider.Registry.INSTANCE.getXSITypeProvider(CDAPackage.eINSTANCE);
		if (provider instanceof CDAXSITypeProvider) {
			result = Collections.unmodifiableMap(((CDAXSITypeProvider) provider).getAllDocumentClasses());
		} else {
			result = Collections.emptyMap();
		}

		return result;
	}

	/**
	 * If not null, use this EClass as the document root in loader.
	 * 
	 * @deprecated Use resource sets created by the {@link #createResourceSet(EClass)} or {@link #createResourceSet(String)} API to force a particular document class in resource loading
	 */
	@Deprecated
	public static EClass getDocumentClass() {
		EClass result = null;

		XSITypeProvider provider = XSITypeProvider.Registry.INSTANCE.getXSITypeProvider(CDAPackage.eINSTANCE);
		if (provider instanceof CDAXSITypeProvider) {
			result = ((CDAXSITypeProvider) provider).getDocumentClass();
		}

		return result;
	}

	/**
	 * EClass to use as the document root, or set to null for default behavior of
	 * discovering EClass based on templateId.
	 * 
	 * @deprecated Use the {@link #createResourceSet(EClass)} API, instead, to create a resource set configured to load documents of the given type
	 */
	@Deprecated
	public static void setDocumentClass(EClass eClass) {
		XSITypeProvider.Registry.INSTANCE.registerXSITypeProvider(CDAPackage.eINSTANCE, new CDAXSITypeProvider(eClass));
	}

	/**
	 * Model qualified name (e.g. ccd::ContinuityOfCareDocument) of the EClass to use 
	 * as the document root, or set to null for default behavior of
	 * discovering EClass based on templateId.
	 * 
	 * @deprecated Use the {@link #createResourceSet(String)} API, instead, to create a resource set configured to load documents of the given type
	 */
	@Deprecated
	public static void setDocumentClassQName(String documentClassQName) {
		XSITypeProvider.Registry.INSTANCE.registerXSITypeProvider(CDAPackage.eINSTANCE, new CDAXSITypeProvider(
			documentClassQName));
	}

	/**
	 * Creates a new resource set for loading CDA-based XML documents.
	 * 
	 * @return the resource set
	 * 
	 * @see #createResourceSet(EClass)
	 * @see #createResourceSet(String)
	 */
	public static FleXMLResourceSet createResourceSet() {
		return FleXMLResourceSet.Factory.INSTANCE.createResourceSet().setDefaultResourceFactory(
			CDAResource.Factory.INSTANCE);
	}

	/**
	 * Creates a new resource set for loading CDA-based XML documents of a specified {@link EClass} type.
	 * 
	 * @param documentClass my forced document class, or {@code null} to discover the document class by template ID
	 * 
	 * @return the resource set
	 */
	public static FleXMLResourceSet createResourceSet(EClass eClass) {
		FleXMLResourceSet result = FleXMLResourceSet.Factory.INSTANCE.createResourceSet().setDefaultResourceFactory(
			CDAResource.Factory.INSTANCE);

		result.getXSITypeProviderRegistry().registerXSITypeProvider(
			CDAPackage.eINSTANCE, new CDAXSITypeProvider(eClass));

		return result;
	}

	/**
	 * Creates a new resource set for loading CDA-based XML documents of a specified document class type.
	 * 
	 * @param documentClassQName my forced document class's qualified name, or {@code null} to discover the document class by template ID
	 * 
	 * @return the resource set
	 */
	public static FleXMLResourceSet createResourceSet(String documentClassQName) {
		FleXMLResourceSet result = FleXMLResourceSet.Factory.INSTANCE.createResourceSet().setDefaultResourceFactory(
			CDAResource.Factory.INSTANCE);

		result.getXSITypeProviderRegistry().registerXSITypeProvider(
			CDAPackage.eINSTANCE, new CDAXSITypeProvider(documentClassQName));

		return result;
	}

	public static ClinicalDocument load(InputStream in) throws Exception {
		return load(in, (ValidationHandler) null);
	}

	public static ClinicalDocument load(InputSource is) throws Exception {
		return load(is, (ValidationHandler) null);
	}

	public static ClinicalDocument load(Document document) throws Exception {
		return load(document, (ValidationHandler) null);
	}

	public static ClinicalDocument load(InputStream in, final ValidationHandler handler) throws Exception {
		return load(CDAUtil.createResourceSet((EClass) null), generateURI(), in, handler);
	}

	public static ClinicalDocument load(ResourceSet resourceSet, URI uri, InputStream in,
			final ValidationHandler handler) throws Exception {
		XMLResource resource = (XMLResource) resourceSet.createResource(uri);
		Map options = new java.util.HashMap();

		if (handler != null) {
			// perform XML schema validation BEFORE load for base standard compliance (complete)
			options.put(FleXMLResource.OPTION_DOM_DOCUMENT_HANDLER, new DOMDocumentHandlerImpl() {
				@Override
				public void aboutToLoadDOM(Document document) {
					performSchemaValidation(document, handler);
				}
			});
		}

		resource.load(in, options);

		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		ClinicalDocument clinicalDocument = root.getClinicalDocument();

		if (handler != null) {
			// perform EMF validation AFTER load for base standard compliance (subset) + IG-specific compliance
			performEMFValidation(clinicalDocument, handler);
		}

		return clinicalDocument;
	}

	public static ClinicalDocument load(InputSource is, final ValidationHandler handler) throws Exception {
		return load(CDAUtil.createResourceSet((EClass) null), generateURI(), is, handler);
	}

	public static ClinicalDocument load(ResourceSet resourceSet, URI uri, InputSource is,
			final ValidationHandler handler) throws Exception {
		XMLResource resource = (XMLResource) resourceSet.createResource(uri);
		Map options = new java.util.HashMap();

		if (handler != null) {
			// perform XML schema validation BEFORE load for base standard compliance (complete)
			options.put(FleXMLResource.OPTION_DOM_DOCUMENT_HANDLER, new DOMDocumentHandlerImpl() {
				@Override
				public void aboutToLoadDOM(Document document) {
					performSchemaValidation(document, handler);
				}
			});
		}

		resource.load(is, options);

		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		ClinicalDocument clinicalDocument = root.getClinicalDocument();

		if (handler != null) {
			// perform EMF validation AFTER load for base standard compliance (subset) + IG-specific compliance
			performEMFValidation(clinicalDocument, handler);
		}

		return clinicalDocument;
	}

	public static ClinicalDocument load(Document document, final ValidationHandler handler) throws Exception {
		return load(CDAUtil.createResourceSet((EClass) null), generateURI(), document, handler);
	}

	public static ClinicalDocument load(ResourceSet resourceSet, URI uri, Document document,
			final ValidationHandler handler) throws Exception {
		XMLResource resource = (XMLResource) resourceSet.createResource(uri);
		Map options = new java.util.HashMap();

		if (handler != null) {
			// perform XML schema validation BEFORE load for base standard compliance (complete)
			options.put(FleXMLResource.OPTION_DOM_DOCUMENT_HANDLER, new DOMDocumentHandlerImpl() {
				@Override
				public void aboutToLoadDOM(Document document) {
					performSchemaValidation(document, handler);
				}
			});
		}

		resource.load(document, options);

		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		ClinicalDocument clinicalDocument = root.getClinicalDocument();

		if (handler != null) {
			// perform EMF validation AFTER load for base standard compliance (subset) + IG-specific compliance
			performEMFValidation(clinicalDocument, handler);
		}

		return clinicalDocument;
	}

	public static ClinicalDocument load(URI uri, final ValidationHandler handler) throws Exception {
		return load(CDAUtil.createResourceSet((EClass) null), uri, handler);
	}

	/**
	 * Convenience load method for loading a CDA document as a specific type
	 * 
	 * @param in
	 * @param docTypeEClass
	 * @return
	 * @throws Exception
	 */
	public static ClinicalDocument loadAs(InputStream in, EClass docTypeEClass) throws Exception {
		return load(CDAUtil.createResourceSet(docTypeEClass), generateURI(), in, (ValidationHandler) null);
	}

	/**
	 * Convenience load method for loading a CDA document as a specific type
	 * 
	 * @param in
	 * @param docTypeEClass
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	public static ClinicalDocument loadAs(InputStream in, EClass docTypeEClass, final ValidationHandler handler)
			throws Exception {
		return load(CDAUtil.createResourceSet(docTypeEClass), generateURI(), in, handler);
	}

	private static int uriCounter = 0;

	private static URI generateURI() {
		URI uri = URI.createURI("http:///resource" + uriCounter + ".xml");
		uriCounter++;
		return uri;
	}

	public static ClinicalDocument load(ResourceSet resourceSet, URI uri, final ValidationHandler handler)
			throws Exception {
		XMLResource resource = (XMLResource) resourceSet.createResource(uri);
		Map options = new java.util.HashMap();

		if (handler != null) {
			// perform XML schema validation BEFORE load for base standard compliance (complete)
			options.put(FleXMLResource.OPTION_DOM_DOCUMENT_HANDLER, new DOMDocumentHandlerImpl() {
				@Override
				public void aboutToLoadDOM(Document document) {
					performSchemaValidation(document, handler);
				}
			});
		}

		resource.load(options);

		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		ClinicalDocument clinicalDocument = root.getClinicalDocument();

		if (handler != null) {
			// perform EMF validation AFTER load for base standard compliance (subset) + IG-specific compliance
			performEMFValidation(clinicalDocument, handler);
		}

		return clinicalDocument;
	}

	/**
	 * @deprecated Use the {@link #load(ResourceSet, URI, LoadHandler)} API, instead.
	 */
	@Deprecated
	public static ClinicalDocument load(InputStream in, LoadHandler handler) throws Exception {
		DocumentBuilder builder = newDocumentBuilder();
		Document doc = builder.parse(in);
		return load(doc, handler);
	}

	/**
	 * @deprecated Use the {@link #load(ResourceSet, URI, LoadHandler)} API, instead.
	 */
	@Deprecated
	public static ClinicalDocument load(InputSource is, LoadHandler handler) throws Exception {
		DocumentBuilder builder = newDocumentBuilder();
		Document doc = builder.parse(is);
		return load(doc, handler);
	}

	/**
	 * @deprecated Use the {@link #load(ResourceSet, URI, LoadHandler)} API, instead.
	 */
	@Deprecated
	public static ClinicalDocument load(Document doc, LoadHandler handler) throws Exception {
		XMLResource resource = (XMLResource) createResourceSet().createResource(URI.createURI(CDAPackage.eNS_URI));

		resource.load(doc, null);
		if (handler != null) {
			processResource(resource, handler);
		}
		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		return root.getClinicalDocument();
	}

	public static ClinicalDocument load(ResourceSet resourceSet, URI uri, LoadHandler handler) throws Exception {
		XMLResource resource = (XMLResource) resourceSet.createResource(uri);

		resource.load(null);
		if (handler != null) {
			processResource(resource, handler);
		}
		DocumentRoot root = (DocumentRoot) resource.getContents().get(0);
		return root.getClinicalDocument();
	}

	private static DocumentBuilder newDocumentBuilder() throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		return factory.newDocumentBuilder();
	}

	private static void processResource(XMLResource resource, LoadHandler handler) {
		Map extMap = resource.getEObjectToExtensionMap();
		for (EObject key : extMap.keySet()) {
			AnyType value = extMap.get(key);
			handleUnknownData(key, value, handler);
		}
	}

	private static void handleUnknownData(EObject object, AnyType unknownData, LoadHandler handler) {
		handleUnknownFeatures(object, unknownData.getMixed(), handler);
		handleUnknownFeatures(object, unknownData.getAnyAttribute(), handler);
	}

	private static void handleUnknownFeatures(EObject owner, FeatureMap featureMap, LoadHandler handler) {
		Iterator iterator = featureMap.iterator();
		while (iterator.hasNext()) {
			FeatureMap.Entry entry = iterator.next();
			EStructuralFeature feature = entry.getEStructuralFeature();
			if (handler.handleUnknownFeature(owner, feature, entry.getValue())) {
				iterator.remove();
			}
		}
	}

	public interface LoadHandler {
		public boolean handleUnknownFeature(EObject owner, EStructuralFeature feature, Object value);
	}

	public static void save(ClinicalDocument clinicalDocument, OutputStream out) throws Exception {
		save(clinicalDocument, out, true);
	}

	public static void save(ClinicalDocument clinicalDocument, OutputStream out, boolean defaults) throws Exception {
		XMLResource resource = prepare(clinicalDocument, defaults);
		resource.save(out, null);
	}

	public static void save(ClinicalDocument clinicalDocument, Writer writer) throws Exception {
		save(clinicalDocument, writer, true);
	}

	public static void save(ClinicalDocument clinicalDocument, Writer writer, boolean defaults) throws Exception {
		XMLResource resource = prepare(clinicalDocument, defaults);
		resource.save(writer, null);
	}

	public static Document save(ClinicalDocument clinicalDocument, DOMHandler handler) throws Exception {
		XMLResource resource = prepare(clinicalDocument, true);
		return resource.save(newDocumentBuilder().newDocument(), null, handler);
	}

	private static XMLResource prepare(ClinicalDocument clinicalDocument, boolean defaults) {
		if (defaults) {
			handleDefaults(clinicalDocument);
		}
		XMLResource resource = (XMLResource) clinicalDocument.eResource();
		if (resource == null) {
			resource = (XMLResource) createResourceSet().createResource(URI.createURI(CDAPackage.eNS_URI));
			DocumentRoot root = CDAFactory.eINSTANCE.createDocumentRoot();
			root.setClinicalDocument(clinicalDocument);
			root.getXMLNSPrefixMap().put("", CDAPackage.eNS_URI);
			root.getXSISchemaLocation().put(CDAPackage.eNS_URI, "CDA.xsd");
			resource.getContents().add(root);
		} else {
			DocumentRoot root = (DocumentRoot) clinicalDocument.eContainer();
			List keys = new ArrayList();
			for (Map.Entry entry : root.getXMLNSPrefixMap().entrySet()) {
				if (EPackage.Registry.INSTANCE.keySet().contains(entry.getValue())) {
					keys.add(entry.getKey());
				}
			}
			for (String key : keys) {
				root.getXMLNSPrefixMap().removeKey(key);
			}
			root.getXMLNSPrefixMap().put("", CDAPackage.eNS_URI);
		}
		return resource;
	}

	public static void saveSnippet(InfrastructureRoot snippet, OutputStream out) throws Exception {
		saveSnippet(snippet, out, true);
	}

	public static void saveSnippet(InfrastructureRoot snippet, OutputStream out, boolean defaults) throws Exception {
		XMLResource resource = prepare(snippet, defaults);
		resource.save(out, snippetOptions());
	}

	public static void saveSnippet(InfrastructureRoot snippet, Writer writer) throws Exception {
		XMLResource resource = prepare(snippet, true);
		resource.save(writer, snippetOptions());

	}

	private static Map snippetOptions() {
		Map options = new HashMap();
		options.put(XMLResource.OPTION_ENCODING, "UTF8");
		options.put(XMLResource.OPTION_SCHEMA_LOCATION, Boolean.FALSE);
		options.put(XMLResource.OPTION_DECLARE_XML, Boolean.FALSE);

		return options;
	}

	/**
	 * Creates a document root instance and adds the cda snippet to be streamed
	 * 
	 * @param cdaSnippet
	 * @param defaults
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static XMLResource prepare(InfrastructureRoot cdaSnippet, boolean defaults) {

		XMLResource resource = (XMLResource) createResourceSet().createResource(URI.createURI(CDAPackage.eNS_URI));

		EClass documentRootEClass = EcoreFactory.eINSTANCE.createEClass();
		documentRootEClass.setName("DocumentRoot");
		ExtendedMetaData.INSTANCE.setName(documentRootEClass, "");
		ExtendedMetaData.INSTANCE.setContentKind(documentRootEClass, ExtendedMetaData.MIXED_CONTENT);

		EAttribute mixed = EcoreFactory.eINSTANCE.createEAttribute();
		mixed.setName("mixed");
		mixed.setEType(EcorePackage.eINSTANCE.getEFeatureMapEntry());
		mixed.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);
		ExtendedMetaData.INSTANCE.setName(mixed, ":mixed");
		ExtendedMetaData.INSTANCE.setFeatureKind(mixed, ExtendedMetaData.ELEMENT_WILDCARD_FEATURE);
		documentRootEClass.getEStructuralFeatures().add(mixed);

		EReference xmlnsPrefixMap = EcoreFactory.eINSTANCE.createEReference();
		xmlnsPrefixMap.setName("xMLNSPrefixMap");

		xmlnsPrefixMap.setEType(EcorePackage.eINSTANCE.getEStringToStringMapEntry());

		xmlnsPrefixMap.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);
		xmlnsPrefixMap.setContainment(true);
		xmlnsPrefixMap.setTransient(true);
		ExtendedMetaData.INSTANCE.setName(xmlnsPrefixMap, "xmlns:prefix");
		ExtendedMetaData.INSTANCE.setFeatureKind(xmlnsPrefixMap, ExtendedMetaData.ATTRIBUTE_FEATURE);
		documentRootEClass.getEStructuralFeatures().add(xmlnsPrefixMap);

		EReference xsiSchemaLocation = EcoreFactory.eINSTANCE.createEReference();
		xsiSchemaLocation.setName("xSISchemaLocation");

		xsiSchemaLocation.setEType(EcorePackage.eINSTANCE.getEStringToStringMapEntry());

		xsiSchemaLocation.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);
		xsiSchemaLocation.setContainment(true);
		xsiSchemaLocation.setTransient(true);
		ExtendedMetaData.INSTANCE.setName(xsiSchemaLocation, "xsi:schemaLocation");
		ExtendedMetaData.INSTANCE.setFeatureKind(xsiSchemaLocation, ExtendedMetaData.ATTRIBUTE_FEATURE);
		documentRootEClass.getEStructuralFeatures().add(xsiSchemaLocation);

		EReference snippetReference = EcoreFactory.eINSTANCE.createEReference();

		String snippetName = cdaSnippet.eClass().getName();

		for (EClass eClass : cdaSnippet.eClass().getEAllSuperTypes()) {
			if (CDAPackage.eINSTANCE.getNsURI().equals(eClass.getEPackage().getNsURI()) &&
					!"ClinicalStatement".equals(eClass.getName())) {
				snippetName = eClass.getName();
				break;
			}
		}
		snippetReference.setName(snippetName.toLowerCase());

		snippetReference.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);
		snippetReference.setContainment(true);
		snippetReference.setEType(cdaSnippet.eClass());

		documentRootEClass.getEStructuralFeatures().add(snippetReference);

		EPackage documentRootPackage = EcoreFactory.eINSTANCE.createEPackage();
		documentRootPackage.setName(CDAPackage.eNAME);
		documentRootPackage.setNsPrefix(CDAPackage.eNS_PREFIX);
		documentRootPackage.setNsURI(CDAPackage.eNS_URI);
		documentRootPackage.getEClassifiers().add(documentRootEClass);
		EFactory documentRootFactoryInstance = documentRootPackage.getEFactoryInstance();
		EObject documentRootInstance = documentRootFactoryInstance.create(documentRootEClass);

		((EMap) documentRootInstance.eGet(xmlnsPrefixMap)).put("", CDAPackage.eNS_URI);

		((EMap) documentRootInstance.eGet(xsiSchemaLocation)).put(CDAPackage.eNS_URI, "CDA.xsd");

		((List) documentRootInstance.eGet(snippetReference)).add(cdaSnippet);

		resource.getContents().add(documentRootInstance);

		return resource;
	}

	// iterative breadth-first traversal using queue
	@SuppressWarnings("unchecked")
	private static void handleDefaults(EObject root) {
		Queue queue = new LinkedList();
		queue.add(root); // root
		while (!queue.isEmpty()) {
			EObject eObject = queue.remove();
			EClass eClass = eObject.eClass();
			for (EAttribute attribute : eClass.getEAllAttributes()) { // visit
				if (!eObject.eIsSet(attribute) && attribute.getLowerBound() > 0 &&
						attribute.getDefaultValueLiteral() != null) {
					if (attribute.isMany()) {
						List list = (List) eObject.eGet(attribute);
						list.add(attribute.getDefaultValue());
					} else {
						eObject.eSet(attribute, attribute.getDefaultValue());
					}
				}
			}
			for (EReference reference : eClass.getEAllReferences()) { // process successors
				Object value = eObject.eGet(reference);
				if (value != null) {
					if (reference.isMany()) {
						queue.addAll((List) value);
					} else {
						queue.add((EObject) value);
					}
				}
			}
		}
	}

	public static boolean validate(ClinicalDocument clinicalDocument) {
		return validate(clinicalDocument, null);
	}

	public static boolean validate(ClinicalDocument clinicalDocument, ValidationHandler handler) {
		return validate(clinicalDocument, handler, true);
	}

	public static boolean validate(ClinicalDocument clinicalDocument, ValidationHandler handler, boolean defaults) {
		if (defaults) {
			handleDefaults(clinicalDocument);
		}
		if (clinicalDocument.eResource() != null) {
			// process diagnostics that were produced during EMF deserialization
			processDiagnostic(EcoreUtil.computeDiagnostic(clinicalDocument.eResource(), true), handler);
		}
		Diagnostic diagnostic = Diagnostician.INSTANCE.validate(clinicalDocument);
		if (handler != null) {
			processDiagnostic(diagnostic, handler);
		}
		return diagnostic.getSeverity() != Diagnostic.ERROR;
	}

	public static void performSchemaValidation(ClinicalDocument clinicalDocument, ValidationHandler handler) {
		try {
			Document document = CDAUtil.save(clinicalDocument, (DOMHandler) null);
			performSchemaValidation(document, handler);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void performSchemaValidation(Document document, ValidationHandler handler) {
		try {
			// URL url = CDAUtil.class.getResource("/samples/CDA.xsd");
			URL url = CDAUtil.class.getResource("/samples/C32_CDA.xsd");
			if (url == null) {
				// url = new File("../org.openhealthtools.mdht.uml.cda/samples/CDA.xsd").toURI().toURL();
				url = new File("../org.openhealthtools.mdht.uml.cda/samples/C32_CDA.xsd").toURI().toURL();
			}

			SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema schema = factory.newSchema(new StreamSource(url.toExternalForm()));

			Validator validator = schema.newValidator();
			validator.setErrorHandler(new SchemaValidationHandler(handler));
			validator.validate(new DOMSource(document));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void performEMFValidation(Document document, ValidationHandler handler) {
		try {
			ClinicalDocument clinicalDocument = CDAUtil.load(document);
			performEMFValidation(clinicalDocument, handler);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void performEMFValidation(ClinicalDocument clinicalDocument, ValidationHandler handler) {
		validate(clinicalDocument, handler);
	}

	public static class SchemaValidationHandler implements ErrorHandler {
		private ValidationHandler handler = null;

		public SchemaValidationHandler(ValidationHandler handler) {
			this.handler = handler;
		}

		public void error(SAXParseException exception) throws SAXException {
			handler.handleError(createDiagnostic(Diagnostic.ERROR, exception));
		}

		public void fatalError(SAXParseException exception) throws SAXException {
		}

		public void warning(SAXParseException exception) throws SAXException {
			handler.handleWarning(createDiagnostic(Diagnostic.WARNING, exception));
		}

		private Diagnostic createDiagnostic(int severity, SAXParseException exception) {
			return new BasicDiagnostic(
				severity, "javax.xml.validation.Validator", 0, exception.getMessage(), new Object[] { exception });
		}
	}

	// iterative breadth-first traversal of diagnostic tree using queue
	private static void processDiagnostic(Diagnostic diagnostic, ValidationHandler handler) {
		Queue queue = new LinkedList();
		queue.add(diagnostic); // root
		while (!queue.isEmpty()) {
			Diagnostic d = queue.remove();
			if (shouldHandle(d)) {
				handleDiagnostic(d, handler); // visit
			}
			for (Diagnostic childDiagnostic : d.getChildren()) { // process successors
				queue.add(childDiagnostic);
			}
		}
	}

	private static boolean shouldHandle(Diagnostic diagnostic) {
		// filter out diagnostics with no message or with root diagnostic message
		if (diagnostic.getMessage() == null || diagnostic.getMessage().startsWith("Diagnosis of")) {
			return false;
		}
		return true;
	}

	private static void handleDiagnostic(Diagnostic diagnostic, ValidationHandler handler) {
		switch (diagnostic.getSeverity()) {
			case Diagnostic.ERROR:
				handler.handleError(diagnostic);
				break;
			case Diagnostic.WARNING:
				handler.handleWarning(diagnostic);
				break;
			case Diagnostic.INFO:
				handler.handleInfo(diagnostic);
				break;
		}
	}

	public interface ValidationHandler {
		public void handleError(Diagnostic diagnostic);

		public void handleWarning(Diagnostic diagnostic);

		public void handleInfo(Diagnostic diagnostic);
	}

	// walk up the containment tree until we reach the ClinicalDocument or we run out of containers
	public static ClinicalDocument getClinicalDocument(EObject object) {
		while (object != null && !(object instanceof ClinicalDocument)) {
			object = object.eContainer();
		}
		return (ClinicalDocument) object;
	}

	// walk up the containment tree until we reach a Section or we run out of containers
	public static Section getSection(EObject object) {
		while (object != null && !(object instanceof Section)) {
			object = object.eContainer();
		}
		return (Section) object;
	}

	/**
	 * 
	 * @param source
	 *            a Clinical Statement
	 * @param typeCode
	 *            If typeCode is null, then all relationships are matched.
	 * @param targetClass
	 *            EClass of relationship target. If null, then all target types are returned.
	 * @return list of Clinical Statements
	 */
	public static EList getEntryRelationshipTargets(ClinicalStatement source,
			x_ActRelationshipEntryRelationship typeCode, EClass targetClass) {
		List targets = new ArrayList();

		// test children
		for (EntryRelationship rel : CDAUtil.getEntryRelationships(source)) {
			boolean typeCodeMatch = typeCode == null
					? true
					: typeCode.equals(rel.getTypeCode());
			ClinicalStatement target = CDAUtil.getClinicalStatement(rel);
			if (target != null && isReference(target)) {
				// resolve 'id' referenced elements
				ClinicalStatement element = resolveReference(target);
				if (element != null) {
					target = element;
				}
			}
			if ((Boolean.FALSE == rel.getInversionInd() || null == rel.getInversionInd()) && typeCodeMatch &&
					target != null && (targetClass == null || targetClass.isSuperTypeOf(target.eClass()))) {
				targets.add(target);
			}
		}

		// test container with inversionInd="true"
		if (source.eContainer() instanceof EntryRelationship) {
			EntryRelationship rel = (EntryRelationship) source.eContainer();
			boolean typeCodeMatch = typeCode == null
					? true
					: typeCode.equals(rel.getTypeCode());
			if (Boolean.TRUE == rel.getInversionInd() && typeCodeMatch &&
					rel.eContainer() instanceof ClinicalStatement &&
					(targetClass == null || targetClass.isSuperTypeOf(rel.eContainer().eClass()))) {
				targets.add((ClinicalStatement) rel.eContainer());
			}
		}

		return new BasicEList.UnmodifiableEList(targets.size(), targets.toArray());
	}

	/**
	 * 
	 * @param source
	 *            a Section
	 * @param typeCode
	 *            If typeCode is null, then all relationships are matched.
	 * @param targetClass
	 *            EClass of relationship target. If null, then all target types are returned.
	 * @return list of Clinical Statements
	 */
	public static EList getEntryTargets(Section source, x_ActRelationshipEntry typeCode,
			EClass targetClass) {
		List targets = new ArrayList();

		// test children
		for (Entry rel : source.getEntries()) {
			boolean typeCodeMatch = typeCode == null
					? true
					: typeCode.equals(rel.getTypeCode());
			ClinicalStatement target = CDAUtil.getClinicalStatement(rel);
			if (target != null && isReference(target)) {
				// resolve 'id' referenced elements
				ClinicalStatement element = resolveReference(target);
				if (element != null) {
					target = element;
				}
			}
			if (typeCodeMatch && target != null && (targetClass == null || targetClass.isSuperTypeOf(target.eClass()))) {
				targets.add(target);
			}
		}

		return new BasicEList.UnmodifiableEList(targets.size(), targets.toArray());
	}

	/**
	 * A CDA element may be reference if it has an 'id' child, but no 'templateId'.
	 */
	public static boolean isReference(EObject element) {
		EObject id = getChildElement(element, "id");
		EObject templateId = getChildElement(element, "templateId");

		return templateId == null && id instanceof II && ((II) id).getRoot() != null;
	}

	@SuppressWarnings("unchecked")
	private static EObject getChildElement(EObject eObject, String name) {
		Object result = null;

		EStructuralFeature feature = eObject.eClass().getEStructuralFeature(name);
		if (feature != null) {
			if (feature.isMany()) {
				List list = (List) eObject.eGet(feature);
				if (list.size() > 0) {
					result = list.get(0);
				}
			} else {
				result = eObject.eGet(feature);
			}
		}

		return (result instanceof EObject)
				? (EObject) result
				: null;
	}

	private static class ReferenceFilter implements Filter {
		private II id;

		public ReferenceFilter(II id) {
			this.id = id;
		}

		public boolean accept(T item) {
			EObject itemId = getChildElement(item, "id");
			EObject templateId = getChildElement(item, "templateId");

			if (itemId instanceof II && templateId != null && id.equals(itemId)) {
				return true;
			}
			return false;
		}
	}

	/**
	 * Return the referenced element, or null if 'element' argument is not a reference
	 * or referenced id is not found.
	 */
	public static EObject resolveReference(EObject element) {
		if (!isReference(element)) {
			return null;
		}
		final II id = (II) getChildElement(element, "id");

		EObject target = null;
		ClinicalDocument clinicalDocument = getClinicalDocument(element);
		if (clinicalDocument != null) {
			Query query = new Query(clinicalDocument);
			target = query.getEObject(EObject.class, new ReferenceFilter(id));
		}

		return target;
	}

	/**
	 * Return the referenced clinical statement, or null if 'element' argument is not a reference
	 * or referenced id is not found.
	 */
	public static ClinicalStatement resolveReference(ClinicalStatement element) {
		if (!isReference(element)) {
			return null;
		}
		final II id = (II) getChildElement(element, "id");

		ClinicalStatement target = null;
		ClinicalDocument clinicalDocument = getClinicalDocument(element);
		if (clinicalDocument != null) {
			Query query = new Query(clinicalDocument);
			target = query.getClinicalStatement(ClinicalStatement.class, new ReferenceFilter(id));
		}

		return target;
	}

	// initializer processing to populate runtime instance
	public static void init(EObject eObject) {
		final Initializer.Registry reg = Initializer.Util.getRegistry(eObject);

		// for backwards compatibility, make sure that the annotation-based initializer is used for old packages
		AnnotationBasedInitializer.ensureCompatibility(eObject.eClass(), reg);

		init(eObject, reg);
	}

	// initializer processing to populate runtime instance
	public static EObject init(EObject eObject, Initializer.Registry initializerRegistry) {
		EObject result = eObject;

		for (Initializer next : initializerRegistry.getInitializers(eObject.eClass())) {
			result = next.initialize(eObject);
		}

		return result;
	}

	public static void init(EObject eObject, Map details) {
		List created = new ArrayList();
		for (String key : details.keySet()) {
			try {
				String path = key.replace(".", "/");
				if (path.contains("/")) {
					String s = path.substring(0, path.lastIndexOf("/"));
					if (!created.contains(s)) {
						create(eObject, s);
						created.add(s);
					}
				}
				set(eObject, path, details.get(key));
			} catch (Exception e) {
			}
		}
	}

	// BEGIN: Path Expression Support
	public static  T create(EObject root, String path) {
		return create(root, path, null);
	}

	@SuppressWarnings("unchecked")
	public static  T create(EObject root, String path, EClass eClass) {
		EObject current = root;
		String[] components = path.split("/");
		int currentIndex = 0;
		for (String component : components) {
			EStructuralFeature feature = current.eClass().getEStructuralFeature(component);
			if (feature instanceof EReference) {
				EObject eObject = null;
				Object value = current.eGet(feature);
				if (value == null || feature.isMany()) {
					EClass type = (EClass) feature.getEType();
					if (currentIndex == components.length - 1 && eClass != null && type.isSuperTypeOf(eClass)) {
						eObject = EcoreUtil.create(eClass);
					} else {
						eObject = EcoreUtil.create(type);
					}
					if (feature.isMany()) {
						List list = (List) value;
						list.add(eObject);
					} else {
						current.eSet(feature, eObject);
					}
				} else {
					eObject = (EObject) value;
				}
				current = eObject;
			}
			currentIndex++;
		}
		return (T) current;
	}

	@SuppressWarnings("unchecked")
	public static void set(EObject root, String path, Object value) {
		String last = path.substring(path.lastIndexOf("/") + 1);
		EObject target = path.equals(last)
				? root
				: (EObject) get(root, path.substring(0, path.lastIndexOf("/")));
		if (target != null) {
			String name = null;
			Integer index = null;
			Matcher matcher = COMPONENT_PATTERN.matcher(last);
			if (matcher.matches()) {
				name = matcher.group(1);
				if (matcher.group(3) != null) {
					index = Integer.valueOf(matcher.group(3)) - 1;
				}
				EStructuralFeature feature = target.eClass().getEStructuralFeature(name);
				if (feature != null && value != null) {
					if (FeatureMapUtil.isFeatureMap(feature) && value instanceof String) {
						FeatureMap featureMap = (FeatureMap) target.eGet(feature);
						FeatureMapUtil.addText(featureMap, (String) value);
					} else {
						if (feature instanceof EAttribute) {
							EDataType type = (EDataType) feature.getEType();
							if (value instanceof String && !type.isInstance(value)) {
								value = EcoreUtil.createFromString(type, (String) value);
							}
						}
						if (feature.isMany()) {
							List list = (List) target.eGet(feature);
							if (index != null) {
								if (index >= 0 && index < list.size()) {
									list.set(index, value);
								}
							} else {
								list.add(value);
							}
						} else {
							target.eSet(feature, value);
						}
					}
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	public static  T get(EObject root, String path) {
		Object result = null;
		EObject current = root;
		String[] components = path.split("/");
		for (String component : components) {
			if (current != null) {
				String name = null;
				Integer index = null;
				Matcher matcher = COMPONENT_PATTERN.matcher(component);
				if (matcher.matches()) {
					name = matcher.group(1);
					if (matcher.group(3) != null) {
						index = Integer.valueOf(matcher.group(3)) - 1;
					}
					EStructuralFeature feature = current.eClass().getEStructuralFeature(name);
					if (feature != null) {
						if (feature.isMany()) {
							List list = (List) current.eGet(feature);
							if (index == null) {
								index = list.size() - 1;
							}
							result = (index >= 0 && index < list.size())
									? list.get(index)
									: null;
						} else {
							result = current.eGet(feature);
						}
						if (feature instanceof EReference) {
							current = (EObject) result;
						}
					} else {
						result = current = null;
					}
				}
			}
		}
		return (T) result;
	}

	public static boolean isSet(EObject root, String path) {
		return get(root, path) != null;
	}

	@SuppressWarnings("unchecked")
	public static void unset(EObject root, String path) {
		String last = path.substring(path.lastIndexOf("/") + 1);
		EObject target = path.equals(last)
				? root
				: (EObject) get(root, path.substring(0, path.lastIndexOf("/")));
		if (target != null) {
			String name = null;
			Integer index = null;
			Matcher matcher = COMPONENT_PATTERN.matcher(last);
			if (matcher.matches()) {
				name = matcher.group(1);
				if (matcher.group(3) != null) {
					index = Integer.valueOf(matcher.group(3)) - 1;
				}
				EStructuralFeature feature = target.eClass().getEStructuralFeature(name);
				if (feature != null) {
					if (feature.isMany() && index != null) {
						List list = (List) target.eGet(feature);
						if (index >= 0 && index < list.size()) {
							list.remove(index);
						}
					} else {
						target.eUnset(feature);
					}
				}
			}
		}
	}

	// END: Path Expression Support

	// BEGIN: OCL Support
	private static final OCL ocl = OCL.newInstance();

	public static Object query(EObject eObject, String body) throws Exception {
		OCL.Helper helper = ocl.createOCLHelper();
		helper.setContext(eObject.eClass());
		OCLExpression expression = helper.createQuery(body);
		OCL.Query query = ocl.createQuery(expression);
		return query.evaluate(eObject);
	}

	public static boolean check(EObject eObject, String body) throws Exception {
		OCL.Helper helper = ocl.createOCLHelper();
		helper.setContext(eObject.eClass());
		Constraint constraint = helper.createInvariant(body);
		OCL.Query query = ocl.createQuery(constraint);
		return query.check(eObject);
	}

	// END: OCL Support

	// BEGIN: Experimental Query/Filter operations
	public interface Filter {
		public boolean accept(T item);
	}

	public static class OCLFilter implements Filter {
		protected String body = null;

		public OCLFilter(String body) {
			this.body = body;
		}

		public boolean accept(T item) {
			boolean result = false;
			try {
				result = check(item, body);
			} catch (Exception e) {
			}
			return result;
		}
	}

	public static class Query {
		private ClinicalDocument clinicalDocument = null;

		private List
allSections = null; private List allClinicalStatements = null; private List allEObjects = null; public Query(ClinicalDocument clinicalDocument) { this.clinicalDocument = clinicalDocument; } // get first section that conforms to clazz and is accepted by filter public T getSection(Class clazz, Filter filter) { List sections = getSections(clazz, filter); return !sections.isEmpty() ? sections.get(0) : null; } // get first section that conforms to clazz public T getSection(Class clazz) { List sections = getSections(clazz); return !sections.isEmpty() ? sections.get(0) : null; } // get sections that conform to clazz and are accepted by filter public List getSections(Class clazz, Filter filter) { List sections = new ArrayList(); for (T section : getSections(clazz)) { if (filter.accept(section)) { sections.add(section); } } return sections; } // get sections that conform to clazz public List getSections(Class clazz) { List sections = new ArrayList(); for (Section section : getAllSections()) { if (clazz.isInstance(section)) { sections.add(clazz.cast(section)); } } return sections; } // get all sections in the document (closure) public List
getAllSections() { if (allSections == null) { allSections = CDAUtil.getAllSections(clinicalDocument); } return allSections; } // get first clinical statement that conform to clazz and is accepted by filter public T getClinicalStatement(Class clazz, Filter filter) { List clinicalStatements = getClinicalStatements(clazz, filter); return !clinicalStatements.isEmpty() ? clinicalStatements.get(0) : null; } // get first clinical statement that conform to clazz public T getClinicalStatement(Class clazz) { List clinicalStatements = getClinicalStatements(clazz); return !clinicalStatements.isEmpty() ? clinicalStatements.get(0) : null; } // get clinical statements that conform to clazz and are accepted by filter public List getClinicalStatements(Class clazz, Filter filter) { List clinicalStatements = new ArrayList(); for (T clinicalStatement : getClinicalStatements(clazz)) { if (filter.accept(clinicalStatement)) { clinicalStatements.add(clinicalStatement); } } return clinicalStatements; } // get clinical statements that conform to clazz public List getClinicalStatements(Class clazz) { List clinicalStatements = new ArrayList(); for (EObject clinicalStatement : getAllClinicalStatements()) { if (clazz.isInstance(clinicalStatement)) { clinicalStatements.add(clazz.cast(clinicalStatement)); } } return clinicalStatements; } // get all clinical statements in the document (closure) public List getAllClinicalStatements() { if (allClinicalStatements == null) { allClinicalStatements = CDAUtil.getAllClinicalStatements(clinicalDocument); } return allClinicalStatements; } // get first object that conforms to clazz and is accepted by filter public T getEObject(Class clazz, Filter filter) { List eObjects = getEObjects(clazz, filter); return !eObjects.isEmpty() ? eObjects.get(0) : null; } // get first object that conforms to clazz public T getEObject(Class clazz) { List eObjects = getEObjects(clazz); return !eObjects.isEmpty() ? eObjects.get(0) : null; } // get objects that conform to clazz and are accepted by filter public List getEObjects(Class clazz, Filter filter) { List eObjects = new ArrayList(); for (T eObject : getEObjects(clazz)) { if (filter.accept(eObject)) { eObjects.add(eObject); } } return eObjects; } // get objects that conform to clazz public List getEObjects(Class clazz) { List eObjects = new ArrayList(); for (EObject eObject : getAllEObjects()) { if (clazz.isInstance(eObject)) { eObjects.add(clazz.cast(eObject)); } } return eObjects; } // get all objects in the document (closure) public List getAllEObjects() { if (allEObjects == null) { allEObjects = CDAUtil.getAllEObjects(clinicalDocument); } return allEObjects; } } // get first section that conforms to clazz and is accepted by filter public static T getSection(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List sections = getSections(clinicalDocument, clazz, filter); return !sections.isEmpty() ? sections.get(0) : null; } // get first section that conforms to clazz public static T getSection(ClinicalDocument clinicalDocument, Class clazz) { List sections = getSections(clinicalDocument, clazz); return !sections.isEmpty() ? sections.get(0) : null; } // get sections that conform to clazz and are accepted by filter public static List getSections(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List sections = new ArrayList(); for (T section : getSections(clinicalDocument, clazz)) { if (filter.accept(section)) { sections.add(section); } } return sections; } // get sections that conform to clazz public static List getSections(ClinicalDocument clinicalDocument, Class clazz) { List sections = new ArrayList(); for (Section section : getAllSections(clinicalDocument)) { if (clazz.isInstance(section)) { sections.add(clazz.cast(section)); } } return sections; } // get all sections in the document (closure) public static List
getAllSections(ClinicalDocument clinicalDocument) { List
allSections = new ArrayList
(); Component2 component2 = clinicalDocument.getComponent(); if (component2 != null) { StructuredBody structuredBody = component2.getStructuredBody(); if (structuredBody != null) { for (Component3 component3 : structuredBody.getComponents()) { Section section = component3.getSection(); if (section != null) { allSections.addAll(getSections(section)); } } } } return allSections; } // get all nested sections in the section (closure) public static List
getAllSections(Section section) { return getSections(section); } // iterative breadth-first traversal using queue private static List
getSections(Section section) { List
sections = new ArrayList
(); Queue
queue = new LinkedList
(); queue.add(section); // root while (!queue.isEmpty()) { Section sect = queue.remove(); sections.add(sect); // visit for (Component5 component : sect.getComponents()) { // process successors Section child = component.getSection(); if (child != null) { queue.add(child); } } } return sections; } // get first clinical statement that conforms to clazz and is accepted by filter public static T getClinicalStatement(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List clinicalStatements = getClinicalStatements(clinicalDocument, clazz, filter); return !clinicalStatements.isEmpty() ? clinicalStatements.get(0) : null; } // get first clinical statement that conforms to clazz public static T getClinicalStatement(ClinicalDocument clinicalDocument, Class clazz) { List clinicalStatements = getClinicalStatements(clinicalDocument, clazz); return !clinicalStatements.isEmpty() ? clinicalStatements.get(0) : null; } // get clinical statements that conform to clazz and are accepted by filter public static List getClinicalStatements(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List clinicalStatements = new ArrayList(); for (T clinicalStatement : getClinicalStatements(clinicalDocument, clazz)) { if (filter.accept(clinicalStatement)) { clinicalStatements.add(clinicalStatement); } } return clinicalStatements; } // get clinical statements that conform to clazz public static List getClinicalStatements(ClinicalDocument clinicalDocument, Class clazz) { List clinicalStatements = new ArrayList(); for (EObject clinicalStatement : getAllClinicalStatements(clinicalDocument)) { if (clazz.isInstance(clinicalStatement)) { clinicalStatements.add(clazz.cast(clinicalStatement)); } } return clinicalStatements; } // get all clinical statements in the document (closure) public static List getAllClinicalStatements(ClinicalDocument clinicalDocument) { List allClinicalStatements = new ArrayList(); for (Section section : getAllSections(clinicalDocument)) { allClinicalStatements.addAll(getClinicalStatements(section)); } return allClinicalStatements; } private static List getClinicalStatements(Section section) { List clinicalStatements = new ArrayList(); for (Entry entry : section.getEntries()) { ClinicalStatement clinicalStatement = getClinicalStatement(entry); if (clinicalStatement != null) { clinicalStatements.addAll(getClinicalStatements(clinicalStatement)); } } return clinicalStatements; } // iterative breadth-first traversal using queue private static List getClinicalStatements(ClinicalStatement clinicalStatement) { List clinicalStatements = new ArrayList(); Queue queue = new LinkedList(); queue.add(clinicalStatement); // root while (!queue.isEmpty()) { ClinicalStatement stmt = queue.remove(); clinicalStatements.add(stmt); // visit if (stmt instanceof Organizer) { Organizer organizer = (Organizer) stmt; for (Component4 component : organizer.getComponents()) { // process successors ClinicalStatement child = getClinicalStatement(component); if (child != null) { queue.add(child); } } } else { for (EntryRelationship entryRelationship : getEntryRelationships(stmt)) { // process successors ClinicalStatement child = getClinicalStatement(entryRelationship); if (child != null) { queue.add(child); } } } } return clinicalStatements; } private static List getEntryRelationships(ClinicalStatement clinicalStatement) { if (clinicalStatement instanceof Act) { return ((Act) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof Encounter) { return ((Encounter) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof Observation) { return ((Observation) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof ObservationMedia) { return ((ObservationMedia) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof Procedure) { return ((Procedure) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof RegionOfInterest) { return ((RegionOfInterest) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof SubstanceAdministration) { return ((SubstanceAdministration) clinicalStatement).getEntryRelationships(); } if (clinicalStatement instanceof Supply) { return ((Supply) clinicalStatement).getEntryRelationships(); } return Collections. emptyList(); } private static ClinicalStatement getClinicalStatement(Entry entry) { if (entry.getAct() != null) { return entry.getAct(); } if (entry.getEncounter() != null) { return entry.getEncounter(); } if (entry.getObservation() != null) { return entry.getObservation(); } if (entry.getObservationMedia() != null) { return entry.getObservationMedia(); } if (entry.getOrganizer() != null) { return entry.getOrganizer(); } if (entry.getProcedure() != null) { return entry.getProcedure(); } if (entry.getRegionOfInterest() != null) { return entry.getRegionOfInterest(); } if (entry.getSubstanceAdministration() != null) { return entry.getSubstanceAdministration(); } if (entry.getSupply() != null) { return entry.getSupply(); } return null; } private static ClinicalStatement getClinicalStatement(EntryRelationship entryRelationship) { if (entryRelationship.getAct() != null) { return entryRelationship.getAct(); } if (entryRelationship.getEncounter() != null) { return entryRelationship.getEncounter(); } if (entryRelationship.getObservation() != null) { return entryRelationship.getObservation(); } if (entryRelationship.getObservationMedia() != null) { return entryRelationship.getObservationMedia(); } if (entryRelationship.getOrganizer() != null) { return entryRelationship.getOrganizer(); } if (entryRelationship.getProcedure() != null) { return entryRelationship.getProcedure(); } if (entryRelationship.getRegionOfInterest() != null) { return entryRelationship.getRegionOfInterest(); } if (entryRelationship.getSubstanceAdministration() != null) { return entryRelationship.getSubstanceAdministration(); } if (entryRelationship.getSupply() != null) { return entryRelationship.getSupply(); } return null; } private static ClinicalStatement getClinicalStatement(Component4 component) { if (component.getAct() != null) { return component.getAct(); } if (component.getEncounter() != null) { return component.getEncounter(); } if (component.getObservation() != null) { return component.getObservation(); } if (component.getObservationMedia() != null) { return component.getObservationMedia(); } if (component.getOrganizer() != null) { return component.getOrganizer(); } if (component.getProcedure() != null) { return component.getProcedure(); } if (component.getRegionOfInterest() != null) { return component.getRegionOfInterest(); } if (component.getSubstanceAdministration() != null) { return component.getSubstanceAdministration(); } if (component.getSupply() != null) { return component.getSupply(); } return null; } // get first object that conforms to clazz and is accepted by filter public static T getEObject(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List eObjects = getEObjects(clinicalDocument, clazz, filter); return !eObjects.isEmpty() ? eObjects.get(0) : null; } // get first object that conforms to clazz public static T getEObject(ClinicalDocument clinicalDocument, Class clazz) { List eObjects = getEObjects(clinicalDocument, clazz); return !eObjects.isEmpty() ? eObjects.get(0) : null; } // get objects that conform to clazz and are accepted by filter public static List getEObjects(ClinicalDocument clinicalDocument, Class clazz, Filter filter) { List eObjects = new ArrayList(); for (T eObject : getEObjects(clinicalDocument, clazz)) { if (filter.accept(eObject)) { eObjects.add(eObject); } } return eObjects; } // get objects that conform to clazz public static List getEObjects(ClinicalDocument clinicalDocument, Class clazz) { List eObjects = new ArrayList(); for (EObject eObject : getAllEObjects(clinicalDocument)) { if (clazz.isInstance(eObject)) { eObjects.add(clazz.cast(eObject)); } } return eObjects; } // get all objects in the document (closure) // iterative breadth-first traversal using queue public static List getAllEObjects(ClinicalDocument clinicalDocument) { List allEObjects = new ArrayList(); Queue queue = new LinkedList(); queue.add(clinicalDocument); // root while (!queue.isEmpty()) { EObject eObject = queue.remove(); allEObjects.add(eObject); // visit for (EObject child : eObject.eContents()) { // process successors queue.add(child); } } return allEObjects; } // END: Experimental Query/Filter operations // BEGIN: CDA XPath Support public static Map CACHE = new HashMap(); // factory method to create CDAXPath instances using cache public static CDAXPath createCDAXPath(ClinicalDocument clinicalDocument) { CDAXPath xpath = CACHE.get(clinicalDocument); if (xpath == null) { try { xpath = new CDAXPath(clinicalDocument); CACHE.put(clinicalDocument, xpath); } catch (Exception e) { } } return xpath; } public static class CDAXPath { private ClinicalDocument clinicalDocument = null; private Document document = null; private DocumentRoot documentRoot = null; private Map nodeToObject = null; private Map objectToNode = null; private XPath xpath = null; public CDAXPath(ClinicalDocument clinicalDocument) throws Exception { this.clinicalDocument = clinicalDocument; nodeToObject = new HashMap(); objectToNode = new HashMap(); document = CDAUtil.save(clinicalDocument, new DOMHandler() { public DOMHelper getDOMHelper() { return null; } public void recordValues(Node node, EObject container, EStructuralFeature feature, Object value) { if (value != null) { nodeToObject.put(node, value); objectToNode.put(value, node); } } }); documentRoot = (DocumentRoot) clinicalDocument.eContainer(); nodeToObject.put(document, documentRoot); objectToNode.put(documentRoot, document); xpath = XPathFactory.newInstance().newXPath(); xpath.setNamespaceContext(new NamespaceContext() { public String getNamespaceURI(String prefix) { if ("cda".equals(prefix)) { return "urn:hl7-org:v3"; } else if ("sdtc".equals(prefix)) { return "urn:hl7-org:sdtc"; } else if ("xsi".equals(prefix)) { return "http://www.w3.org/2001/XMLSchema-instance"; } return null; } public String getPrefix(String namespaceURI) { if ("urn:hl7-org:v3".equals(namespaceURI)) { return "cda"; } else if ("urn:hl7-org:sdtc".equals(namespaceURI)) { return "sdtc"; } else if ("http://www.w3.org/2001/XMLSchema-instance".equals(namespaceURI)) { return "xsi"; } return null; } public Iterator getPrefixes(String namespaceURI) { return null; } }); } public Object evaluate(Node item, String expr, QName returnType) throws Exception { XPathExpression expression = xpath.compile(expr); return expression.evaluate(item, returnType); } public T evaluate(Object item, String expr, Class clazz) throws Exception { QName returnType = null; if (clazz.equals(Double.class)) { returnType = XPathConstants.NUMBER; } else if (clazz.equals(String.class)) { returnType = XPathConstants.STRING; } else if (clazz.equals(Boolean.class)) { returnType = XPathConstants.BOOLEAN; } else if (clazz.equals(NodeList.class)) { returnType = XPathConstants.NODESET; } else if (clazz.equals(Node.class)) { returnType = XPathConstants.NODE; } return returnType != null ? clazz.cast(evaluate(getNode(item), expr, returnType)) : null; } public T evaluate(String expr, Class clazz) throws Exception { return evaluate(documentRoot, expr, clazz); } public Object evaluate(String expr, QName returnType) throws Exception { return evaluate(document, expr, returnType); } public ClinicalDocument getClinicalDocument() { return clinicalDocument; } public Document getDocument() { return document; } public DocumentRoot getDocumentRoot() { return documentRoot; } public Node getNode(Object object) { return objectToNode.get(object); } public Object getObject(Node node) { return nodeToObject.get(node); } public List selectNodes(Node item, String expr) throws Exception { List result = new ArrayList(); NodeList nodeList = (NodeList) evaluate(item, expr, XPathConstants.NODESET); if (nodeList != null) { for (int i = 0; i < nodeList.getLength(); i++) { result.add(nodeList.item(i)); } } return result; } public List selectNodes(Object item, String expr, Class clazz) throws Exception { List result = new ArrayList(); for (Node node : selectNodes(getNode(item), expr)) { Object object = getObject(node); if (clazz.isInstance(object)) { result.add(clazz.cast(object)); } } return result; } public List selectNodes(String expr) throws Exception { return selectNodes(document, expr); } public List selectNodes(String expr, Class clazz) throws Exception { return selectNodes(documentRoot, expr, clazz); } public Node selectSingleNode(Node item, String expr) throws Exception { List result = selectNodes(item, expr); return !result.isEmpty() ? result.get(0) : null; } public T selectSingleNode(Object item, String expr, Class clazz) throws Exception { List result = selectNodes(item, expr, clazz); return !result.isEmpty() ? result.get(0) : null; } public Node selectSingleNode(String expr) throws Exception { return selectSingleNode(document, expr); } public T selectSingleNode(String expr, Class clazz) throws Exception { return selectSingleNode(documentRoot, expr, clazz); } } // END: CDA XPath Support public static String getPath(EObject eObject) { String path = ""; while (eObject != null && !(eObject instanceof DocumentRoot)) { EStructuralFeature feature = eObject.eContainingFeature(); EObject container = eObject.eContainer(); Object value = container.eGet(feature); if (feature.isMany()) { List list = (List) value; int index = list.indexOf(eObject) + 1; if (index > 1) { path = "/" + getName(feature) + "[" + index + "]" + path; } else { path = "/" + getName(feature) + path; } } else { path = "/" + getName(feature) + path; } eObject = eObject.eContainer(); } return path; } public static String getName(ENamedElement eNamedElement) { String result = EcoreUtil.getAnnotation(eNamedElement, ExtendedMetaData.ANNOTATION_URI, "name"); if (result != null) { return result; } return eNamedElement.getName(); } public static void loadPackages() { CDAPackageLoader.loadPackages(); } public static void loadPackages(String location) { CDAPackageLoader.loadPackages(location); } }