Commit 91a4ff99 authored by Thomas Huster's avatar Thomas Huster
Browse files

[17381] updated core hapi fhir to version, removed dstu2

parent 3b6305b7
Loading
Loading
Loading
Loading
+0 −268
Original line number Diff line number Diff line
package ch.elexis.core.findings.fhir.po.service.internal;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.INarrative;
import org.slf4j.LoggerFactory;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.cache.AlwaysValidCacheEntryValidity;
import org.thymeleaf.cache.ICacheEntryValidity;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.standard.StandardDialect;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.DefaultTemplateResolver;
import org.thymeleaf.templateresource.ITemplateResource;
import org.thymeleaf.templateresource.StringTemplateResource;

import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.parser.DataFormatException;
import ch.elexis.core.findings.IFindingsService;

public class CustomNarrativeGenerator implements ca.uhn.fhir.narrative.INarrativeGenerator {

	private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomNarrativeGenerator.class);

	private boolean initialized;

	private HashMap<Class<?>, String> classToName;
	private HashMap<String, String> nameToNarrativeTemplate;

	private TemplateEngine profileTemplateEngine;

	@Override
	public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
		if (!initialized) {
			initialize(theContext);
		}

		String name = null;
		if (name == null) {
			name = classToName.get(theResource.getClass());
		}
		if (name == null) {
			name = theContext.getResourceDefinition(theResource).getName().toLowerCase();
		}

		if (name == null || !nameToNarrativeTemplate.containsKey(name)) {
			logger.debug("No narrative template available for resorce: {}", name);
			return;
		}

		try {
			Context context = new Context();
			context.setVariable("resource", theResource);
			context.setVariable("fhirVersion", theContext.getVersion().getVersion().name());

			String result = profileTemplateEngine.process(name, context);

			if (result == null || result.trim().isEmpty()) {
				return;
			}

			theNarrative.setDivAsString(result);
			theNarrative.setStatusAsString("generated");
			return;
		} catch (Exception e) {
			logger.error("Failed to generate narrative", e);
		}
	}

	private synchronized void initialize(final FhirContext theContext) {
		if (initialized) {
			return;
		}

		logger.info("Initializing narrative generator");

		classToName = new HashMap<Class<?>, String>();
		nameToNarrativeTemplate = new HashMap<String, String>();

		try {
			loadProperties("/rsc/narrative/custom.properties");
		} catch (IOException e) {
			logger.info("Failed to load property file.", e);
		}

		profileTemplateEngine = new TemplateEngine();
		ProfileResourceResolver resolver = new ProfileResourceResolver();
		profileTemplateEngine.setTemplateResolver(resolver);
		StandardDialect dialect = new StandardDialect() {
			public Set<IProcessor> getProcessors(String theDialectPrefix) {
				Set<IProcessor> retVal = super.getProcessors(theDialectPrefix);
				retVal.add(new NarrativeAttributeProcessor(theContext, theDialectPrefix));
				return retVal;
			}
		};
		profileTemplateEngine.setDialect(dialect);

		initialized = true;
	}

	private InputStream loadResource(String name) {
		return IFindingsService.class.getResourceAsStream(name);
	}

	private void loadProperties(String propFileName) throws IOException {
		Properties file = new Properties();
		InputStream resource = IFindingsService.class.getResourceAsStream(propFileName);
		file.load(resource);
		for (Object nextKeyObj : file.keySet()) {
			String nextKey = (String) nextKeyObj;
			if (nextKey.endsWith(".profile")) {
				String name = nextKey.substring(0, nextKey.indexOf(".profile"));
				if (name == null || name.trim().isEmpty()) {
					continue;
				}
				String narrativePropName = name + ".narrative";
				String narrativeName = file.getProperty(narrativePropName);
				if (narrativeName != null && !narrativeName.trim().isEmpty()) {
					String narrative = IOUtils.toString(loadResource(narrativeName), "UTF-8");
					nameToNarrativeTemplate.put(name, narrative);
				}
			} else if (nextKey.endsWith(".class")) {
				String name = nextKey.substring(0, nextKey.indexOf(".class"));
				if (name == null || name.trim().isEmpty()) {
					continue;
				}
				String className = file.getProperty(nextKey);
				Class<?> clazz;
				try {
					clazz = Class.forName(className);
				} catch (ClassNotFoundException e) {
					logger.debug("Unknown datatype class '{}' identified in narrative file {}", name, propFileName);
					clazz = null;
				}

				if (clazz != null) {
					classToName.put(clazz, name);
				}
			} else if (nextKey.endsWith(".narrative")) {
				String name = nextKey.substring(0, nextKey.indexOf(".narrative"));
				if (name == null || name.trim().isEmpty()) {
					continue;
				}
				String narrativePropName = name + ".narrative";
				String narrativeName = file.getProperty(narrativePropName);
				if (narrativeName != null && !narrativeName.trim().isEmpty()) {
					String narrative = IOUtils.toString(loadResource(narrativeName), "UTF-8");
					nameToNarrativeTemplate.put(name, narrative);
				}
				continue;
			} else {
				throw new ConfigurationException("Invalid property name: " + nextKey);
			}
		}
	}

	public class NarrativeAttributeProcessor extends AbstractAttributeTagProcessor {

		private FhirContext fhirContext;

		protected NarrativeAttributeProcessor(FhirContext theContext, String theDialectPrefix) {
			super(TemplateMode.XML, theDialectPrefix, null, false, "narrative", true, 0, true);
			this.fhirContext = theContext;
		}

		protected void doProcess(ITemplateContext theContext, IProcessableElementTag theTag,
				AttributeName theAttributeName, String theAttributeValue,
				IElementTagStructureHandler theStructureHandler) {
			IEngineConfiguration configuration = theContext.getConfiguration();
			IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(configuration);

			final IStandardExpression expression = expressionParser.parseExpression(theContext, theAttributeValue);
			final Object value = expression.execute(theContext);

			if (value == null) {
				return;
			}

			Context context = new Context();
			context.setVariable("fhirVersion", fhirContext.getVersion().getVersion().name());
			context.setVariable("resource", value);

			String name = null;
			if (value != null) {
				Class<? extends Object> nextClass = value.getClass();
				do {
					name = classToName.get(nextClass);
					nextClass = nextClass.getSuperclass();
				} while (name == null && nextClass.equals(Object.class) == false);

				if (name == null) {
					if (value instanceof IBaseResource) {
						name = fhirContext.getResourceDefinition((Class<? extends IBaseResource>) value).getName();
					} else if (value instanceof IDatatype) {
						name = value.getClass().getSimpleName();
						name = name.substring(0, name.length() - 2);
					} else if (value instanceof IBaseDatatype) {
						name = value.getClass().getSimpleName();
						if (name.endsWith("Type")) {
							name = name.substring(0, name.length() - 4);
						}
					} else {
						throw new DataFormatException("Don't know how to determine name for type: " + value.getClass());
					}
					name = name.toLowerCase();
					if (!nameToNarrativeTemplate.containsKey(name)) {
						name = null;
					}
				}
			}

			if (name == null) {
				logger.debug("No narrative template available for type: {}", value.getClass());
				return;
			}

			String result = profileTemplateEngine.process(name, context);
			String trim = result.trim();

			theStructureHandler.setBody(trim, true);
		}
	}

	private final class ProfileResourceResolver extends DefaultTemplateResolver {

		protected boolean computeResolvable(IEngineConfiguration theConfiguration, String theOwnerTemplate,
				String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
			String template = nameToNarrativeTemplate.get(theTemplate);
			return template != null;
		}

		protected TemplateMode computeTemplateMode(IEngineConfiguration theConfiguration, String theOwnerTemplate,
				String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
			return TemplateMode.XML;
		}

		protected ITemplateResource computeTemplateResource(IEngineConfiguration theConfiguration,
				String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
			String template = nameToNarrativeTemplate.get(theTemplate);
			return new StringTemplateResource(template);
		}

		protected ICacheEntryValidity computeValidity(IEngineConfiguration theConfiguration, String theOwnerTemplate,
				String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
			return AlwaysValidCacheEntryValidity.INSTANCE;
		}
	}
}