Narrative Generator refactoring (#1219)

* Starting work on this

* Working on narrative templates

* Work on new narrative system

* Account for some failing tests due to refactoring of narrative mode

* Another test fix

* More test fixes

* One more test fix

* Work on searches

* Address review comments
This commit is contained in:
James Agnew 2019-02-27 14:59:57 -05:00 committed by GitHub
parent 94f9ffa977
commit 48c10bddc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 2555 additions and 1867 deletions

View File

@ -108,7 +108,7 @@ public class JpaServerDemo extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing

View File

@ -108,7 +108,7 @@ public class JpaServerDemoDstu2 extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing

View File

@ -75,7 +75,7 @@ public class JpaServerDemo extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing

View File

@ -21,7 +21,7 @@ patient.addAddress().addLine("742 Evergreen Terrace").setCity("Springfield").set
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forDstu2();
// Use the narrative generator // Use the narrative generator
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ctx));
// Encode the output, including the narrative // Encode the output, including the narrative
String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient); String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);

View File

@ -10,10 +10,10 @@ public class NarrativeGenerator {
public void testGenerator() throws IOException { public void testGenerator() throws IOException {
//START SNIPPET: gen //START SNIPPET: gen
String propFile = "classpath:/com/foo/customnarrative.properties";
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(propFile);
FhirContext ctx = FhirContext.forDstu2(); FhirContext ctx = FhirContext.forDstu2();
String propFile = "classpath:/com/foo/customnarrative.properties";
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(ctx, propFile);
ctx.setNarrativeGenerator(gen); ctx.setNarrativeGenerator(gen);
//END SNIPPET: gen //END SNIPPET: gen

View File

@ -8,7 +8,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.PrePopulatedValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.PrePopulatedValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;

View File

@ -8,6 +8,7 @@ import javax.servlet.ServletException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.*; import org.hl7.fhir.dstu3.hapi.validation.*;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;

View File

@ -1,8 +1,7 @@
package example; package example;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;

View File

@ -128,8 +128,9 @@ public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement im
} }
@Override @Override
public void setResource(IBaseResource theResource) { public BaseResourceReferenceDt setResource(IBaseResource theResource) {
myResource = theResource; myResource = theResource;
return this;
} }
@Override @Override

View File

@ -19,588 +19,53 @@ package ca.uhn.fhir.narrative;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.*;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.*;
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.messageresolver.IMessageResolver;
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.*;
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.context.FhirContext;
import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.narrative2.NarrativeTemplateManifest;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.narrative2.ThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBaseResource;
public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGenerator { import java.io.IOException;
import java.util.List;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseThymeleafNarrativeGenerator.class); public abstract class BaseThymeleafNarrativeGenerator extends ThymeleafNarrativeGenerator {
private boolean myApplyDefaultDatatypeTemplates = true; private boolean myInitialized;
private HashMap<Class<?>, String> myClassToName;
private boolean myCleanWhitespace = true;
private boolean myIgnoreFailures = true;
private boolean myIgnoreMissingTemplates = true;
private volatile boolean myInitialized;
private HashMap<String, String> myNameToNarrativeTemplate;
private TemplateEngine myProfileTemplateEngine;
private IMessageResolver resolver;
/** /**
* Constructor * Constructor
*/ */
public BaseThymeleafNarrativeGenerator() { public BaseThymeleafNarrativeGenerator(FhirContext theFhirContext) {
super(); super(theFhirContext);
} }
@Override @Override
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) { public boolean populateResourceNarrative(IBaseResource theResource) {
if (!myInitialized) { if (!myInitialized) {
initialize(theContext); initialize();
} }
super.populateResourceNarrative(theResource);
String name = myClassToName.get(theResource.getClass()); return false;
if (name == null) {
name = theContext.getResourceDefinition(theResource).getName().toLowerCase();
}
if (name == null || !myNameToNarrativeTemplate.containsKey(name)) {
if (myIgnoreMissingTemplates) {
ourLog.debug("No narrative template available for resorce: {}", name);
return;
}
throw new DataFormatException("No narrative template for class " + theResource.getClass().getCanonicalName());
}
try {
Context context = new Context();
context.setVariable("resource", theResource);
context.setVariable("fhirVersion", theContext.getVersion().getVersion().name());
String result = myProfileTemplateEngine.process(name, context);
if (myCleanWhitespace) {
ourLog.trace("Pre-whitespace cleaning: ", result);
result = cleanWhitespace(result);
ourLog.trace("Post-whitespace cleaning: ", result);
}
if (isBlank(result)) {
return;
}
theNarrative.setDivAsString(result);
theNarrative.setStatusAsString("generated");
return;
} catch (Exception e) {
if (myIgnoreFailures) {
ourLog.error("Failed to generate narrative", e);
try {
theNarrative.setDivAsString("<div>No narrative available - Error: " + e.getMessage() + "</div>");
} catch (Exception e1) {
// last resort..
}
theNarrative.setStatusAsString("empty");
return;
}
throw new DataFormatException(e);
}
} }
protected abstract List<String> getPropertyFile(); protected abstract List<String> getPropertyFile();
private synchronized void initialize(final FhirContext theContext) { private synchronized void initialize() {
if (myInitialized) { if (myInitialized) {
return; return;
} }
ourLog.info("Initializing narrative generator");
myClassToName = new HashMap<Class<?>, String>();
myNameToNarrativeTemplate = new HashMap<String, String>();
List<String> propFileName = getPropertyFile(); List<String> propFileName = getPropertyFile();
try { try {
if (myApplyDefaultDatatypeTemplates) { NarrativeTemplateManifest manifest = NarrativeTemplateManifest.forManifestFileLocation(getFhirContext(), propFileName);
loadProperties(DefaultThymeleafNarrativeGenerator.NARRATIVES_PROPERTIES); setManifest(manifest);
}
for (String next : propFileName) {
loadProperties(next);
}
} catch (IOException e) { } catch (IOException e) {
ourLog.info("Failed to load property file " + propFileName, e); throw new InternalErrorException(e);
throw new ConfigurationException("Can not load property file " + propFileName, e);
}
{
myProfileTemplateEngine = new TemplateEngine();
ProfileResourceResolver resolver = new ProfileResourceResolver();
myProfileTemplateEngine.setTemplateResolver(resolver);
StandardDialect dialect = new StandardDialect() {
@Override
public Set<IProcessor> getProcessors(String theDialectPrefix) {
Set<IProcessor> retVal = super.getProcessors(theDialectPrefix);
retVal.add(new NarrativeAttributeProcessor(theContext, theDialectPrefix));
return retVal;
}
};
myProfileTemplateEngine.setDialect(dialect);
if (this.resolver != null) {
myProfileTemplateEngine.setMessageResolver(this.resolver);
}
} }
myInitialized = true; myInitialized = true;
} }
public void setMessageResolver(IMessageResolver resolver) {
this.resolver = resolver;
if (myProfileTemplateEngine != null && resolver != null) {
myProfileTemplateEngine.setMessageResolver(resolver);
}
}
/**
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative
* before it is returned.
* <p>
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g.
* "\n \n ") will be trimmed to a single space.
* </p>
*/
public boolean isCleanWhitespace() {
return myCleanWhitespace;
}
/**
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the
* generator will suppress any generated exceptions, and simply return a default narrative indicating that no
* narrative is available.
*/
public boolean isIgnoreFailures() {
return myIgnoreFailures;
}
/**
* If set to true, will return an empty narrative block for any profiles where no template is available
*/
public boolean isIgnoreMissingTemplates() {
return myIgnoreMissingTemplates;
}
private void loadProperties(String propFileName) throws IOException {
ourLog.debug("Loading narrative properties file: {}", propFileName);
Properties file = new Properties();
InputStream resource = loadResource(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 (isBlank(name)) {
continue;
}
String narrativePropName = name + ".narrative";
String narrativeName = file.getProperty(narrativePropName);
if (isBlank(narrativeName)) {
//FIXME resource leak
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName);
}
if (StringUtils.isNotBlank(narrativeName)) {
String narrative = IOUtils.toString(loadResource(narrativeName), Constants.CHARSET_UTF8);
myNameToNarrativeTemplate.put(name, narrative);
}
} else if (nextKey.endsWith(".class")) {
String name = nextKey.substring(0, nextKey.indexOf(".class"));
if (isBlank(name)) {
continue;
}
String className = file.getProperty(nextKey);
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
ourLog.debug("Unknown datatype class '{}' identified in narrative file {}", name, propFileName);
clazz = null;
}
if (clazz != null) {
myClassToName.put(clazz, name);
}
} else if (nextKey.endsWith(".narrative")) {
String name = nextKey.substring(0, nextKey.indexOf(".narrative"));
if (isBlank(name)) {
continue;
}
String narrativePropName = name + ".narrative";
String narrativeName = file.getProperty(narrativePropName);
if (StringUtils.isNotBlank(narrativeName)) {
String narrative = IOUtils.toString(loadResource(narrativeName), Constants.CHARSET_UTF8);
myNameToNarrativeTemplate.put(name, narrative);
}
continue;
} else if (nextKey.endsWith(".title")) {
ourLog.debug("Ignoring title property as narrative generator no longer generates titles: {}", nextKey);
} else {
throw new ConfigurationException("Invalid property name: " + nextKey);
}
}
}
private InputStream loadResource(String name) throws IOException {
if (name.startsWith("classpath:")) {
String cpName = name.substring("classpath:".length());
InputStream resource = DefaultThymeleafNarrativeGenerator.class.getResourceAsStream(cpName);
if (resource == null) {
resource = DefaultThymeleafNarrativeGenerator.class.getResourceAsStream("/" + cpName);
if (resource == null) {
throw new IOException("Can not find '" + cpName + "' on classpath");
}
}
//FIXME resource leak
return resource;
} else if (name.startsWith("file:")) {
File file = new File(name.substring("file:".length()));
if (file.exists() == false) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
return new FileInputStream(file);
} else {
throw new IOException("Invalid resource name: '" + name + "' (must start with classpath: or file: )");
}
}
/**
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative
* before it is returned.
* <p>
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g.
* "\n \n ") will be trimmed to a single space.
* </p>
*/
public void setCleanWhitespace(boolean theCleanWhitespace) {
myCleanWhitespace = theCleanWhitespace;
}
/**
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the
* generator will suppress any generated exceptions, and simply return a default narrative indicating that no
* narrative is available.
*/
public void setIgnoreFailures(boolean theIgnoreFailures) {
myIgnoreFailures = theIgnoreFailures;
}
/**
* If set to true, will return an empty narrative block for any profiles where no template is available
*/
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
myIgnoreMissingTemplates = theIgnoreMissingTemplates;
}
static String cleanWhitespace(String theResult) {
StringBuilder b = new StringBuilder();
boolean inWhitespace = false;
boolean betweenTags = false;
boolean lastNonWhitespaceCharWasTagEnd = false;
boolean inPre = false;
for (int i = 0; i < theResult.length(); i++) {
char nextChar = theResult.charAt(i);
if (inPre) {
b.append(nextChar);
continue;
} else if (nextChar == '>') {
b.append(nextChar);
betweenTags = true;
lastNonWhitespaceCharWasTagEnd = true;
continue;
} else if (nextChar == '\n' || nextChar == '\r') {
// if (inWhitespace) {
// b.append(' ');
// inWhitespace = false;
// }
continue;
}
if (betweenTags) {
if (Character.isWhitespace(nextChar)) {
inWhitespace = true;
} else if (nextChar == '<') {
if (inWhitespace && !lastNonWhitespaceCharWasTagEnd) {
b.append(' ');
}
inWhitespace = false;
b.append(nextChar);
inWhitespace = false;
betweenTags = false;
lastNonWhitespaceCharWasTagEnd = false;
if (i + 3 < theResult.length()) {
char char1 = Character.toLowerCase(theResult.charAt(i + 1));
char char2 = Character.toLowerCase(theResult.charAt(i + 2));
char char3 = Character.toLowerCase(theResult.charAt(i + 3));
char char4 = Character.toLowerCase((i + 4 < theResult.length()) ? theResult.charAt(i + 4) : ' ');
if (char1 == 'p' && char2 == 'r' && char3 == 'e') {
inPre = true;
} else if (char1 == '/' && char2 == 'p' && char3 == 'r' && char4 == 'e') {
inPre = false;
}
}
} else {
lastNonWhitespaceCharWasTagEnd = false;
if (inWhitespace) {
b.append(' ');
inWhitespace = false;
}
b.append(nextChar);
}
} else {
b.append(nextChar);
}
}
return b.toString();
}
public class NarrativeAttributeProcessor extends AbstractAttributeTagProcessor {
private FhirContext myContext;
protected NarrativeAttributeProcessor(FhirContext theContext, String theDialectPrefix) {
super(TemplateMode.XML, theDialectPrefix, null, false, "narrative", true, 0, true);
myContext = theContext;
}
@SuppressWarnings("unchecked")
@Override
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", myContext.getVersion().getVersion().name());
context.setVariable("resource", value);
String name = null;
Class<? extends Object> nextClass = value.getClass();
do {
name = myClassToName.get(nextClass);
nextClass = nextClass.getSuperclass();
} while (name == null && nextClass.equals(Object.class) == false);
if (name == null) {
if (value instanceof IBaseResource) {
name = myContext.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 (!myNameToNarrativeTemplate.containsKey(name)) {
name = null;
}
}
if (name == null) {
if (myIgnoreMissingTemplates) {
ourLog.debug("No narrative template available for type: {}", value.getClass());
return;
}
throw new DataFormatException("No narrative template for class " + value.getClass());
}
String result = myProfileTemplateEngine.process(name, context);
String trim = result.trim();
theStructureHandler.setBody(trim, true);
}
}
// public class NarrativeAttributeProcessor extends AbstractAttributeTagProcessor {
//
// private FhirContext myContext;
//
// protected NarrativeAttributeProcessor(FhirContext theContext) {
// super()
// myContext = theContext;
// }
//
// @Override
// public int getPrecedence() {
// return 0;
// }
//
// @SuppressWarnings("unchecked")
// @Override
// protected ProcessorResult processAttribute(Arguments theArguments, Element theElement, String theAttributeName) {
// final String attributeValue = theElement.getAttributeValue(theAttributeName);
//
// final Configuration configuration = theArguments.getConfiguration();
// final IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(configuration);
//
// final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue);
// final Object value = expression.execute(configuration, theArguments);
//
// theElement.removeAttribute(theAttributeName);
// theElement.clearChildren();
//
// if (value == null) {
// return ProcessorResult.ok();
// }
//
// Context context = new Context();
// context.setVariable("fhirVersion", myContext.getVersion().getVersion().name());
// context.setVariable("resource", value);
//
// String name = null;
// if (value != null) {
// Class<? extends Object> nextClass = value.getClass();
// do {
// name = myClassToName.get(nextClass);
// nextClass = nextClass.getSuperclass();
// } while (name == null && nextClass.equals(Object.class) == false);
//
// if (name == null) {
// if (value instanceof IBaseResource) {
// name = myContext.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 (!myNameToNarrativeTemplate.containsKey(name)) {
// name = null;
// }
// }
// }
//
// if (name == null) {
// if (myIgnoreMissingTemplates) {
// ourLog.debug("No narrative template available for type: {}", value.getClass());
// return ProcessorResult.ok();
// } else {
// throw new DataFormatException("No narrative template for class " + value.getClass());
// }
// }
//
// String result = myProfileTemplateEngine.process(name, context);
// String trim = result.trim();
// if (!isBlank(trim + "AAA")) {
// Document dom = getXhtmlDOMFor(new StringReader(trim));
//
// Element firstChild = (Element) dom.getFirstChild();
// for (int i = 0; i < firstChild.getChildren().size(); i++) {
// Node next = firstChild.getChildren().get(i);
// if (i == 0 && firstChild.getChildren().size() == 1) {
// if (next instanceof org.thymeleaf.dom.Text) {
// org.thymeleaf.dom.Text nextText = (org.thymeleaf.dom.Text) next;
// nextText.setContent(nextText.getContent().trim());
// }
// }
// theElement.addChild(next);
// }
//
// }
//
//
// return ProcessorResult.ok();
// }
//
// }
// public String generateString(Patient theValue) {
//
// Context context = new Context();
// context.setVariable("resource", theValue);
// String result =
// myProfileTemplateEngine.process("ca/uhn/fhir/narrative/Patient.html",
// context);
//
// ourLog.info("Result: {}", result);
//
// return result;
// }
private final class ProfileResourceResolver extends DefaultTemplateResolver {
@Override
protected boolean computeResolvable(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
String template = myNameToNarrativeTemplate.get(theTemplate);
return template != null;
}
@Override
protected TemplateMode computeTemplateMode(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return TemplateMode.XML;
}
@Override
protected ITemplateResource computeTemplateResource(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
String template = myNameToNarrativeTemplate.get(theTemplate);
return new StringTemplateResource(template);
}
@Override
protected ICacheEntryValidity computeValidity(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return AlwaysValidCacheEntryValidity.INSTANCE;
}
}
} }

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.narrative;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator { public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator {
@ -39,7 +40,8 @@ public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGen
* <li>classpath:/com/package/file.properties</li> * <li>classpath:/com/package/file.properties</li>
* </ul> * </ul>
*/ */
public CustomThymeleafNarrativeGenerator(String... thePropertyFile) { public CustomThymeleafNarrativeGenerator(FhirContext theFhirContext, String... thePropertyFile) {
super(theFhirContext);
setPropertyFile(thePropertyFile); setPropertyFile(thePropertyFile);
} }

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.narrative;
* #L% * #L%
*/ */
import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -30,6 +32,10 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
private boolean myUseHapiServerConformanceNarrative; private boolean myUseHapiServerConformanceNarrative;
public DefaultThymeleafNarrativeGenerator(FhirContext theFhirContext) {
super(theFhirContext);
}
@Override @Override
protected List<String> getPropertyFile() { protected List<String> getPropertyFile() {
List<String> retVal = new ArrayList<String>(); List<String> retVal = new ArrayList<String>();

View File

@ -21,12 +21,17 @@ package ca.uhn.fhir.narrative;
*/ */
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.INarrative;
import ca.uhn.fhir.context.FhirContext;
public interface INarrativeGenerator { public interface INarrativeGenerator {
void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative); /**
* Generate any narratives for the given resource that have applicable
* templates, and populates the appropriate field(s). This almost always means
* the <code>Resource.text.narrative</code> field, but for some resource types
* it can mean other fields (e.g. <code>Composition.</code>
*
* @return Returns <code>true</code> if a narrative was actually generated
*/
boolean populateResourceNarrative(IBaseResource theResource);
} }

View File

@ -0,0 +1,221 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.fluentpath.IFluentPath;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.INarrative;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseNarrativeGenerator implements INarrativeGenerator {
private INarrativeTemplateManifest myManifest;
private final FhirContext myFhirContext;
public BaseNarrativeGenerator(FhirContext theFhirContext) {
Validate.notNull(theFhirContext, "theFhirContext must not be null");
myFhirContext = theFhirContext;
}
public INarrativeTemplateManifest getManifest() {
return myManifest;
}
public void setManifest(INarrativeTemplateManifest theManifest) {
myManifest = theManifest;
}
public FhirContext getFhirContext() {
return myFhirContext;
}
@Override
public boolean populateResourceNarrative(IBaseResource theResource) {
Optional<INarrativeTemplate> templateOpt = getTemplateForElement(theResource);
if (templateOpt.isPresent()) {
return applyTemplate(templateOpt.get(), theResource);
} else {
return false;
}
}
private Optional<INarrativeTemplate> getTemplateForElement(IBase theElement) {
return myManifest.getTemplateByElement(getStyle(), theElement);
}
private boolean applyTemplate(INarrativeTemplate theTemplate, IBaseResource theResource) {
if (templateDoesntApplyToResource(theTemplate, theResource)) {
return false;
}
boolean retVal = false;
String resourceName = myFhirContext.getResourceDefinition(theResource).getName();
String contextPath = defaultIfEmpty(theTemplate.getContextPath(), resourceName);
// Narrative templates define a path within the resource that they apply to. Here, we're
// finding anywhere in the resource that gets a narrative
List<IBase> targets = findElementsInResourceRequiringNarratives(theResource, contextPath);
for (IBase nextTargetContext : targets) {
// Extract [element].text of type Narrative
INarrative nextTargetNarrative = getOrCreateNarrativeChildElement(nextTargetContext);
// Create the actual narrative text
String narrative = applyTemplate(theTemplate, nextTargetContext);
narrative = cleanWhitespace(narrative);
if (isNotBlank(narrative)) {
try {
nextTargetNarrative.setDivAsString(narrative);
nextTargetNarrative.setStatusAsString("generated");
retVal = true;
} catch (Exception e) {
throw new InternalErrorException(e);
}
}
}
return retVal;
}
private INarrative getOrCreateNarrativeChildElement(IBase nextTargetContext) {
BaseRuntimeElementCompositeDefinition<?> targetElementDef = (BaseRuntimeElementCompositeDefinition<?>) getFhirContext().getElementDefinition(nextTargetContext.getClass());
BaseRuntimeChildDefinition targetTextChild = targetElementDef.getChildByName("text");
List<IBase> existing = targetTextChild.getAccessor().getValues(nextTargetContext);
INarrative nextTargetNarrative;
if (existing.isEmpty()) {
nextTargetNarrative = (INarrative) getFhirContext().getElementDefinition("narrative").newInstance();
targetTextChild.getMutator().addValue(nextTargetContext, nextTargetNarrative);
} else {
nextTargetNarrative = (INarrative) existing.get(0);
}
return nextTargetNarrative;
}
private List<IBase> findElementsInResourceRequiringNarratives(IBaseResource theResource, String theContextPath) {
if (myFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
return Collections.singletonList(theResource);
}
IFluentPath fhirPath = myFhirContext.newFluentPath();
return fhirPath.evaluate(theResource, theContextPath, IBase.class);
}
protected abstract String applyTemplate(INarrativeTemplate theTemplate, IBase theTargetContext);
private boolean templateDoesntApplyToResource(INarrativeTemplate theTemplate, IBaseResource theResource) {
boolean retVal = false;
if (theTemplate.getAppliesToProfiles() != null && !theTemplate.getAppliesToProfiles().isEmpty()) {
Set<String> resourceProfiles = theResource
.getMeta()
.getProfile()
.stream()
.map(t -> t.getValueAsString())
.collect(Collectors.toSet());
retVal = true;
for (String next : theTemplate.getAppliesToProfiles()) {
if (resourceProfiles.contains(next)) {
retVal = false;
break;
}
}
}
return retVal;
}
protected abstract TemplateTypeEnum getStyle();
/**
* Trims the superfluous whitespace out of an HTML block
*/
public static String cleanWhitespace(String theResult) {
StringBuilder b = new StringBuilder();
boolean inWhitespace = false;
boolean betweenTags = false;
boolean lastNonWhitespaceCharWasTagEnd = false;
boolean inPre = false;
for (int i = 0; i < theResult.length(); i++) {
char nextChar = theResult.charAt(i);
if (inPre) {
b.append(nextChar);
continue;
} else if (nextChar == '>') {
b.append(nextChar);
betweenTags = true;
lastNonWhitespaceCharWasTagEnd = true;
continue;
} else if (nextChar == '\n' || nextChar == '\r') {
continue;
}
if (betweenTags) {
if (Character.isWhitespace(nextChar)) {
inWhitespace = true;
} else if (nextChar == '<') {
if (inWhitespace && !lastNonWhitespaceCharWasTagEnd) {
b.append(' ');
}
inWhitespace = false;
b.append(nextChar);
inWhitespace = false;
betweenTags = false;
lastNonWhitespaceCharWasTagEnd = false;
if (i + 3 < theResult.length()) {
char char1 = Character.toLowerCase(theResult.charAt(i + 1));
char char2 = Character.toLowerCase(theResult.charAt(i + 2));
char char3 = Character.toLowerCase(theResult.charAt(i + 3));
char char4 = Character.toLowerCase((i + 4 < theResult.length()) ? theResult.charAt(i + 4) : ' ');
if (char1 == 'p' && char2 == 'r' && char3 == 'e') {
inPre = true;
} else if (char1 == '/' && char2 == 'p' && char3 == 'r' && char4 == 'e') {
inPre = false;
}
}
} else {
lastNonWhitespaceCharWasTagEnd = false;
if (inWhitespace) {
b.append(' ');
inWhitespace = false;
}
b.append(nextChar);
}
} else {
b.append(nextChar);
}
}
return b.toString();
}
}

View File

@ -0,0 +1,42 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBase;
import java.io.IOException;
import java.util.Set;
public interface INarrativeTemplate {
String getContextPath();
Set<String> getAppliesToProfiles();
Set<String> getAppliesToResourceTypes();
Set<Class<? extends IBase>> getAppliesToResourceClasses();
TemplateTypeEnum getTemplateType();
String getTemplateName();
String getTemplateText();
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBase;
import java.util.Optional;
public interface INarrativeTemplateManifest {
Optional<INarrativeTemplate> getTemplateByResourceName(TemplateTypeEnum theStyle, String theResourceName);
Optional<INarrativeTemplate> getTemplateByName(TemplateTypeEnum theStyle, String theName);
Optional<INarrativeTemplate> getTemplateByElement(TemplateTypeEnum theStyle, IBase theElementValue);
}

View File

@ -0,0 +1,121 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBase;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class NarrativeTemplate implements INarrativeTemplate {
private String myTemplateFileName;
private Set<String> myAppliesToProfiles = new HashSet<>();
private Set<String> myAppliesToResourceTypes = new HashSet<>();
private Set<String> myAppliesToDataTypes = new HashSet<>();
private Set<Class<? extends IBase>> myAppliesToResourceClasses = new HashSet<>();
private TemplateTypeEnum myTemplateType = TemplateTypeEnum.THYMELEAF;
private String myContextPath;
private String myTemplateName;
public Set<String> getAppliesToDataTypes() {
return Collections.unmodifiableSet(myAppliesToDataTypes);
}
@Override
public String getContextPath() {
return myContextPath;
}
public void setContextPath(String theContextPath) {
myContextPath = theContextPath;
}
private String getTemplateFileName() {
return myTemplateFileName;
}
void setTemplateFileName(String theTemplateFileName) {
myTemplateFileName = theTemplateFileName;
}
@Override
public Set<String> getAppliesToProfiles() {
return Collections.unmodifiableSet(myAppliesToProfiles);
}
void addAppliesToProfile(String theAppliesToProfile) {
myAppliesToProfiles.add(theAppliesToProfile);
}
@Override
public Set<String> getAppliesToResourceTypes() {
return Collections.unmodifiableSet(myAppliesToResourceTypes);
}
void addAppliesToResourceType(String theAppliesToResourceType) {
myAppliesToResourceTypes.add(theAppliesToResourceType);
}
@Override
public Set<Class<? extends IBase>> getAppliesToResourceClasses() {
return Collections.unmodifiableSet(myAppliesToResourceClasses);
}
void addAppliesToResourceClass(Class<? extends IBase> theAppliesToResourceClass) {
myAppliesToResourceClasses.add(theAppliesToResourceClass);
}
@Override
public TemplateTypeEnum getTemplateType() {
return myTemplateType;
}
void setTemplateType(TemplateTypeEnum theTemplateType) {
myTemplateType = theTemplateType;
}
@Override
public String getTemplateName() {
return myTemplateName;
}
NarrativeTemplate setTemplateName(String theTemplateName) {
myTemplateName = theTemplateName;
return this;
}
@Override
public String getTemplateText() {
try {
return NarrativeTemplateManifest.loadResource(getTemplateFileName());
} catch (IOException e) {
throw new InternalErrorException(e);
}
}
void addAppliesToDatatype(String theDataType) {
myAppliesToDataTypes.add(theDataType);
}
}

View File

@ -0,0 +1,239 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class NarrativeTemplateManifest implements INarrativeTemplateManifest {
private static final Logger ourLog = LoggerFactory.getLogger(NarrativeTemplateManifest.class);
private final Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> myStyleToResourceTypeToTemplate;
private final Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> myStyleToDatatypeToTemplate;
private final Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> myStyleToNameToTemplate;
private final FhirContext myCtx;
private final int myTemplateCount;
private NarrativeTemplateManifest(FhirContext theFhirContext, Collection<NarrativeTemplate> theTemplates) {
myCtx = theFhirContext;
Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> styleToResourceTypeToTemplate = new HashMap<>();
Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> styleToDatatypeToTemplate = new HashMap<>();
Map<TemplateTypeEnum, Map<String, NarrativeTemplate>> styleToNameToTemplate = new HashMap<>();
for (NarrativeTemplate nextTemplate : theTemplates) {
Map<String, NarrativeTemplate> resourceTypeToTemplate = styleToResourceTypeToTemplate.computeIfAbsent(nextTemplate.getTemplateType(), t -> new HashMap<>());
Map<String, NarrativeTemplate> datatypeToTemplate = styleToDatatypeToTemplate.computeIfAbsent(nextTemplate.getTemplateType(), t -> new HashMap<>());
Map<String, NarrativeTemplate> nameToTemplate = styleToNameToTemplate.computeIfAbsent(nextTemplate.getTemplateType(), t -> new HashMap<>());
nameToTemplate.put(nextTemplate.getTemplateName(), nextTemplate);
for (String nextResourceType : nextTemplate.getAppliesToResourceTypes()) {
resourceTypeToTemplate.put(nextResourceType.toUpperCase(), nextTemplate);
}
for (String nextDataType : nextTemplate.getAppliesToDataTypes()) {
datatypeToTemplate.put(nextDataType.toUpperCase(), nextTemplate);
}
}
myTemplateCount = theTemplates.size();
myStyleToNameToTemplate = makeImmutable(styleToNameToTemplate);
myStyleToResourceTypeToTemplate = makeImmutable(styleToResourceTypeToTemplate);
myStyleToDatatypeToTemplate = makeImmutable(styleToDatatypeToTemplate);
}
public int getNamedTemplateCount() {
return myTemplateCount;
}
@Override
public Optional<INarrativeTemplate> getTemplateByResourceName(TemplateTypeEnum theStyle, String theResourceName) {
return getFromMap(theStyle, theResourceName.toUpperCase(), myStyleToResourceTypeToTemplate);
}
@Override
public Optional<INarrativeTemplate> getTemplateByName(TemplateTypeEnum theStyle, String theName) {
return getFromMap(theStyle, theName, myStyleToNameToTemplate);
}
@Override
public Optional<INarrativeTemplate> getTemplateByElement(TemplateTypeEnum theStyle, IBase theElement) {
if (theElement instanceof IBaseResource) {
String resourceName = myCtx.getResourceDefinition((IBaseResource) theElement).getName();
return getTemplateByResourceName(theStyle, resourceName);
} else {
String datatypeName = myCtx.getElementDefinition(theElement.getClass()).getName();
return getFromMap(theStyle, datatypeName.toUpperCase(), myStyleToDatatypeToTemplate);
}
}
public static NarrativeTemplateManifest forManifestFileLocation(FhirContext theFhirContext, String... thePropertyFilePaths) throws IOException {
return forManifestFileLocation(theFhirContext, Arrays.asList(thePropertyFilePaths));
}
public static NarrativeTemplateManifest forManifestFileLocation(FhirContext theFhirContext, Collection<String> thePropertyFilePaths) throws IOException {
ourLog.debug("Loading narrative properties file(s): {}", thePropertyFilePaths);
List<String> manifestFileContents = new ArrayList<>(thePropertyFilePaths.size());
for (String next : thePropertyFilePaths) {
String resource = loadResource(next);
manifestFileContents.add(resource);
}
return forManifestFileContents(theFhirContext, manifestFileContents);
}
public static NarrativeTemplateManifest forManifestFileContents(FhirContext theFhirContext, String... theResources) throws IOException {
return forManifestFileContents(theFhirContext, Arrays.asList(theResources));
}
public static NarrativeTemplateManifest forManifestFileContents(FhirContext theFhirContext, Collection<String> theResources) throws IOException {
List<NarrativeTemplate> templates = new ArrayList<>();
for (String next : theResources) {
templates.addAll(loadProperties(next));
}
return new NarrativeTemplateManifest(theFhirContext, templates);
}
private static Collection<NarrativeTemplate> loadProperties(String theManifestText) throws IOException {
Map<String, NarrativeTemplate> nameToTemplate = new HashMap<>();
Properties file = new Properties();
file.load(new StringReader(theManifestText));
for (Object nextKeyObj : file.keySet()) {
String nextKey = (String) nextKeyObj;
Validate.isTrue(StringUtils.countMatches(nextKey, ".") == 1, "Invalid narrative property file key: %s", nextKey);
String name = nextKey.substring(0, nextKey.indexOf('.'));
Validate.notBlank(name, "Invalid narrative property file key: %s", nextKey);
NarrativeTemplate nextTemplate = nameToTemplate.computeIfAbsent(name, t -> new NarrativeTemplate().setTemplateName(name));
Validate.isTrue(!nextKey.endsWith(".class"), "Narrative manifest does not support specifying templates by class name - Use \"[name].resourceType=[resourceType]\" instead");
if (nextKey.endsWith(".profile")) {
String profile = file.getProperty(nextKey);
if (isNotBlank(profile)) {
nextTemplate.addAppliesToProfile(profile);
}
} else if (nextKey.endsWith(".resourceType")) {
String resourceType = file.getProperty(nextKey);
Arrays
.stream(resourceType.split(","))
.map(t -> t.trim())
.filter(t -> isNotBlank(t))
.forEach(t -> nextTemplate.addAppliesToResourceType(t));
} else if (nextKey.endsWith(".dataType")) {
String dataType = file.getProperty(nextKey);
Arrays
.stream(dataType.split(","))
.map(t -> t.trim())
.filter(t -> isNotBlank(t))
.forEach(t -> nextTemplate.addAppliesToDatatype(t));
} else if (nextKey.endsWith(".class")) {
String className = file.getProperty(nextKey);
Class<? extends IBase> clazz;
try {
clazz = (Class<? extends IBase>) Class.forName(className);
} catch (ClassNotFoundException e) {
ourLog.debug("Unknown datatype class '{}' identified in manifest", name);
clazz = null;
}
if (clazz != null) {
nextTemplate.addAppliesToResourceClass(clazz);
}
} else if (nextKey.endsWith(".style")) {
String templateTypeName = file.getProperty(nextKey).toUpperCase();
TemplateTypeEnum templateType = TemplateTypeEnum.valueOf(templateTypeName);
nextTemplate.setTemplateType(templateType);
} else if (nextKey.endsWith(".contextPath")) {
String contextPath = file.getProperty(nextKey);
nextTemplate.setContextPath(contextPath);
} else if (nextKey.endsWith(".narrative")) {
String narrativePropName = name + ".narrative";
String narrativeName = file.getProperty(narrativePropName);
if (StringUtils.isNotBlank(narrativeName)) {
nextTemplate.setTemplateFileName(narrativeName);
}
} else if (nextKey.endsWith(".title")) {
ourLog.debug("Ignoring title property as narrative generator no longer generates titles: {}", nextKey);
} else {
throw new ConfigurationException("Invalid property name: " + nextKey);
}
}
return nameToTemplate.values();
}
static String loadResource(String name) throws IOException {
if (name.startsWith("classpath:")) {
String cpName = name.substring("classpath:".length());
try (InputStream resource = DefaultThymeleafNarrativeGenerator.class.getResourceAsStream(cpName)) {
if (resource == null) {
try (InputStream resource2 = DefaultThymeleafNarrativeGenerator.class.getResourceAsStream("/" + cpName)) {
if (resource2 == null) {
throw new IOException("Can not find '" + cpName + "' on classpath");
}
return IOUtils.toString(resource2, Charsets.UTF_8);
}
}
return IOUtils.toString(resource, Charsets.UTF_8);
}
} else if (name.startsWith("file:")) {
File file = new File(name.substring("file:".length()));
if (file.exists() == false) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
try (FileInputStream inputStream = new FileInputStream(file)) {
return IOUtils.toString(inputStream, Charsets.UTF_8);
}
} else {
throw new IOException("Invalid resource name: '" + name + "' (must start with classpath: or file: )");
}
}
private static <T> Optional<INarrativeTemplate> getFromMap(TemplateTypeEnum theStyle, T theResourceName, Map<TemplateTypeEnum, Map<T, NarrativeTemplate>> theMap) {
NarrativeTemplate retVal = null;
Map<T, NarrativeTemplate> resourceTypeToTemplate = theMap.get(theStyle);
if (resourceTypeToTemplate != null) {
retVal = resourceTypeToTemplate.get(theResourceName);
}
return Optional.ofNullable(retVal);
}
private static <T> Map<TemplateTypeEnum, Map<T, NarrativeTemplate>> makeImmutable(Map<TemplateTypeEnum, Map<T, NarrativeTemplate>> theStyleToResourceTypeToTemplate) {
theStyleToResourceTypeToTemplate.replaceAll((key, value) -> Collections.unmodifiableMap(value));
return Collections.unmodifiableMap(theStyleToResourceTypeToTemplate);
}
}

View File

@ -0,0 +1,31 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.narrative.INarrativeGenerator;
import org.hl7.fhir.instance.model.api.IBaseResource;
public class NullNarrativeGenerator implements INarrativeGenerator {
@Override
public boolean populateResourceNarrative(IBaseResource theResource) {
return false;
}
}

View File

@ -0,0 +1,28 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public enum TemplateTypeEnum {
THYMELEAF,
LIQUID
}

View File

@ -0,0 +1,195 @@
package ca.uhn.fhir.narrative2;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBase;
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.messageresolver.IMessageResolver;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.AbstractElementTagProcessor;
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 java.util.Map;
import java.util.Optional;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class ThymeleafNarrativeGenerator extends BaseNarrativeGenerator {
private IMessageResolver myMessageResolver;
/**
* Constructor
*/
public ThymeleafNarrativeGenerator(FhirContext theFhirContext) {
super(theFhirContext);
}
private TemplateEngine getTemplateEngine() {
TemplateEngine engine = new TemplateEngine();
ProfileResourceResolver resolver = new ProfileResourceResolver();
engine.setTemplateResolver(resolver);
if (myMessageResolver != null) {
engine.setMessageResolver(myMessageResolver);
}
StandardDialect dialect = new StandardDialect() {
@Override
public Set<IProcessor> getProcessors(String theDialectPrefix) {
Set<IProcessor> retVal = super.getProcessors(theDialectPrefix);
retVal.add(new NarrativeTagProcessor(theDialectPrefix));
retVal.add(new NarrativeAttributeProcessor(theDialectPrefix));
return retVal;
}
};
engine.setDialect(dialect);
return engine;
}
@Override
protected String applyTemplate(INarrativeTemplate theTemplate, IBase theTargetContext) {
Context context = new Context();
context.setVariable("resource", theTargetContext);
context.setVariable("context", theTargetContext);
context.setVariable("fhirVersion", getFhirContext().getVersion().getVersion().name());
String result = getTemplateEngine().process(theTemplate.getTemplateName(), context);
return result;
}
@Override
protected TemplateTypeEnum getStyle() {
return TemplateTypeEnum.THYMELEAF;
}
private String applyTemplateWithinTag(ITemplateContext theTemplateContext, String theName, String theElement) {
IEngineConfiguration configuration = theTemplateContext.getConfiguration();
IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = expressionParser.parseExpression(theTemplateContext, theElement);
Object elementValueObj = expression.execute(theTemplateContext);
final IBase elementValue = (IBase) elementValueObj;
if (elementValue == null) {
return "";
}
Optional<INarrativeTemplate> templateOpt;
if (isNotBlank(theName)) {
templateOpt = getManifest().getTemplateByName(getStyle(), theName);
if (!templateOpt.isPresent()) {
throw new InternalErrorException("Unknown template name: " + theName);
}
} else {
templateOpt = getManifest().getTemplateByElement(getStyle(), elementValue);
if (!templateOpt.isPresent()) {
throw new InternalErrorException("No template for type: " + elementValue.getClass());
}
}
return applyTemplate(templateOpt.get(), elementValue);
}
public void setMessageResolver(IMessageResolver theMessageResolver) {
myMessageResolver = theMessageResolver;
}
private class ProfileResourceResolver extends DefaultTemplateResolver {
@Override
protected boolean computeResolvable(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return getManifest().getTemplateByName(getStyle(), theTemplate).isPresent();
}
@Override
protected TemplateMode computeTemplateMode(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return TemplateMode.XML;
}
@Override
protected ITemplateResource computeTemplateResource(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return getManifest()
.getTemplateByName(getStyle(), theTemplate)
.map(t -> new StringTemplateResource(t.getTemplateText()))
.orElseThrow(() -> new IllegalArgumentException("Unknown template: " + theTemplate));
}
@Override
protected ICacheEntryValidity computeValidity(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map<String, Object> theTemplateResolutionAttributes) {
return AlwaysValidCacheEntryValidity.INSTANCE;
}
}
private class NarrativeTagProcessor extends AbstractElementTagProcessor {
public NarrativeTagProcessor(String dialectPrefix) {
super(TemplateMode.XML, dialectPrefix, "narrative", true, null, true, 0);
}
@Override
protected void doProcess(ITemplateContext theTemplateContext, IProcessableElementTag theTag, IElementTagStructureHandler theStructureHandler) {
String name = theTag.getAttributeValue("th:name");
String element = theTag.getAttributeValue("th:element");
String appliedTemplate = applyTemplateWithinTag(theTemplateContext, name, element);
theStructureHandler.replaceWith(appliedTemplate, false);
}
}
/**
* This is a thymeleaf extension that allows people to do things like
* <th:block th:narrative="${result}"/>
*/
private class NarrativeAttributeProcessor extends AbstractAttributeTagProcessor {
protected NarrativeAttributeProcessor(String theDialectPrefix) {
super(TemplateMode.XML, theDialectPrefix, null, false, "narrative", true, 0, true);
}
@Override
protected void doProcess(ITemplateContext theContext, IProcessableElementTag theTag, AttributeName theAttributeName, String theAttributeValue, IElementTagStructureHandler theStructureHandler) {
String text = applyTemplateWithinTag(theContext, null, theAttributeValue);
theStructureHandler.setBody(text, false);
}
}
}

View File

@ -360,7 +360,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
narr = null; narr = null;
} }
if (narr != null && narr.isEmpty()) { if (narr != null && narr.isEmpty()) {
gen.generateNarrative(myContext, theResource, narr); gen.populateResourceNarrative(theResource);
if (!narr.isEmpty()) { if (!narr.isEmpty()) {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype()); String childName = nextChild.getChildNameByDatatype(child.getDatatype());

View File

@ -371,7 +371,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
} }
// FIXME potential null access on narr see line 623 // FIXME potential null access on narr see line 623
if (gen != null && narr.isEmpty()) { if (gen != null && narr.isEmpty()) {
gen.generateNarrative(myContext, theResource, narr); gen.populateResourceNarrative(theResource);
} }
if (narr != null && narr.isEmpty() == false) { if (narr != null && narr.isEmpty() == false) {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;

View File

@ -25,7 +25,7 @@ public interface IBaseReference extends ICompositeType {
IBaseResource getResource(); IBaseResource getResource();
void setResource(IBaseResource theResource); IBaseReference setResource(IBaseResource theResource);
IIdType getReferenceElement(); IIdType getReferenceElement();

View File

@ -3,6 +3,6 @@
# Resources # Resources
################################################ ################################################
conformance.class=ca.uhn.fhir.model.dstu.resource.Conformance conformance.resourceType =Conformance
conformance.narrative=classpath:ca/uhn/fhir/narrative/ConformanceHapiServer.html conformance.narrative =classpath:ca/uhn/fhir/narrative/ConformanceHapiServer.html

View File

@ -3,66 +3,62 @@
# Primitive Datatypes # Primitive Datatypes
################################################ ################################################
code.class=ca.uhn.fhir.model.primitive.CodeDt code.dataType=code
code.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeDt.html code.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeDt.html
datetime.class=ca.uhn.fhir.model.primitive.DateTimeDt datetime.dataType=dateTime, instant
datetime.narrative=classpath:ca/uhn/fhir/narrative/datatype/DateTimeDt.html datetime.narrative=classpath:ca/uhn/fhir/narrative/datatype/DateTimeDt.html
# Instant uses DateTime narrative string.dataType=string
instant.class=ca.uhn.fhir.model.primitive.InstantDt
instant.narrative=classpath:ca/uhn/fhir/narrative/datatype/DateTimeDt.html
string.class=ca.uhn.fhir.model.primitive.StringDt
string.narrative=classpath:ca/uhn/fhir/narrative/datatype/StringDt.html string.narrative=classpath:ca/uhn/fhir/narrative/datatype/StringDt.html
################################################ ################################################
# Composite Datatypes # Composite Datatypes
################################################ ################################################
address.class=ca.uhn.fhir.model.dstu.composite.AddressDt address.dataType=Address
address.narrative=classpath:ca/uhn/fhir/narrative/datatype/AddressDt.html address.narrative=classpath:ca/uhn/fhir/narrative/datatype/AddressDt.html
codeableconcept.class=ca.uhn.fhir.model.dstu.composite.CodeableConceptDt codeableconcept.dataType=CodeableConcept
codeableconcept.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeableConceptDt.html codeableconcept.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeableConceptDt.html
boundcodeableconcept.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeableConceptDt.html boundcodeableconcept.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeableConceptDt.html
humanname.class=ca.uhn.fhir.model.dstu.composite.HumanNameDt humanname.dataType=HumanName
humanname.narrative=classpath:ca/uhn/fhir/narrative/datatype/HumanNameDt.html humanname.narrative=classpath:ca/uhn/fhir/narrative/datatype/HumanNameDt.html
identifier.class=ca.uhn.fhir.model.dstu.composite.IdentifierDt identifier.dataType=Identifier
identifier.narrative=classpath:ca/uhn/fhir/narrative/datatype/IdentifierDt.html identifier.narrative=classpath:ca/uhn/fhir/narrative/datatype/IdentifierDt.html
period.class=ca.uhn.fhir.model.dstu.composite.PeriodDt period.dataType=Period
period.narrative=classpath:ca/uhn/fhir/narrative/datatype/PeriodDt.html period.narrative=classpath:ca/uhn/fhir/narrative/datatype/PeriodDt.html
quantity.class=ca.uhn.fhir.model.dstu.composite.QuantityDt quantity.dataType=Quantity
quantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/QuantityDt.html quantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/QuantityDt.html
simplequantity.class=ca.uhn.fhir.model.dstu.composite.SimpleQuantityDt simplequantity.dataType=SimpleQuantity
simplequantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/SimpleQuantityDt.html simplequantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/SimpleQuantityDt.html
################################################ ################################################
# Resources # Resources
################################################ ################################################
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport diagnosticreport.resourceType=DiagnosticReport
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
encounter.class=ca.uhn.fhir.model.dstu.resource.Encounter #encounter.class=ca.uhn.fhir.model.dstu.resource.Encounter
operationoutcome.class=ca.uhn.fhir.model.dstu.resource.OperationOutcome operationoutcome.resourceType=OperationOutcome
operationoutcome.narrative=classpath:ca/uhn/fhir/narrative/OperationOutcome.html operationoutcome.narrative=classpath:ca/uhn/fhir/narrative/OperationOutcome.html
organization.class=ca.uhn.fhir.model.dstu.resource.Organization #organization.class=ca.uhn.fhir.model.dstu.resource.Organization
patient.class=ca.uhn.fhir.model.dstu.resource.Patient patient.resourceType=Patient
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
medicationprescription.class=ca.uhn.fhir.model.dstu.resource.MedicationPrescription medicationprescription.resourceType=MedicationPrescription
medicationprescription.narrative=classpath:ca/uhn/fhir/narrative/MedicationPrescription.html medicationprescription.narrative=classpath:ca/uhn/fhir/narrative/MedicationPrescription.html
medication.class=ca.uhn.fhir.model.dstu.resource.Medication medication.resourceType=Medication
medication.narrative=classpath:ca/uhn/fhir/narrative/Medication.html medication.narrative=classpath:ca/uhn/fhir/narrative/Medication.html
medication.title=classpath:ca/uhn/fhir/narrative/Medication.html medication.title=classpath:ca/uhn/fhir/narrative/Medication.html

View File

@ -44,7 +44,7 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.dstu3.model.Bundle.BundleType;

View File

@ -35,7 +35,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.text.WordUtils; import org.apache.commons.lang3.text.WordUtils;
import org.fusesource.jansi.Ansi.Color; import org.fusesource.jansi.Ansi.Color;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition;

View File

@ -125,7 +125,7 @@ public class JpaServerDemo extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to XML and pretty printing * Default to XML and pretty printing

View File

@ -91,6 +91,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
private String myResourceName; private String myResourceName;
private Class<T> myResourceType; private Class<T> myResourceType;
private String mySecondaryPrimaryKeyParamName; private String mySecondaryPrimaryKeyParamName;
@Autowired
private IResourceReindexingSvc myResourceReindexingSvc;
@Override @Override
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@ -572,10 +574,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
BaseHasResource entity = txTemplate.execute(t->readEntity(theId)); BaseHasResource entity = txTemplate.execute(t -> readEntity(theId));
if (theId.hasVersionIdPart()) { if (theId.hasVersionIdPart()) {
BaseHasResource currentVersion; BaseHasResource currentVersion;
currentVersion = txTemplate.execute(t->readEntity(theId.toVersionless())); currentVersion = txTemplate.execute(t -> readEntity(theId.toVersionless()));
if (entity.getVersion() == currentVersion.getVersion()) { if (entity.getVersion() == currentVersion.getVersion()) {
throw new PreconditionFailedException("Can not perform version-specific expunge of resource " + theId.toUnqualified().getValue() + " as this is the current version"); throw new PreconditionFailedException("Can not perform version-specific expunge of resource " + theId.toUnqualified().getValue() + " as this is the current version");
} }
@ -682,7 +684,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txTemplate.execute(t->{ txTemplate.execute(t -> {
myResourceReindexingSvc.markAllResourcesForReindexing(resourceType); myResourceReindexingSvc.markAllResourcesForReindexing(resourceType);
return null; return null;
}); });
@ -694,9 +696,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
mySearchParamRegistry.requestRefresh(); mySearchParamRegistry.requestRefresh();
} }
@Autowired
private IResourceReindexingSvc myResourceReindexingSvc;
@Override @Override
public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) { public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) {
// Notify interceptors // Notify interceptors
@ -722,7 +721,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
doMetaAdd(theMetaAdd, history); doMetaAdd(theMetaAdd, history);
} }
ourLog.debug("Processed metaAddOperation on {} in {}ms", new Object[] {theResourceId, w.getMillisAndRestart()}); ourLog.debug("Processed metaAddOperation on {} in {}ms", new Object[]{theResourceId, w.getMillisAndRestart()});
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails); MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
@ -756,7 +755,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myEntityManager.flush(); myEntityManager.flush();
ourLog.debug("Processed metaDeleteOperation on {} in {}ms", new Object[] {theResourceId.getValue(), w.getMillisAndRestart()}); ourLog.debug("Processed metaDeleteOperation on {} in {}ms", new Object[]{theResourceId.getValue(), w.getMillisAndRestart()});
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails); MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
@ -918,7 +917,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return read(theId, theRequestDetails, false); return read(theId, theRequestDetails, false);
} }
@Override @Override
public T read(IIdType theId, RequestDetails theRequestDetails, boolean theDeletedOk) { public T read(IIdType theId, RequestDetails theRequestDetails, boolean theDeletedOk) {
validateResourceTypeAndThrowIllegalArgumentException(theId); validateResourceTypeAndThrowIllegalArgumentException(theId);
@ -941,6 +940,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
} }
// Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
HookParams params = new HookParams().add(IBaseResource.class, retVal);
myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
ourLog.debug("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart()); ourLog.debug("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal; return retVal;
@ -1189,6 +1191,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
outcome.setId(id); outcome.setId(id);
outcome.setResource(theResource); outcome.setResource(theResource);
outcome.setEntity(theEntity); outcome.setEntity(theEntity);
// Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
HookParams params = new HookParams().add(IBaseResource.class, theResource);
myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
return outcome; return outcome;
} }

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -29,6 +29,9 @@ import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService; import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
import ca.uhn.fhir.jpa.entity.ResourceSearchView; import ca.uhn.fhir.jpa.entity.ResourceSearchView;
import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.util.StringNormalizer; import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
@ -137,6 +140,8 @@ public class SearchBuilder implements ISearchBuilder {
private MatchUrlService myMatchUrlService; private MatchUrlService myMatchUrlService;
@Autowired @Autowired
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao; private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
private List<Long> myAlsoIncludePids; private List<Long> myAlsoIncludePids;
private CriteriaBuilder myBuilder; private CriteriaBuilder myBuilder;
private BaseHapiFhirDao<?> myCallingDao; private BaseHapiFhirDao<?> myCallingDao;
@ -1922,6 +1927,10 @@ public class SearchBuilder implements ISearchBuilder {
} }
} }
// Interceptor broadcast: RESOURCE_MAY_BE_RETURNED
HookParams params = new HookParams().add(IBaseResource.class, resource);
myInterceptorBroadcaster.callHooks(Pointcut.RESOURCE_MAY_BE_RETURNED, params);
theResourceListToPopulate.set(index, resource); theResourceListToPopulate.set(index, resource);
} }
} }

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.validation;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;

View File

@ -2350,6 +2350,31 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
} }
@Test
public void testSearchWithDateRange() {
SearchParameterMap sp = new SearchParameterMap();
sp.setLoadSynchronous(true);
sp.add(MedicationRequest.SP_INTENT, new TokenParam("FOO", "BAR"));
sp.setLastUpdated(new DateRangeParam()
.setUpperBound(new DateParam("le2019-02-22T17:50:00"))
.setLowerBound(new DateParam("ge2019-02-22T13:50:00")));
IBundleProvider retrieved = myMedicationRequestDao.search(sp);
List<String> queries = CaptureQueriesListener
.getLastNQueries()
.stream()
.map(t -> t.getSql(true, true))
.collect(Collectors.toList());
ourLog.info("Queries:\n {}", queries.stream().findFirst());
String searchQuery = queries.get(0);
assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"));
assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"));
assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toUpperCase(), "AND RESOURCETA0_.RES_UPDATED"));
}
@Test @Test
public void testSearchTokenParam() { public void testSearchTokenParam() {
Patient patient = new Patient(); Patient patient = new Patient();

View File

@ -74,7 +74,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
ourRestServer.setResourceProviders((List)myResourceProviders); ourRestServer.setResourceProviders((List)myResourceProviders);
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx));
ourRestServer.setPlainProviders(mySystemProvider); ourRestServer.setPlainProviders(mySystemProvider);

View File

@ -92,7 +92,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
ourRestServer.setResourceProviders((List) myResourceProviders); ourRestServer.setResourceProviders((List) myResourceProviders);
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx));
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class); myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class);
ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider);

View File

@ -99,7 +99,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
ourRestServer.setResourceProviders((List) myResourceProviders); ourRestServer.setResourceProviders((List) myResourceProviders);
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx));
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class); myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class);
ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider"); ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");

View File

@ -1,33 +1,39 @@
package ca.uhn.fhir.jpa.provider.r4; package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.model.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.model.interceptor.api.IAnonymousLambdaHook;
import ca.uhn.fhir.jpa.model.interceptor.api.Pointcut;
import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.codesystems.EncounterStatus;
import org.hl7.fhir.r4.model.codesystems.ObservationStatus;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mockito;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
@ -38,6 +44,8 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
private String encId; private String encId;
private String listId; private String listId;
private String compId; private String compId;
@Captor
private ArgumentCaptor<HookParams> myHookParamsCaptor;
@Before @Before
public void beforeDisableResultReuse() { public void beforeDisableResultReuse() {
@ -49,6 +57,7 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
public void after() throws Exception { public void after() throws Exception {
super.after(); super.after();
myInterceptorRegistry.clearAnonymousHookForUnitTest();
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
} }
@ -77,7 +86,7 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
ListResource listResource = new ListResource(); ListResource listResource = new ListResource();
ArrayList<Observation> myObs = new ArrayList<>(); ArrayList<Observation> myObs = new ArrayList<>();
myObsIds = new ArrayList<String>(); myObsIds = new ArrayList<>();
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getSubject().setReference(patId); obs.getSubject().setReference(patId);
@ -125,14 +134,38 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test {
assertThat(actual, hasItems(myObsIds.toArray(new String[0]))); assertThat(actual, hasItems(myObsIds.toArray(new String[0])));
} }
private Bundle fetchBundle(String theUrl, EncodingEnum theEncoding) throws IOException, ClientProtocolException { @Test
public void testInterceptorHookIsCalledForAllContents_RESOURCE_MAY_BE_RETURNED() throws IOException {
IAnonymousLambdaHook pointcut = mock(IAnonymousLambdaHook.class);
myInterceptorRegistry.registerAnonymousHookForUnitTest(Pointcut.RESOURCE_MAY_BE_RETURNED, pointcut);
String theUrl = ourServerBase + "/" + compId + "/$document?_format=json";
fetchBundle(theUrl, EncodingEnum.JSON);
Mockito.verify(pointcut, times(10)).invoke(myHookParamsCaptor.capture());
List<String> returnedClasses = myHookParamsCaptor
.getAllValues()
.stream()
.map(t -> t.get(IBaseResource.class, 0))
.map(t -> t.getClass().getSimpleName())
.collect(Collectors.toList());
ourLog.info("Returned classes: {}", returnedClasses);
assertThat(returnedClasses, hasItem("Composition"));
assertThat(returnedClasses, hasItem("Organization"));
}
private Bundle fetchBundle(String theUrl, EncodingEnum theEncoding) throws IOException {
Bundle bundle; Bundle bundle;
HttpGet get = new HttpGet(theUrl); HttpGet get = new HttpGet(theUrl);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) { try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String resourceString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); String resourceString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
bundle = theEncoding.newParser(myFhirCtx).parseResource(Bundle.class, resourceString); bundle = theEncoding.newParser(myFhirCtx).parseResource(Bundle.class, resourceString);
} }
return bundle; return bundle;
} }

View File

@ -109,7 +109,7 @@ public class JpaServerDemo extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing

View File

@ -108,7 +108,7 @@ public class JpaServerDemoDstu2 extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing

View File

@ -29,6 +29,11 @@ import java.util.List;
*/ */
public enum Pointcut { public enum Pointcut {
/**
* This pointcut will be called once when a given interceptor is registered
*/
REGISTERED,
/** /**
* Invoked whenever a persisted resource has been modified and is being submitted to the * Invoked whenever a persisted resource has been modified and is being submitted to the
* subscription processing pipeline. This method is called before the resource is placed * subscription processing pipeline. This method is called before the resource is placed
@ -330,7 +335,32 @@ public enum Pointcut {
* Hooks should return <code>void</code>. * Hooks should return <code>void</code>.
* </p> * </p>
*/ */
OP_PRESTORAGE_RESOURCE_UPDATED("org.hl7.fhir.instance.model.api.IBaseResource", "org.hl7.fhir.instance.model.api.IBaseResource"); OP_PRESTORAGE_RESOURCE_UPDATED("org.hl7.fhir.instance.model.api.IBaseResource", "org.hl7.fhir.instance.model.api.IBaseResource"),
/**
* Invoked when a resource may be returned to the user, whether as a part of a READ,
* a SEARCH, or even as the response to a CREATE/UPDATE, etc.
* <p>
* This hook is invoked when a resource has been loaded by the storage engine and
* is being returned to the HTTP stack for response. This is not a guarantee that the
* client will ultimately see it, since filters/headers/etc may affect what
* is returned but if a resource is loaded it is likely to be used.
* Note also that caching may affect whether this pointcut is invoked.
* </p>
* <p>
* Hooks will have access to the contents of the resource being returned
* and may choose to make modifications. These changes will be reflected in
* returned resource but have no effect on storage.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>org.hl7.fhir.instance.model.api.IBaseResource (the resource being returned)</li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
RESOURCE_MAY_BE_RETURNED("org.hl7.fhir.instance.model.api.IBaseResource");
private final List<String> myParameterTypes; private final List<String> myParameterTypes;

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -92,11 +93,26 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
Class<?> interceptorClass = theInterceptor.getClass(); Class<?> interceptorClass = theInterceptor.getClass();
int typeOrder = determineOrder(interceptorClass); int typeOrder = determineOrder(interceptorClass);
if (!scanInterceptorForHookMethodsAndAddThem(theInterceptor, typeOrder)) { List<HookInvoker> addedInvokers = scanInterceptorForHookMethods(theInterceptor, typeOrder);
if (addedInvokers.isEmpty()) {
return false; return false;
} }
// Invoke the REGISTERED pointcut for any added hooks
addedInvokers.stream()
.filter(t -> t.getPointcuts().contains(Pointcut.REGISTERED))
.forEach(t -> t.invoke(new HookParams()));
// Register the interceptor and its various hooks
myInterceptors.add(theInterceptor); myInterceptors.add(theInterceptor);
for (HookInvoker nextAddedHook : addedInvokers) {
for (Pointcut nextPointcut : nextAddedHook.getPointcuts()) {
if (nextPointcut.equals(Pointcut.REGISTERED)) {
continue;
}
myInvokers.put(nextPointcut, nextAddedHook);
}
}
// Make sure we're always sorted according to the order declared in // Make sure we're always sorted according to the order declared in
// @Order // @Order
@ -110,27 +126,25 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
} }
} }
private boolean scanInterceptorForHookMethodsAndAddThem(Object theInterceptor, int theTypeOrder) { /**
boolean retVal = false; * @return Returns a list of any added invokers
*/
private List<HookInvoker> scanInterceptorForHookMethods(Object theInterceptor, int theTypeOrder) {
ArrayList<HookInvoker> retVal = new ArrayList<>();
for (Method nextMethod : theInterceptor.getClass().getDeclaredMethods()) { for (Method nextMethod : theInterceptor.getClass().getDeclaredMethods()) {
Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class); Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class);
if (hook != null) { if (hook != null) {
int methodOrder = theTypeOrder; int methodOrder = theTypeOrder;
Order methodOrderAnnotation = AnnotationUtils.findAnnotation(nextMethod, Order.class); Order methodOrderAnnotation = AnnotationUtils.findAnnotation(nextMethod, Order.class);
if (methodOrderAnnotation != null) { if (methodOrderAnnotation != null) {
methodOrder = methodOrderAnnotation.value(); methodOrder = methodOrderAnnotation.value();
} }
HookInvoker invoker = new HookInvoker(hook, theInterceptor, nextMethod, methodOrder); retVal.add(new HookInvoker(hook, theInterceptor, nextMethod, methodOrder));
for (Pointcut nextPointcut : hook.value()) {
myInvokers.put(nextPointcut, invoker);
}
retVal = true;
} }
} }
return retVal; return retVal;
} }
@ -300,12 +314,14 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
private final Method myMethod; private final Method myMethod;
private final Class<?>[] myParameterTypes; private final Class<?>[] myParameterTypes;
private final int[] myParameterIndexes; private final int[] myParameterIndexes;
private final Set<Pointcut> myPointcuts;
/** /**
* Constructor * Constructor
*/ */
private HookInvoker(Hook theHook, @Nonnull Object theInterceptor, @Nonnull Method theHookMethod, int theOrder) { private HookInvoker(Hook theHook, @Nonnull Object theInterceptor, @Nonnull Method theHookMethod, int theOrder) {
super(theInterceptor, theOrder); super(theInterceptor, theOrder);
myPointcuts = Collections.unmodifiableSet(Sets.newHashSet(theHook.value()));
myParameterTypes = theHookMethod.getParameterTypes(); myParameterTypes = theHookMethod.getParameterTypes();
myMethod = theHookMethod; myMethod = theHookMethod;
@ -325,6 +341,10 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
} }
} }
public Set<Pointcut> getPointcuts() {
return myPointcuts;
}
/** /**
* @return Returns true/false if the hook method returns a boolean, returns true otherwise * @return Returns true/false if the hook method returns a boolean, returns true otherwise
*/ */
@ -352,7 +372,7 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro
if (targetException instanceof RuntimeException) { if (targetException instanceof RuntimeException) {
throw ((RuntimeException) targetException); throw ((RuntimeException) targetException);
} else { } else {
throw new InternalErrorException(targetException); throw new InternalErrorException("Failure invoking interceptor for pointcut(s) " + getPointcuts(), targetException);
} }
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.model.interceptor.executor; package ca.uhn.fhir.jpa.model.interceptor.executor;
import ca.uhn.fhir.jpa.model.interceptor.api.*; import ca.uhn.fhir.jpa.model.interceptor.api.*;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.junit.Before; import org.junit.Before;
@ -40,6 +41,21 @@ public class InterceptorServiceTest {
@Autowired @Autowired
private MyTestInterceptorManual myInterceptorManual; private MyTestInterceptorManual myInterceptorManual;
@Test
public void testRegisterHookFails() {
int initialSize = myInterceptorRegistry.getGlobalInterceptorsForUnitTest().size();
try {
myInterceptorRegistry.registerInterceptor(new InterceptorThatFailsOnRegister());
fail();
} catch (InternalErrorException e) {
// good
}
assertEquals(initialSize, myInterceptorRegistry.getGlobalInterceptorsForUnitTest().size());
}
@Test @Test
public void testGlobalInterceptorsAreFound() { public void testGlobalInterceptorsAreFound() {
List<Object> globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest(); List<Object> globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest();
@ -265,4 +281,15 @@ public class InterceptorServiceTest {
*/ */
private static class ResourceDeliveryMessage { private static class ResourceDeliveryMessage {
} }
@Interceptor(manualRegistration = true)
public static class InterceptorThatFailsOnRegister {
@Hook(Pointcut.REGISTERED)
public void start() throws Exception {
throw new Exception("InterceptorThatFailsOnRegister FAILED!");
}
}
} }

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.junit.Test; import org.junit.Test;

View File

@ -11,7 +11,7 @@ import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;

View File

@ -26,7 +26,7 @@ import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;

View File

@ -29,9 +29,12 @@ import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
public class InMemorySubscriptionMatcher implements ISubscriptionMatcher { public class InMemorySubscriptionMatcher implements ISubscriptionMatcher {
private static final Logger ourLog = LoggerFactory.getLogger(InMemorySubscriptionMatcher.class);
@Autowired @Autowired
private FhirContext myContext; private FhirContext myContext;
@ -49,7 +52,8 @@ public class InMemorySubscriptionMatcher implements ISubscriptionMatcher {
try { try {
return match(theSubscription.getCriteriaString(), theMsg.getNewPayload(myContext)); return match(theSubscription.getCriteriaString(), theMsg.getNewPayload(myContext));
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException("Failure processing resource ID[" + theMsg.getId(myContext) + "] for subscription ID[" + theSubscription.getIdElementString() + "]: " + e.getMessage(), e); ourLog.error("Failure in in-memory matcher", e);
throw new InternalErrorException("Failure performing memory-match for resource ID[" + theMsg.getId(myContext) + "] for subscription ID[" + theSubscription.getIdElementString() + "]: " + e.getMessage(), e);
} }
} }

View File

@ -159,7 +159,7 @@ public class TestRestfulServer extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext()));
/* /*
* The resource and system providers (which actually implement the various FHIR * The resource and system providers (which actually implement the various FHIR

View File

@ -37,7 +37,8 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
public IBaseResource getResource() { @Override
public IBaseResource getResource() {
return resource; return resource;
} }
@ -53,9 +54,11 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
public void setResource(IBaseResource theResource) { @Override
public BaseReference setResource(IBaseResource theResource) {
resource = theResource; resource = theResource;
} return null;
}
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {

View File

@ -13,7 +13,8 @@ public abstract class BaseResource extends Base implements IAnyResource, IElemen
/** /**
* @param value The logical id of the resource, as used in the url for the resoure. Once assigned, this value never changes. * @param value The logical id of the resource, as used in the url for the resoure. Once assigned, this value never changes.
*/ */
public BaseResource setId(IIdType value) { @Override
public BaseResource setId(IIdType value) {
if (value == null) { if (value == null) {
setIdElement((IdType)null); setIdElement((IdType)null);
} else if (value instanceof IdType) { } else if (value instanceof IdType) {

View File

@ -1,321 +1,322 @@
package org.hl7.fhir.dstu2016may.model; package org.hl7.fhir.dstu2016may.model;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to * Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific endorse or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 // Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0
import java.util.List; import java.util.List;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
/** /**
* A reference from one resource to another. * A reference from one resource to another.
*/ */
@DatatypeDef(name="Reference") @DatatypeDef(name="Reference")
public class Reference extends BaseReference implements IBaseReference, ICompositeType { public class Reference extends BaseReference implements IBaseReference, ICompositeType {
/** /**
* A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. * A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
*/ */
@Child(name = "reference", type = {StringType.class}, order=0, min=0, max=1, modifier=false, summary=true) @Child(name = "reference", type = {StringType.class}, order=0, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Relative, internal or absolute URL reference", formalDefinition="A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources." ) @Description(shortDefinition="Relative, internal or absolute URL reference", formalDefinition="A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources." )
protected StringType reference; protected StringType reference;
/** /**
* Plain text narrative that identifies the resource in addition to the resource reference. * Plain text narrative that identifies the resource in addition to the resource reference.
*/ */
@Child(name = "display", type = {StringType.class}, order=1, min=0, max=1, modifier=false, summary=true) @Child(name = "display", type = {StringType.class}, order=1, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." )
protected StringType display; protected StringType display;
private static final long serialVersionUID = 22777321L; private static final long serialVersionUID = 22777321L;
/** /**
* Constructor * Constructor
*/ */
public Reference() { public Reference() {
super(); super();
} }
/** /**
* Constructor * Constructor
* *
* @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123")
*/ */
public Reference(String theReference) { public Reference(String theReference) {
super(theReference); super(theReference);
} }
/** /**
* Constructor * Constructor
* *
* @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123")
*/ */
public Reference(IIdType theReference) { public Reference(IIdType theReference) {
super(theReference); super(theReference);
} }
/** /**
* Constructor * Constructor
* *
* @param theResource The resource represented by this reference * @param theResource The resource represented by this reference
*/ */
public Reference(IAnyResource theResource) { public Reference(IAnyResource theResource) {
super(theResource); super(theResource);
} }
/** /**
* @return {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value * @return {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value
*/ */
public StringType getReferenceElement_() { public StringType getReferenceElement_() {
if (this.reference == null) if (this.reference == null)
if (Configuration.errorOnAutoCreate()) if (Configuration.errorOnAutoCreate())
throw new Error("Attempt to auto-create Reference.reference"); throw new Error("Attempt to auto-create Reference.reference");
else if (Configuration.doAutoCreate()) else if (Configuration.doAutoCreate())
this.reference = new StringType(); // bb this.reference = new StringType(); // bb
return this.reference; return this.reference;
} }
public boolean hasReferenceElement() { public boolean hasReferenceElement() {
return this.reference != null && !this.reference.isEmpty(); return this.reference != null && !this.reference.isEmpty();
} }
public boolean hasReference() { public boolean hasReference() {
return this.reference != null && !this.reference.isEmpty(); return this.reference != null && !this.reference.isEmpty();
} }
/** /**
* @param value {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value * @param value {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value
*/ */
public Reference setReferenceElement(StringType value) { public Reference setReferenceElement(StringType value) {
this.reference = value; this.reference = value;
return this; return this;
} }
/** /**
* @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. * @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
*/ */
public String getReference() { @Override
return this.reference == null ? null : this.reference.getValue(); public String getReference() {
} return this.reference == null ? null : this.reference.getValue();
}
/**
* @param value A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. /**
*/ * @param value A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
public Reference setReference(String value) { */
if (Utilities.noString(value)) public Reference setReference(String value) {
this.reference = null; if (Utilities.noString(value))
else { this.reference = null;
if (this.reference == null) else {
this.reference = new StringType(); if (this.reference == null)
this.reference.setValue(value); this.reference = new StringType();
} this.reference.setValue(value);
return this; }
} return this;
}
/**
* @return {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value /**
*/ * @return {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value
public StringType getDisplayElement() { */
if (this.display == null) public StringType getDisplayElement() {
if (Configuration.errorOnAutoCreate()) if (this.display == null)
throw new Error("Attempt to auto-create Reference.display"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Reference.display");
this.display = new StringType(); // bb else if (Configuration.doAutoCreate())
return this.display; this.display = new StringType(); // bb
} return this.display;
}
public boolean hasDisplayElement() {
return this.display != null && !this.display.isEmpty(); public boolean hasDisplayElement() {
} return this.display != null && !this.display.isEmpty();
}
public boolean hasDisplay() {
return this.display != null && !this.display.isEmpty(); public boolean hasDisplay() {
} return this.display != null && !this.display.isEmpty();
}
/**
* @param value {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value /**
*/ * @param value {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value
public Reference setDisplayElement(StringType value) { */
this.display = value; public Reference setDisplayElement(StringType value) {
return this; this.display = value;
} return this;
}
/**
* @return Plain text narrative that identifies the resource in addition to the resource reference. /**
*/ * @return Plain text narrative that identifies the resource in addition to the resource reference.
public String getDisplay() { */
return this.display == null ? null : this.display.getValue(); public String getDisplay() {
} return this.display == null ? null : this.display.getValue();
}
/**
* @param value Plain text narrative that identifies the resource in addition to the resource reference. /**
*/ * @param value Plain text narrative that identifies the resource in addition to the resource reference.
public Reference setDisplay(String value) { */
if (Utilities.noString(value)) public Reference setDisplay(String value) {
this.display = null; if (Utilities.noString(value))
else { this.display = null;
if (this.display == null) else {
this.display = new StringType(); if (this.display == null)
this.display.setValue(value); this.display = new StringType();
} this.display.setValue(value);
return this; }
} return this;
}
/**
* Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given /**
* reference. * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given
* * reference.
* @param theReference The reference, or <code>null</code> *
* @return * @param theReference The reference, or <code>null</code>
* @return Returns a reference to this * @return
*/ * @return Returns a reference to this
public Reference setReferenceElement(IIdType theReference) { */
if (theReference != null) { public Reference setReferenceElement(IIdType theReference) {
setReference(theReference.getValue()); if (theReference != null) {
} else { setReference(theReference.getValue());
setReference(null); } else {
} setReference(null);
return this; }
} return this;
protected void listChildren(List<Property> childrenList) { }
super.listChildren(childrenList); protected void listChildren(List<Property> childrenList) {
childrenList.add(new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, java.lang.Integer.MAX_VALUE, reference)); super.listChildren(childrenList);
childrenList.add(new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, java.lang.Integer.MAX_VALUE, display)); childrenList.add(new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, java.lang.Integer.MAX_VALUE, reference));
} childrenList.add(new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, java.lang.Integer.MAX_VALUE, display));
}
@Override
public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { @Override
switch (hash) { public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType switch (hash) {
case 1671764162: /*display*/ return this.display == null ? new Base[0] : new Base[] {this.display}; // StringType case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType
default: return super.getProperty(hash, name, checkValid); case 1671764162: /*display*/ return this.display == null ? new Base[0] : new Base[] {this.display}; // StringType
} default: return super.getProperty(hash, name, checkValid);
}
}
}
@Override
public void setProperty(int hash, String name, Base value) throws FHIRException { @Override
switch (hash) { public void setProperty(int hash, String name, Base value) throws FHIRException {
case -925155509: // reference switch (hash) {
this.reference = castToString(value); // StringType case -925155509: // reference
break; this.reference = castToString(value); // StringType
case 1671764162: // display break;
this.display = castToString(value); // StringType case 1671764162: // display
break; this.display = castToString(value); // StringType
default: super.setProperty(hash, name, value); break;
} default: super.setProperty(hash, name, value);
}
}
}
@Override
public void setProperty(String name, Base value) throws FHIRException { @Override
if (name.equals("reference")) public void setProperty(String name, Base value) throws FHIRException {
this.reference = castToString(value); // StringType if (name.equals("reference"))
else if (name.equals("display")) this.reference = castToString(value); // StringType
this.display = castToString(value); // StringType else if (name.equals("display"))
else this.display = castToString(value); // StringType
super.setProperty(name, value); else
} super.setProperty(name, value);
}
@Override
public Base makeProperty(int hash, String name) throws FHIRException { @Override
switch (hash) { public Base makeProperty(int hash, String name) throws FHIRException {
case -925155509: throw new FHIRException("Cannot make property reference as it is not a complex type"); // StringType switch (hash) {
case 1671764162: throw new FHIRException("Cannot make property display as it is not a complex type"); // StringType case -925155509: throw new FHIRException("Cannot make property reference as it is not a complex type"); // StringType
default: return super.makeProperty(hash, name); case 1671764162: throw new FHIRException("Cannot make property display as it is not a complex type"); // StringType
} default: return super.makeProperty(hash, name);
}
}
}
@Override
public Base addChild(String name) throws FHIRException { @Override
if (name.equals("reference")) { public Base addChild(String name) throws FHIRException {
throw new FHIRException("Cannot call addChild on a primitive type Reference.reference"); if (name.equals("reference")) {
} throw new FHIRException("Cannot call addChild on a primitive type Reference.reference");
else if (name.equals("display")) { }
throw new FHIRException("Cannot call addChild on a primitive type Reference.display"); else if (name.equals("display")) {
} throw new FHIRException("Cannot call addChild on a primitive type Reference.display");
else }
return super.addChild(name); else
} return super.addChild(name);
}
public String fhirType() {
return "Reference"; public String fhirType() {
return "Reference";
}
}
public Reference copy() {
Reference dst = new Reference(); public Reference copy() {
copyValues(dst); Reference dst = new Reference();
dst.reference = reference == null ? null : reference.copy(); copyValues(dst);
dst.display = display == null ? null : display.copy(); dst.reference = reference == null ? null : reference.copy();
return dst; dst.display = display == null ? null : display.copy();
} return dst;
}
protected Reference typedCopy() {
return copy(); protected Reference typedCopy() {
} return copy();
}
@Override
public boolean equalsDeep(Base other) { @Override
if (!super.equalsDeep(other)) public boolean equalsDeep(Base other) {
return false; if (!super.equalsDeep(other))
if (!(other instanceof Reference)) return false;
return false; if (!(other instanceof Reference))
Reference o = (Reference) other; return false;
return compareDeep(reference, o.reference, true) && compareDeep(display, o.display, true); Reference o = (Reference) other;
} return compareDeep(reference, o.reference, true) && compareDeep(display, o.display, true);
}
@Override
public boolean equalsShallow(Base other) { @Override
if (!super.equalsShallow(other)) public boolean equalsShallow(Base other) {
return false; if (!super.equalsShallow(other))
if (!(other instanceof Reference)) return false;
return false; if (!(other instanceof Reference))
Reference o = (Reference) other; return false;
return compareValues(reference, o.reference, true) && compareValues(display, o.display, true); Reference o = (Reference) other;
} return compareValues(reference, o.reference, true) && compareValues(display, o.display, true);
}
public boolean isEmpty() {
return super.isEmpty() && (reference == null || reference.isEmpty()) && (display == null || display.isEmpty()) public boolean isEmpty() {
; return super.isEmpty() && (reference == null || reference.isEmpty()) && (display == null || display.isEmpty())
} ;
}
}
}

View File

@ -1,382 +1,383 @@
package org.hl7.fhir.dstu2016may.model; package org.hl7.fhir.dstu2016may.model;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to * Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific endorse or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 // Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0
import java.util.List; import java.util.List;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
/** /**
* This is the base resource type for everything. * This is the base resource type for everything.
*/ */
public abstract class Resource extends BaseResource implements IAnyResource { public abstract class Resource extends BaseResource implements IAnyResource {
/** /**
* The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes. * The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.
*/ */
@Child(name = "id", type = {IdType.class}, order=0, min=0, max=1, modifier=false, summary=true) @Child(name = "id", type = {IdType.class}, order=0, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Logical id of this artifact", formalDefinition="The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes." ) @Description(shortDefinition="Logical id of this artifact", formalDefinition="The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes." )
protected IdType id; protected IdType id;
/** /**
* The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource. * The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.
*/ */
@Child(name = "meta", type = {Meta.class}, order=1, min=0, max=1, modifier=false, summary=true) @Child(name = "meta", type = {Meta.class}, order=1, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Metadata about the resource", formalDefinition="The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource." ) @Description(shortDefinition="Metadata about the resource", formalDefinition="The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource." )
protected Meta meta; protected Meta meta;
/** /**
* A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content. * A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.
*/ */
@Child(name = "implicitRules", type = {UriType.class}, order=2, min=0, max=1, modifier=true, summary=true) @Child(name = "implicitRules", type = {UriType.class}, order=2, min=0, max=1, modifier=true, summary=true)
@Description(shortDefinition="A set of rules under which this content was created", formalDefinition="A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content." ) @Description(shortDefinition="A set of rules under which this content was created", formalDefinition="A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content." )
protected UriType implicitRules; protected UriType implicitRules;
/** /**
* The base language in which the resource is written. * The base language in which the resource is written.
*/ */
@Child(name = "language", type = {CodeType.class}, order=3, min=0, max=1, modifier=false, summary=false) @Child(name = "language", type = {CodeType.class}, order=3, min=0, max=1, modifier=false, summary=false)
@Description(shortDefinition="Language of the resource content", formalDefinition="The base language in which the resource is written." ) @Description(shortDefinition="Language of the resource content", formalDefinition="The base language in which the resource is written." )
protected CodeType language; protected CodeType language;
private static final long serialVersionUID = -559462759L; private static final long serialVersionUID = -559462759L;
/** /**
* Constructor * Constructor
*/ */
public Resource() { public Resource() {
super(); super();
} }
/** /**
* @return {@link #id} (The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.). This is the underlying object with id, value and extensions. The accessor "getId" gives direct access to the value * @return {@link #id} (The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.). This is the underlying object with id, value and extensions. The accessor "getId" gives direct access to the value
*/ */
public IdType getIdElement() { public IdType getIdElement() {
if (this.id == null) if (this.id == null)
if (Configuration.errorOnAutoCreate()) if (Configuration.errorOnAutoCreate())
throw new Error("Attempt to auto-create Resource.id"); throw new Error("Attempt to auto-create Resource.id");
else if (Configuration.doAutoCreate()) else if (Configuration.doAutoCreate())
this.id = new IdType(); // bb this.id = new IdType(); // bb
return this.id; return this.id;
} }
public boolean hasIdElement() { public boolean hasIdElement() {
return this.id != null && !this.id.isEmpty(); return this.id != null && !this.id.isEmpty();
} }
public boolean hasId() { public boolean hasId() {
return this.id != null && !this.id.isEmpty(); return this.id != null && !this.id.isEmpty();
} }
/** /**
* @param value {@link #id} (The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.). This is the underlying object with id, value and extensions. The accessor "getId" gives direct access to the value * @param value {@link #id} (The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.). This is the underlying object with id, value and extensions. The accessor "getId" gives direct access to the value
*/ */
public Resource setIdElement(IdType value) { @Override
this.id = value; public Resource setIdElement(IdType value) {
return this; this.id = value;
} return this;
}
/**
* @return The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes. /**
*/ * @return The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.
public String getId() { */
return this.id == null ? null : this.id.getValue(); public String getId() {
} return this.id == null ? null : this.id.getValue();
}
/**
* @param value The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes. /**
*/ * @param value The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.
public Resource setId(String value) { */
if (Utilities.noString(value)) public Resource setId(String value) {
this.id = null; if (Utilities.noString(value))
else { this.id = null;
if (this.id == null) else {
this.id = new IdType(); if (this.id == null)
this.id.setValue(value); this.id = new IdType();
} this.id.setValue(value);
return this; }
} return this;
}
/**
* @return {@link #meta} (The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.) /**
*/ * @return {@link #meta} (The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.)
public Meta getMeta() { */
if (this.meta == null) public Meta getMeta() {
if (Configuration.errorOnAutoCreate()) if (this.meta == null)
throw new Error("Attempt to auto-create Resource.meta"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Resource.meta");
this.meta = new Meta(); // cc else if (Configuration.doAutoCreate())
return this.meta; this.meta = new Meta(); // cc
} return this.meta;
}
public boolean hasMeta() {
return this.meta != null && !this.meta.isEmpty(); public boolean hasMeta() {
} return this.meta != null && !this.meta.isEmpty();
}
/**
* @param value {@link #meta} (The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.) /**
*/ * @param value {@link #meta} (The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.)
public Resource setMeta(Meta value) { */
this.meta = value; public Resource setMeta(Meta value) {
return this; this.meta = value;
} return this;
}
/**
* @return {@link #implicitRules} (A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.). This is the underlying object with id, value and extensions. The accessor "getImplicitRules" gives direct access to the value /**
*/ * @return {@link #implicitRules} (A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.). This is the underlying object with id, value and extensions. The accessor "getImplicitRules" gives direct access to the value
public UriType getImplicitRulesElement() { */
if (this.implicitRules == null) public UriType getImplicitRulesElement() {
if (Configuration.errorOnAutoCreate()) if (this.implicitRules == null)
throw new Error("Attempt to auto-create Resource.implicitRules"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Resource.implicitRules");
this.implicitRules = new UriType(); // bb else if (Configuration.doAutoCreate())
return this.implicitRules; this.implicitRules = new UriType(); // bb
} return this.implicitRules;
}
public boolean hasImplicitRulesElement() {
return this.implicitRules != null && !this.implicitRules.isEmpty(); public boolean hasImplicitRulesElement() {
} return this.implicitRules != null && !this.implicitRules.isEmpty();
}
public boolean hasImplicitRules() {
return this.implicitRules != null && !this.implicitRules.isEmpty(); public boolean hasImplicitRules() {
} return this.implicitRules != null && !this.implicitRules.isEmpty();
}
/**
* @param value {@link #implicitRules} (A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.). This is the underlying object with id, value and extensions. The accessor "getImplicitRules" gives direct access to the value /**
*/ * @param value {@link #implicitRules} (A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.). This is the underlying object with id, value and extensions. The accessor "getImplicitRules" gives direct access to the value
public Resource setImplicitRulesElement(UriType value) { */
this.implicitRules = value; public Resource setImplicitRulesElement(UriType value) {
return this; this.implicitRules = value;
} return this;
}
/**
* @return A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content. /**
*/ * @return A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.
public String getImplicitRules() { */
return this.implicitRules == null ? null : this.implicitRules.getValue(); public String getImplicitRules() {
} return this.implicitRules == null ? null : this.implicitRules.getValue();
}
/**
* @param value A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content. /**
*/ * @param value A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.
public Resource setImplicitRules(String value) { */
if (Utilities.noString(value)) public Resource setImplicitRules(String value) {
this.implicitRules = null; if (Utilities.noString(value))
else { this.implicitRules = null;
if (this.implicitRules == null) else {
this.implicitRules = new UriType(); if (this.implicitRules == null)
this.implicitRules.setValue(value); this.implicitRules = new UriType();
} this.implicitRules.setValue(value);
return this; }
} return this;
}
/**
* @return {@link #language} (The base language in which the resource is written.). This is the underlying object with id, value and extensions. The accessor "getLanguage" gives direct access to the value /**
*/ * @return {@link #language} (The base language in which the resource is written.). This is the underlying object with id, value and extensions. The accessor "getLanguage" gives direct access to the value
public CodeType getLanguageElement() { */
if (this.language == null) public CodeType getLanguageElement() {
if (Configuration.errorOnAutoCreate()) if (this.language == null)
throw new Error("Attempt to auto-create Resource.language"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Resource.language");
this.language = new CodeType(); // bb else if (Configuration.doAutoCreate())
return this.language; this.language = new CodeType(); // bb
} return this.language;
}
public boolean hasLanguageElement() {
return this.language != null && !this.language.isEmpty(); public boolean hasLanguageElement() {
} return this.language != null && !this.language.isEmpty();
}
public boolean hasLanguage() {
return this.language != null && !this.language.isEmpty(); public boolean hasLanguage() {
} return this.language != null && !this.language.isEmpty();
}
/**
* @param value {@link #language} (The base language in which the resource is written.). This is the underlying object with id, value and extensions. The accessor "getLanguage" gives direct access to the value /**
*/ * @param value {@link #language} (The base language in which the resource is written.). This is the underlying object with id, value and extensions. The accessor "getLanguage" gives direct access to the value
public Resource setLanguageElement(CodeType value) { */
this.language = value; public Resource setLanguageElement(CodeType value) {
return this; this.language = value;
} return this;
}
/**
* @return The base language in which the resource is written. /**
*/ * @return The base language in which the resource is written.
public String getLanguage() { */
return this.language == null ? null : this.language.getValue(); public String getLanguage() {
} return this.language == null ? null : this.language.getValue();
}
/**
* @param value The base language in which the resource is written. /**
*/ * @param value The base language in which the resource is written.
public Resource setLanguage(String value) { */
if (Utilities.noString(value)) public Resource setLanguage(String value) {
this.language = null; if (Utilities.noString(value))
else { this.language = null;
if (this.language == null) else {
this.language = new CodeType(); if (this.language == null)
this.language.setValue(value); this.language = new CodeType();
} this.language.setValue(value);
return this; }
} return this;
}
protected void listChildren(List<Property> childrenList) {
childrenList.add(new Property("id", "id", "The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.", 0, java.lang.Integer.MAX_VALUE, id)); protected void listChildren(List<Property> childrenList) {
childrenList.add(new Property("meta", "Meta", "The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.", 0, java.lang.Integer.MAX_VALUE, meta)); childrenList.add(new Property("id", "id", "The logical id of the resource, as used in the URL for the resource. Once assigned, this value never changes.", 0, java.lang.Integer.MAX_VALUE, id));
childrenList.add(new Property("implicitRules", "uri", "A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.", 0, java.lang.Integer.MAX_VALUE, implicitRules)); childrenList.add(new Property("meta", "Meta", "The metadata about the resource. This is content that is maintained by the infrastructure. Changes to the content may not always be associated with version changes to the resource.", 0, java.lang.Integer.MAX_VALUE, meta));
childrenList.add(new Property("language", "code", "The base language in which the resource is written.", 0, java.lang.Integer.MAX_VALUE, language)); childrenList.add(new Property("implicitRules", "uri", "A reference to a set of rules that were followed when the resource was constructed, and which must be understood when processing the content.", 0, java.lang.Integer.MAX_VALUE, implicitRules));
} childrenList.add(new Property("language", "code", "The base language in which the resource is written.", 0, java.lang.Integer.MAX_VALUE, language));
}
@Override
public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { @Override
switch (hash) { public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
case 3355: /*id*/ return this.id == null ? new Base[0] : new Base[] {this.id}; // IdType switch (hash) {
case 3347973: /*meta*/ return this.meta == null ? new Base[0] : new Base[] {this.meta}; // Meta case 3355: /*id*/ return this.id == null ? new Base[0] : new Base[] {this.id}; // IdType
case -961826286: /*implicitRules*/ return this.implicitRules == null ? new Base[0] : new Base[] {this.implicitRules}; // UriType case 3347973: /*meta*/ return this.meta == null ? new Base[0] : new Base[] {this.meta}; // Meta
case -1613589672: /*language*/ return this.language == null ? new Base[0] : new Base[] {this.language}; // CodeType case -961826286: /*implicitRules*/ return this.implicitRules == null ? new Base[0] : new Base[] {this.implicitRules}; // UriType
default: return super.getProperty(hash, name, checkValid); case -1613589672: /*language*/ return this.language == null ? new Base[0] : new Base[] {this.language}; // CodeType
} default: return super.getProperty(hash, name, checkValid);
}
}
}
@Override
public void setProperty(int hash, String name, Base value) throws FHIRException { @Override
switch (hash) { public void setProperty(int hash, String name, Base value) throws FHIRException {
case 3355: // id switch (hash) {
this.id = castToId(value); // IdType case 3355: // id
break; this.id = castToId(value); // IdType
case 3347973: // meta break;
this.meta = castToMeta(value); // Meta case 3347973: // meta
break; this.meta = castToMeta(value); // Meta
case -961826286: // implicitRules break;
this.implicitRules = castToUri(value); // UriType case -961826286: // implicitRules
break; this.implicitRules = castToUri(value); // UriType
case -1613589672: // language break;
this.language = castToCode(value); // CodeType case -1613589672: // language
break; this.language = castToCode(value); // CodeType
default: super.setProperty(hash, name, value); break;
} default: super.setProperty(hash, name, value);
}
}
}
@Override
public void setProperty(String name, Base value) throws FHIRException { @Override
if (name.equals("id")) public void setProperty(String name, Base value) throws FHIRException {
this.id = castToId(value); // IdType if (name.equals("id"))
else if (name.equals("meta")) this.id = castToId(value); // IdType
this.meta = castToMeta(value); // Meta else if (name.equals("meta"))
else if (name.equals("implicitRules")) this.meta = castToMeta(value); // Meta
this.implicitRules = castToUri(value); // UriType else if (name.equals("implicitRules"))
else if (name.equals("language")) this.implicitRules = castToUri(value); // UriType
this.language = castToCode(value); // CodeType else if (name.equals("language"))
else this.language = castToCode(value); // CodeType
super.setProperty(name, value); else
} super.setProperty(name, value);
}
@Override
public Base makeProperty(int hash, String name) throws FHIRException { @Override
switch (hash) { public Base makeProperty(int hash, String name) throws FHIRException {
case 3355: throw new FHIRException("Cannot make property id as it is not a complex type"); // IdType switch (hash) {
case 3347973: return getMeta(); // Meta case 3355: throw new FHIRException("Cannot make property id as it is not a complex type"); // IdType
case -961826286: throw new FHIRException("Cannot make property implicitRules as it is not a complex type"); // UriType case 3347973: return getMeta(); // Meta
case -1613589672: throw new FHIRException("Cannot make property language as it is not a complex type"); // CodeType case -961826286: throw new FHIRException("Cannot make property implicitRules as it is not a complex type"); // UriType
default: return super.makeProperty(hash, name); case -1613589672: throw new FHIRException("Cannot make property language as it is not a complex type"); // CodeType
} default: return super.makeProperty(hash, name);
}
}
}
@Override
public Base addChild(String name) throws FHIRException { @Override
if (name.equals("id")) { public Base addChild(String name) throws FHIRException {
throw new FHIRException("Cannot call addChild on a primitive type Resource.id"); if (name.equals("id")) {
} throw new FHIRException("Cannot call addChild on a primitive type Resource.id");
else if (name.equals("meta")) { }
this.meta = new Meta(); else if (name.equals("meta")) {
return this.meta; this.meta = new Meta();
} return this.meta;
else if (name.equals("implicitRules")) { }
throw new FHIRException("Cannot call addChild on a primitive type Resource.implicitRules"); else if (name.equals("implicitRules")) {
} throw new FHIRException("Cannot call addChild on a primitive type Resource.implicitRules");
else if (name.equals("language")) { }
throw new FHIRException("Cannot call addChild on a primitive type Resource.language"); else if (name.equals("language")) {
} throw new FHIRException("Cannot call addChild on a primitive type Resource.language");
else }
return super.addChild(name); else
} return super.addChild(name);
}
public String fhirType() {
return "Resource"; public String fhirType() {
return "Resource";
}
}
public abstract Resource copy();
public abstract Resource copy();
public void copyValues(Resource dst) {
dst.id = id == null ? null : id.copy(); public void copyValues(Resource dst) {
dst.meta = meta == null ? null : meta.copy(); dst.id = id == null ? null : id.copy();
dst.implicitRules = implicitRules == null ? null : implicitRules.copy(); dst.meta = meta == null ? null : meta.copy();
dst.language = language == null ? null : language.copy(); dst.implicitRules = implicitRules == null ? null : implicitRules.copy();
} dst.language = language == null ? null : language.copy();
}
@Override
public boolean equalsDeep(Base other) { @Override
if (!super.equalsDeep(other)) public boolean equalsDeep(Base other) {
return false; if (!super.equalsDeep(other))
if (!(other instanceof Resource)) return false;
return false; if (!(other instanceof Resource))
Resource o = (Resource) other; return false;
return compareDeep(id, o.id, true) && compareDeep(meta, o.meta, true) && compareDeep(implicitRules, o.implicitRules, true) Resource o = (Resource) other;
&& compareDeep(language, o.language, true); return compareDeep(id, o.id, true) && compareDeep(meta, o.meta, true) && compareDeep(implicitRules, o.implicitRules, true)
} && compareDeep(language, o.language, true);
}
@Override
public boolean equalsShallow(Base other) { @Override
if (!super.equalsShallow(other)) public boolean equalsShallow(Base other) {
return false; if (!super.equalsShallow(other))
if (!(other instanceof Resource)) return false;
return false; if (!(other instanceof Resource))
Resource o = (Resource) other; return false;
return compareValues(id, o.id, true) && compareValues(implicitRules, o.implicitRules, true) && compareValues(language, o.language, true) Resource o = (Resource) other;
; return compareValues(id, o.id, true) && compareValues(implicitRules, o.implicitRules, true) && compareValues(language, o.language, true)
} ;
}
public boolean isEmpty() {
return super.isEmpty() && (id == null || id.isEmpty()) && (meta == null || meta.isEmpty()) && (implicitRules == null || implicitRules.isEmpty()) public boolean isEmpty() {
&& (language == null || language.isEmpty()); return super.isEmpty() && (id == null || id.isEmpty()) && (meta == null || meta.isEmpty()) && (implicitRules == null || implicitRules.isEmpty())
} && (language == null || language.isEmpty());
}
public abstract ResourceType getResourceType();
public abstract ResourceType getResourceType();
}
}

View File

@ -1020,7 +1020,7 @@ public class JsonParserDstu2_1Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().addFamily("Smith").addGiven("John"); p.addName().addFamily("Smith").addGiven("John");
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
String output = ourCtx.newJsonParser().encodeResourceToString(p); String output = ourCtx.newJsonParser().encodeResourceToString(p);
ourLog.info(output); ourLog.info(output);

View File

@ -1695,7 +1695,7 @@ public class XmlParserDstu2_1Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().addFamily("Smith").addGiven("John"); p.addName().addFamily("Smith").addGiven("John");
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
String output = ourCtx.newXmlParser().encodeResourceToString(p); String output = ourCtx.newXmlParser().encodeResourceToString(p);
ourLog.info(output); ourLog.info(output);

View File

@ -7,7 +7,6 @@ import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu2.resource.Practitioner; import ca.uhn.fhir.model.dstu2.resource.Practitioner;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
@ -27,7 +26,7 @@ public class CustomThymeleafNarrativeGeneratorDstu2Test {
public void testGenerator() { public void testGenerator() {
// CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("file:src/test/resources/narrative/customnarrative.properties"); // CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("file:src/test/resources/narrative/customnarrative.properties");
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("classpath:narrative/customnarrative_dstu2.properties"); CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(ourCtx,"classpath:narrative/customnarrative_dstu2.properties");
ourCtx.setNarrativeGenerator(gen); ourCtx.setNarrativeGenerator(gen);
Practitioner p = new Practitioner(); Practitioner p = new Practitioner();
@ -36,10 +35,9 @@ public class CustomThymeleafNarrativeGeneratorDstu2Test {
p.addAddress().addLine("line1").addLine("line2"); p.addAddress().addLine("line1").addLine("line2");
p.getName().addFamily("fam1").addGiven("given"); p.getName().addFamily("fam1").addGiven("given");
NarrativeDt narrative = new NarrativeDt(); gen.populateResourceNarrative(p);
gen.generateNarrative(ourCtx, p, narrative);
String actual = narrative.getDiv().getValueAsString(); String actual = p.getText().getDiv().getValueAsString();
ourLog.info(actual); ourLog.info(actual);
assertThat(actual, containsString("<h1>Name</h1><div class=\"nameElement\">given <b>FAM1 </b></div><h1>Address</h1><div><span>line1 </span><br/><span>line2 </span><br/></div></div>")); assertThat(actual, containsString("<h1>Name</h1><div class=\"nameElement\">given <b>FAM1 </b></div><h1>Address</h1><div><span>line1 </span><br/><span>line2 </span><br/></div></div>"));

View File

@ -71,7 +71,7 @@ public class BundleTypeInResponseTest {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx); RestfulServer servlet = new RestfulServer(ourCtx);
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
servlet.setResourceProviders(patientProvider); servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(servlet);

View File

@ -21,4 +21,4 @@
<div th:narrative="${resource.addressFirstRep}"></div> <div th:narrative="${resource.addressFirstRep}"></div>
</div> </div>

View File

@ -8,9 +8,12 @@
# template file. # template file.
# Format is file:/path/foo.html or classpath:/com/classpath/foo.html # Format is file:/path/foo.html or classpath:/com/classpath/foo.html
# #
practitioner.class=ca.uhn.fhir.model.dstu2.resource.Practitioner practitioner.resourceType=Practitioner
practitioner.narrative=classpath:narrative/PractitionerDstu2.html practitioner.narrative=classpath:narrative/PractitionerDstu2.html
# You may also override/define behaviour for datatypes # You may also override/define behaviour for datatypes
humanname.class=ca.uhn.fhir.model.dev.composite.HumanNameDt humanname.dataType=HumanName
humanname.narrative=classpath:ca/uhn/fhir/narrative/datatype/HumanNameDt.html humanname.narrative=classpath:ca/uhn/fhir/narrative/datatype/HumanNameDt.html
address.dataType=Address
address.narrative=classpath:ca/uhn/fhir/narrative/datatype/AddressDt.html

View File

@ -1,11 +1,10 @@
package org.hl7.fhir.dstu3.hapi.validation; package org.hl7.fhir.dstu3.hapi.ctx;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.io.Charsets; import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;

View File

@ -51,7 +51,7 @@ public class FhirDstu3 implements IFhirVersion {
@Override @Override
public IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport() { public IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport() {
String className = "org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport"; String className = "org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport";
try { try {
return (IContextValidationSupport<?, ?, ?, ?, ?, ?>) Class.forName(className).newInstance(); return (IContextValidationSupport<?, ?, ?, ?, ?, ?>) Class.forName(className).newInstance();
} catch (Exception theE) { } catch (Exception theE) {

View File

@ -37,6 +37,7 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
@Override
public IBaseResource getResource() { public IBaseResource getResource() {
return resource; return resource;
} }
@ -53,8 +54,10 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
public void setResource(IBaseResource theResource) { @Override
public BaseReference setResource(IBaseResource theResource) {
resource = theResource; resource = theResource;
return this;
} }
@Override @Override

View File

@ -1,389 +1,390 @@
package org.hl7.fhir.dstu3.model; package org.hl7.fhir.dstu3.model;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to * Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific endorse or promote products derived from this software without specific
prior written permission. prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
// Generated on Fri, Mar 16, 2018 15:21+1100 for FHIR v3.0.1 // Generated on Fri, Mar 16, 2018 15:21+1100 for FHIR v3.0.1
import java.util.*; import java.util.*;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ChildOrder; import ca.uhn.fhir.model.api.annotation.ChildOrder;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Block; import ca.uhn.fhir.model.api.annotation.Block;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.FHIRFormatError;
/** /**
* A reference from one resource to another. * A reference from one resource to another.
*/ */
@DatatypeDef(name="Reference") @DatatypeDef(name="Reference")
public class Reference extends BaseReference implements IBaseReference, ICompositeType { public class Reference extends BaseReference implements IBaseReference, ICompositeType {
/** /**
* A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. * A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
*/ */
@Child(name = "reference", type = {StringType.class}, order=0, min=0, max=1, modifier=false, summary=true) @Child(name = "reference", type = {StringType.class}, order=0, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Literal reference, Relative, internal or absolute URL", formalDefinition="A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources." ) @Description(shortDefinition="Literal reference, Relative, internal or absolute URL", formalDefinition="A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources." )
protected StringType reference; protected StringType reference;
/** /**
* An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference. * An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.
*/ */
@Child(name = "identifier", type = {Identifier.class}, order=1, min=0, max=1, modifier=false, summary=true) @Child(name = "identifier", type = {Identifier.class}, order=1, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Logical reference, when literal reference is not known", formalDefinition="An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference." ) @Description(shortDefinition="Logical reference, when literal reference is not known", formalDefinition="An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference." )
protected Identifier identifier; protected Identifier identifier;
/** /**
* Plain text narrative that identifies the resource in addition to the resource reference. * Plain text narrative that identifies the resource in addition to the resource reference.
*/ */
@Child(name = "display", type = {StringType.class}, order=2, min=0, max=1, modifier=false, summary=true) @Child(name = "display", type = {StringType.class}, order=2, min=0, max=1, modifier=false, summary=true)
@Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." )
protected StringType display; protected StringType display;
private static final long serialVersionUID = -909353281L; private static final long serialVersionUID = -909353281L;
/** /**
* Constructor * Constructor
*/ */
public Reference() { public Reference() {
super(); super();
} }
/** /**
* Constructor * Constructor
* *
* @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123")
*/ */
public Reference(String theReference) { public Reference(String theReference) {
super(theReference); super(theReference);
} }
/** /**
* Constructor * Constructor
* *
* @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123")
*/ */
public Reference(IIdType theReference) { public Reference(IIdType theReference) {
super(theReference); super(theReference);
} }
/** /**
* Constructor * Constructor
* *
* @param theResource The resource represented by this reference * @param theResource The resource represented by this reference
*/ */
public Reference(IAnyResource theResource) { public Reference(IAnyResource theResource) {
super(theResource); super(theResource);
} }
/** /**
* @return {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value * @return {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value
*/ */
public StringType getReferenceElement_() { public StringType getReferenceElement_() {
if (this.reference == null) if (this.reference == null)
if (Configuration.errorOnAutoCreate()) if (Configuration.errorOnAutoCreate())
throw new Error("Attempt to auto-create Reference.reference"); throw new Error("Attempt to auto-create Reference.reference");
else if (Configuration.doAutoCreate()) else if (Configuration.doAutoCreate())
this.reference = new StringType(); // bb this.reference = new StringType(); // bb
return this.reference; return this.reference;
} }
public boolean hasReferenceElement() { public boolean hasReferenceElement() {
return this.reference != null && !this.reference.isEmpty(); return this.reference != null && !this.reference.isEmpty();
} }
public boolean hasReference() { public boolean hasReference() {
return this.reference != null && !this.reference.isEmpty(); return this.reference != null && !this.reference.isEmpty();
} }
/** /**
* @param value {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value * @param value {@link #reference} (A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.). This is the underlying object with id, value and extensions. The accessor "getReference" gives direct access to the value
*/ */
public Reference setReferenceElement(StringType value) { public Reference setReferenceElement(StringType value) {
this.reference = value; this.reference = value;
return this; return this;
} }
/** /**
* @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. * @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
*/ */
public String getReference() { @Override
return this.reference == null ? null : this.reference.getValue(); public String getReference() {
} return this.reference == null ? null : this.reference.getValue();
}
/**
* @param value A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. /**
*/ * @param value A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
public Reference setReference(String value) { */
if (Utilities.noString(value)) public Reference setReference(String value) {
this.reference = null; if (Utilities.noString(value))
else { this.reference = null;
if (this.reference == null) else {
this.reference = new StringType(); if (this.reference == null)
this.reference.setValue(value); this.reference = new StringType();
} this.reference.setValue(value);
return this; }
} return this;
}
/**
* @return {@link #identifier} (An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.) /**
*/ * @return {@link #identifier} (An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.)
public Identifier getIdentifier() { */
if (this.identifier == null) public Identifier getIdentifier() {
if (Configuration.errorOnAutoCreate()) if (this.identifier == null)
throw new Error("Attempt to auto-create Reference.identifier"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Reference.identifier");
this.identifier = new Identifier(); // cc else if (Configuration.doAutoCreate())
return this.identifier; this.identifier = new Identifier(); // cc
} return this.identifier;
}
public boolean hasIdentifier() {
return this.identifier != null && !this.identifier.isEmpty(); public boolean hasIdentifier() {
} return this.identifier != null && !this.identifier.isEmpty();
}
/**
* @param value {@link #identifier} (An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.) /**
*/ * @param value {@link #identifier} (An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.)
public Reference setIdentifier(Identifier value) { */
this.identifier = value; public Reference setIdentifier(Identifier value) {
return this; this.identifier = value;
} return this;
}
/**
* @return {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value /**
*/ * @return {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value
public StringType getDisplayElement() { */
if (this.display == null) public StringType getDisplayElement() {
if (Configuration.errorOnAutoCreate()) if (this.display == null)
throw new Error("Attempt to auto-create Reference.display"); if (Configuration.errorOnAutoCreate())
else if (Configuration.doAutoCreate()) throw new Error("Attempt to auto-create Reference.display");
this.display = new StringType(); // bb else if (Configuration.doAutoCreate())
return this.display; this.display = new StringType(); // bb
} return this.display;
}
public boolean hasDisplayElement() {
return this.display != null && !this.display.isEmpty(); public boolean hasDisplayElement() {
} return this.display != null && !this.display.isEmpty();
}
public boolean hasDisplay() {
return this.display != null && !this.display.isEmpty(); public boolean hasDisplay() {
} return this.display != null && !this.display.isEmpty();
}
/**
* @param value {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value /**
*/ * @param value {@link #display} (Plain text narrative that identifies the resource in addition to the resource reference.). This is the underlying object with id, value and extensions. The accessor "getDisplay" gives direct access to the value
public Reference setDisplayElement(StringType value) { */
this.display = value; public Reference setDisplayElement(StringType value) {
return this; this.display = value;
} return this;
}
/**
* @return Plain text narrative that identifies the resource in addition to the resource reference. /**
*/ * @return Plain text narrative that identifies the resource in addition to the resource reference.
public String getDisplay() { */
return this.display == null ? null : this.display.getValue(); public String getDisplay() {
} return this.display == null ? null : this.display.getValue();
}
/**
* @param value Plain text narrative that identifies the resource in addition to the resource reference. /**
*/ * @param value Plain text narrative that identifies the resource in addition to the resource reference.
public Reference setDisplay(String value) { */
if (Utilities.noString(value)) public Reference setDisplay(String value) {
this.display = null; if (Utilities.noString(value))
else { this.display = null;
if (this.display == null) else {
this.display = new StringType(); if (this.display == null)
this.display.setValue(value); this.display = new StringType();
} this.display.setValue(value);
return this; }
} return this;
}
/**
* Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given /**
* reference. * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given
* * reference.
* @param theReference The reference, or <code>null</code> *
* @return * @param theReference The reference, or <code>null</code>
* @return Returns a reference to this * @return
*/ * @return Returns a reference to this
public Reference setReferenceElement(IIdType theReference) { */
if (theReference != null) { public Reference setReferenceElement(IIdType theReference) {
setReference(theReference.getValue()); if (theReference != null) {
} else { setReference(theReference.getValue());
setReference(null); } else {
} setReference(null);
return this; }
} return this;
protected void listChildren(List<Property> children) { }
super.listChildren(children); protected void listChildren(List<Property> children) {
children.add(new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, 1, reference)); super.listChildren(children);
children.add(new Property("identifier", "Identifier", "An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.", 0, 1, identifier)); children.add(new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, 1, reference));
children.add(new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, 1, display)); children.add(new Property("identifier", "Identifier", "An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.", 0, 1, identifier));
} children.add(new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, 1, display));
}
@Override
public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException { @Override
switch (_hash) { public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
case -925155509: /*reference*/ return new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, 1, reference); switch (_hash) {
case -1618432855: /*identifier*/ return new Property("identifier", "Identifier", "An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.", 0, 1, identifier); case -925155509: /*reference*/ return new Property("reference", "string", "A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", 0, 1, reference);
case 1671764162: /*display*/ return new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, 1, display); case -1618432855: /*identifier*/ return new Property("identifier", "Identifier", "An identifier for the other resource. This is used when there is no way to reference the other resource directly, either because the entity is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference.", 0, 1, identifier);
default: return super.getNamedProperty(_hash, _name, _checkValid); case 1671764162: /*display*/ return new Property("display", "string", "Plain text narrative that identifies the resource in addition to the resource reference.", 0, 1, display);
} default: return super.getNamedProperty(_hash, _name, _checkValid);
}
}
}
@Override
public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { @Override
switch (hash) { public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType switch (hash) {
case -1618432855: /*identifier*/ return this.identifier == null ? new Base[0] : new Base[] {this.identifier}; // Identifier case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType
case 1671764162: /*display*/ return this.display == null ? new Base[0] : new Base[] {this.display}; // StringType case -1618432855: /*identifier*/ return this.identifier == null ? new Base[0] : new Base[] {this.identifier}; // Identifier
default: return super.getProperty(hash, name, checkValid); case 1671764162: /*display*/ return this.display == null ? new Base[0] : new Base[] {this.display}; // StringType
} default: return super.getProperty(hash, name, checkValid);
}
}
}
@Override
public Base setProperty(int hash, String name, Base value) throws FHIRException { @Override
switch (hash) { public Base setProperty(int hash, String name, Base value) throws FHIRException {
case -925155509: // reference switch (hash) {
this.reference = castToString(value); // StringType case -925155509: // reference
return value; this.reference = castToString(value); // StringType
case -1618432855: // identifier return value;
this.identifier = castToIdentifier(value); // Identifier case -1618432855: // identifier
return value; this.identifier = castToIdentifier(value); // Identifier
case 1671764162: // display return value;
this.display = castToString(value); // StringType case 1671764162: // display
return value; this.display = castToString(value); // StringType
default: return super.setProperty(hash, name, value); return value;
} default: return super.setProperty(hash, name, value);
}
}
}
@Override
public Base setProperty(String name, Base value) throws FHIRException { @Override
if (name.equals("reference")) { public Base setProperty(String name, Base value) throws FHIRException {
this.reference = castToString(value); // StringType if (name.equals("reference")) {
} else if (name.equals("identifier")) { this.reference = castToString(value); // StringType
this.identifier = castToIdentifier(value); // Identifier } else if (name.equals("identifier")) {
} else if (name.equals("display")) { this.identifier = castToIdentifier(value); // Identifier
this.display = castToString(value); // StringType } else if (name.equals("display")) {
} else this.display = castToString(value); // StringType
return super.setProperty(name, value); } else
return value; return super.setProperty(name, value);
} return value;
}
@Override
public Base makeProperty(int hash, String name) throws FHIRException { @Override
switch (hash) { public Base makeProperty(int hash, String name) throws FHIRException {
case -925155509: return getReferenceElement_(); switch (hash) {
case -1618432855: return getIdentifier(); case -925155509: return getReferenceElement_();
case 1671764162: return getDisplayElement(); case -1618432855: return getIdentifier();
default: return super.makeProperty(hash, name); case 1671764162: return getDisplayElement();
} default: return super.makeProperty(hash, name);
}
}
}
@Override
public String[] getTypesForProperty(int hash, String name) throws FHIRException { @Override
switch (hash) { public String[] getTypesForProperty(int hash, String name) throws FHIRException {
case -925155509: /*reference*/ return new String[] {"string"}; switch (hash) {
case -1618432855: /*identifier*/ return new String[] {"Identifier"}; case -925155509: /*reference*/ return new String[] {"string"};
case 1671764162: /*display*/ return new String[] {"string"}; case -1618432855: /*identifier*/ return new String[] {"Identifier"};
default: return super.getTypesForProperty(hash, name); case 1671764162: /*display*/ return new String[] {"string"};
} default: return super.getTypesForProperty(hash, name);
}
}
}
@Override
public Base addChild(String name) throws FHIRException { @Override
if (name.equals("reference")) { public Base addChild(String name) throws FHIRException {
throw new FHIRException("Cannot call addChild on a primitive type Reference.reference"); if (name.equals("reference")) {
} throw new FHIRException("Cannot call addChild on a primitive type Reference.reference");
else if (name.equals("identifier")) { }
this.identifier = new Identifier(); else if (name.equals("identifier")) {
return this.identifier; this.identifier = new Identifier();
} return this.identifier;
else if (name.equals("display")) { }
throw new FHIRException("Cannot call addChild on a primitive type Reference.display"); else if (name.equals("display")) {
} throw new FHIRException("Cannot call addChild on a primitive type Reference.display");
else }
return super.addChild(name); else
} return super.addChild(name);
}
public String fhirType() {
return "Reference"; public String fhirType() {
return "Reference";
}
}
public Reference copy() {
Reference dst = new Reference(); public Reference copy() {
copyValues(dst); Reference dst = new Reference();
dst.reference = reference == null ? null : reference.copy(); copyValues(dst);
dst.identifier = identifier == null ? null : identifier.copy(); dst.reference = reference == null ? null : reference.copy();
dst.display = display == null ? null : display.copy(); dst.identifier = identifier == null ? null : identifier.copy();
return dst; dst.display = display == null ? null : display.copy();
} return dst;
}
protected Reference typedCopy() {
return copy(); protected Reference typedCopy() {
} return copy();
}
@Override
public boolean equalsDeep(Base other_) { @Override
if (!super.equalsDeep(other_)) public boolean equalsDeep(Base other_) {
return false; if (!super.equalsDeep(other_))
if (!(other_ instanceof Reference)) return false;
return false; if (!(other_ instanceof Reference))
Reference o = (Reference) other_; return false;
return compareDeep(reference, o.reference, true) && compareDeep(identifier, o.identifier, true) Reference o = (Reference) other_;
&& compareDeep(display, o.display, true); return compareDeep(reference, o.reference, true) && compareDeep(identifier, o.identifier, true)
} && compareDeep(display, o.display, true);
}
@Override
public boolean equalsShallow(Base other_) { @Override
if (!super.equalsShallow(other_)) public boolean equalsShallow(Base other_) {
return false; if (!super.equalsShallow(other_))
if (!(other_ instanceof Reference)) return false;
return false; if (!(other_ instanceof Reference))
Reference o = (Reference) other_; return false;
return compareValues(reference, o.reference, true) && compareValues(display, o.display, true); Reference o = (Reference) other_;
} return compareValues(reference, o.reference, true) && compareValues(display, o.display, true);
}
public boolean isEmpty() {
return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(reference, identifier, display public boolean isEmpty() {
); return super.isEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(reference, identifier, display
} );
}
}
}

View File

@ -20,7 +20,6 @@ import org.hl7.fhir.dstu3.model.DiagnosticReport.DiagnosticReportStatus;
import org.hl7.fhir.dstu3.model.Medication; import org.hl7.fhir.dstu3.model.Medication;
import org.hl7.fhir.dstu3.model.MedicationRequest; import org.hl7.fhir.dstu3.model.MedicationRequest;
import org.hl7.fhir.dstu3.model.MedicationRequest.MedicationRequestStatus; import org.hl7.fhir.dstu3.model.MedicationRequest.MedicationRequestStatus;
import org.hl7.fhir.dstu3.model.Narrative;
import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome;
@ -47,10 +46,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
@Before @Before
public void before() { public void before() {
myGen = new DefaultThymeleafNarrativeGenerator(); myGen = new DefaultThymeleafNarrativeGenerator(ourCtx);
myGen.setUseHapiServerConformanceNarrative(true); myGen.setUseHapiServerConformanceNarrative(true);
myGen.setIgnoreFailures(false);
myGen.setIgnoreMissingTemplates(false);
ourCtx.setNarrativeGenerator(myGen); ourCtx.setNarrativeGenerator(myGen);
} }
@ -67,18 +64,15 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
Patient value = new Patient(); Patient value = new Patient();
value.addIdentifier().setSystem("urn:names").setValue("123456"); value.addIdentifier().setSystem("urn:names").setValue("123456");
value.addName().setFamily("blow").addGiven("joe").addGiven((String) null).addGiven("john"); value.addName().setFamily("blow").addGiven("joe").addGiven(null).addGiven("john");
//@formatter:off
value.addAddress() value.addAddress()
.addLine("123 Fake Street").addLine("Unit 1") .addLine("123 Fake Street").addLine("Unit 1")
.setCity("Toronto").setState("ON").setCountry("Canada"); .setCity("Toronto").setState("ON").setCountry("Canada");
//@formatter:on
value.setBirthDate(new Date()); value.setBirthDate(new Date());
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(value);
myGen.generateNarrative(ourCtx, value, narrative); String output = value.getText().getDiv().getValueAsString();
String output = narrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\">joe john <b>BLOW </b></div>")); assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\">joe john <b>BLOW </b></div>"));
@ -86,9 +80,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
@Test @Test
public void testTranslations() throws DataFormatException { public void testTranslations() throws DataFormatException {
CustomThymeleafNarrativeGenerator customGen = new CustomThymeleafNarrativeGenerator("classpath:/testnarrative.properties"); CustomThymeleafNarrativeGenerator customGen = new CustomThymeleafNarrativeGenerator(ourCtx, "classpath:/testnarrative.properties");
customGen.setIgnoreFailures(false);
customGen.setIgnoreMissingTemplates(false);
FhirContext ctx = FhirContext.forDstu3(); FhirContext ctx = FhirContext.forDstu3();
ctx.setNarrativeGenerator(customGen); ctx.setNarrativeGenerator(customGen);
@ -96,7 +88,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
Patient value = new Patient(); Patient value = new Patient();
value.addIdentifier().setSystem("urn:names").setValue("123456"); value.addIdentifier().setSystem("urn:names").setValue("123456");
value.addName().setFamily("blow").addGiven("joe").addGiven((String) null).addGiven("john"); value.addName().setFamily("blow").addGiven("joe").addGiven(null).addGiven("john");
//@formatter:off //@formatter:off
value.addAddress() value.addAddress()
.addLine("123 Fake Street").addLine("Unit 1") .addLine("123 Fake Street").addLine("Unit 1")
@ -106,7 +98,6 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
value.setBirthDate(new Date()); value.setBirthDate(new Date());
Transformer transformer = new Transformer() { Transformer transformer = new Transformer() {
@Override @Override
public Object transform(Object input) { public Object transform(Object input) {
return "UNTRANSLATED:" + input; return "UNTRANSLATED:" + input;
@ -123,9 +114,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
} }
}); });
Narrative narrative = new Narrative(); customGen.populateResourceNarrative(value);
customGen.generateNarrative(ctx, value, narrative); String output = value.getText().getDiv().getValueAsString();
String output = narrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString("Some beautiful proze")); assertThat(output, StringContains.containsString("Some beautiful proze"));
assertThat(output, StringContains.containsString("UNTRANSLATED:other_text")); assertThat(output, StringContains.containsString("UNTRANSLATED:other_text"));
@ -140,9 +130,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
value.addResult().setReference("Observation/2"); value.addResult().setReference("Observation/2");
value.addResult().setReference("Observation/3"); value.addResult().setReference("Observation/3");
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(value);
myGen.generateNarrative(ourCtx, value, narrative); String output = value.getText().getDiv().getValueAsString();
String output = narrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString(value.getCode().getTextElement().getValue())); assertThat(output, StringContains.containsString(value.getCode().getTextElement().getValue()));
@ -169,9 +158,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
// ourLog.info(output); // ourLog.info(output);
// assertEquals("Operation Outcome (2 issues)", output); // assertEquals("Operation Outcome (2 issues)", output);
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(oo);
myGen.generateNarrative(ourCtx, oo, narrative); String output = oo.getText().getDiv().getValueAsString();
String output = narrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
@ -209,9 +197,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
value.addResult().setResource(obs); value.addResult().setResource(obs);
} }
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(value);
myGen.generateNarrative(ourCtx, value, narrative); String output = value.getText().getDiv().getValueAsString();
String output = narrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some &amp; Diagnostic Report </div>")); assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some &amp; Diagnostic Report </div>"));
@ -230,11 +217,11 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
mp.setStatus(MedicationRequestStatus.ACTIVE); mp.setStatus(MedicationRequestStatus.ACTIVE);
mp.setAuthoredOnElement(new DateTimeType("2014-09-01")); mp.setAuthoredOnElement(new DateTimeType("2014-09-01"));
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(mp);
myGen.generateNarrative(ourCtx, mp, narrative); String output = mp.getText().getDiv().getValueAsString();
assertTrue("Expected medication name of ciprofloaxin within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ciprofloaxin") > -1); assertTrue("Expected medication name of ciprofloaxin within narrative: "+output, output.contains("ciprofloaxin"));
assertTrue("Expected string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1); assertTrue("Expected string status of ACTIVE within narrative: " +output, output.contains("ACTIVE"));
} }
@ -243,11 +230,10 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test {
Medication med = new Medication(); Medication med = new Medication();
med.getCode().setText("ciproflaxin"); med.getCode().setText("ciproflaxin");
Narrative narrative = new Narrative(); myGen.populateResourceNarrative(med);
myGen.generateNarrative(ourCtx, med, narrative);
String string = narrative.getDiv().getValueAsString(); String output = med.getText().getDiv().getValueAsString();
assertThat(string, containsString("ciproflaxin")); assertThat(output, containsString("ciproflaxin"));
} }

View File

@ -0,0 +1,85 @@
package ca.uhn.fhir.narrative2;
import ca.uhn.fhir.context.FhirContext;
import org.hamcrest.Matchers;
import org.hl7.fhir.dstu3.model.*;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class ThymeleafNarrativeGeneratorTest {
private static final Logger ourLog = LoggerFactory.getLogger(ThymeleafNarrativeGeneratorTest.class);
private FhirContext myCtx = FhirContext.forDstu3();
@Test
public void testGenerateCompositionWithContextPath() throws IOException {
DiagnosticReport dr1 = new DiagnosticReport();
dr1.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
dr1.setIssuedElement(new InstantType("2019-01-01T12:12:12-05:00"));
dr1.getCode().getCodingFirstRep().setDisplay("Complete Blood Count");
Observation obs1 = new Observation();
obs1.getCode().getCodingFirstRep().setDisplay("Hemoglobin [Mass/volume] in Blood");
obs1.setValue(new Quantity(null, 176, "http://unitsofmeasure.org", "g/L", "g/L"));
obs1.getReferenceRangeFirstRep().getLow().setValue(135).setSystem("http://unitsofmeasure.org").setCode("g/L").setUnit("g/L");
obs1.getReferenceRangeFirstRep().getHigh().setValue(180).setSystem("http://unitsofmeasure.org").setCode("g/L").setUnit("g/L");
obs1.getReferenceRangeFirstRep().getTextElement().setValue("135 - 180");
dr1.addResult().setResource(obs1);
Observation obs2 = new Observation();
obs2.getCode().getCodingFirstRep().setDisplay("Erythrocytes [#/volume] in Blood by Automated count");
obs2.setValue(new Quantity(null, 5.9, "http://unitsofmeasure.org", "x10*12/L", "x10*12/L"));
obs2.getReferenceRangeFirstRep().getLow().setValue(4.2).setSystem("http://unitsofmeasure.org").setCode("x10*12/L").setUnit("x10*12/L");
obs2.getReferenceRangeFirstRep().getHigh().setValue(6.0).setSystem("http://unitsofmeasure.org").setCode("x10*12/L").setUnit("x10*12/L");
obs2.getReferenceRangeFirstRep().getTextElement().setValue("4.2 - 6.0");
dr1.addResult().setResource(obs2);
Composition composition = new Composition();
Composition.SectionComponent sect = composition.addSection();
sect.setTitle("History of Medication use Narrative");
sect.getCode().getCodingFirstRep().setSystem("2.16.840.1.113883.6.1");
sect.getCode().getCodingFirstRep().setCode("10160-0");
sect.getCode().getCodingFirstRep().setDisplay("History of Medication use Narrative");
sect = composition.addSection();
sect.setTitle("Relevant diagnostic tests/laboratory data Narrative");
sect.getCode().getCodingFirstRep().setSystem("2.16.840.1.113883.6.1");
sect.getCode().getCodingFirstRep().setCode("30954-2");
sect.getCode().getCodingFirstRep().setDisplay("Relevant diagnostic tests/laboratory data Narrative");
Reference ref = new Reference();
ref.setReference("DiagnosticReport/1").setResource(dr1);
sect.getEntry().add(ref);
NarrativeTemplateManifest manifest = NarrativeTemplateManifest.forManifestFileLocation(myCtx, "classpath:narrative2/narratives.properties");
ThymeleafNarrativeGenerator gen = new ThymeleafNarrativeGenerator(myCtx);
gen.setManifest(manifest);
gen.populateResourceNarrative(composition);
// Firt narrative should be empty
String narrative = composition.getSection().get(0).getText().getDiv().getValueAsString();
assertThat(narrative, Matchers.emptyOrNullString());
// Second narrative should have details
narrative = composition.getSection().get(1).getText().getDiv().getValueAsString();
ourLog.info("Narrative:\n{}", narrative);
assertThat(narrative, containsString("<thead><tr><td>Name</td><td>Value</td>"));
assertThat(narrative, containsString("<td> 4.2 - 6.0 </td>"));
}
@Test
public void testTemplateCount() throws IOException {
NarrativeTemplateManifest manifest = NarrativeTemplateManifest.forManifestFileLocation(myCtx, "classpath:narrative2/narratives.properties");
assertEquals(4, manifest.getNamedTemplateCount());
}
}

View File

@ -1241,7 +1241,7 @@ public class JsonParserDstu3Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().setFamily("Smith").addGiven("John"); p.addName().setFamily("Smith").addGiven("John");
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
String output = ourCtx.newJsonParser().encodeResourceToString(p); String output = ourCtx.newJsonParser().encodeResourceToString(p);
ourLog.info(output); ourLog.info(output);

View File

@ -1943,7 +1943,7 @@ public class XmlParserDstu3Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().setFamily("Smith").addGiven("John"); p.addName().setFamily("Smith").addGiven("John");
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
String output = ourCtx.newXmlParser().encodeResourceToString(p); String output = ourCtx.newXmlParser().encodeResourceToString(p);
ourLog.info(output); ourLog.info(output);

View File

@ -0,0 +1,12 @@
<th:block xmlns:th="http://www.thymeleaf.org">
<!--/* Try to pick the best title from what's available */-->
<th:block th:if="${not resource.code.textElement.empty} or ${resource.code.coding.empty}" th:text="${resource.code.textElement.value}"/>
<th:block th:if="${not resource.code.coding.empty} and ${resource.code.textElement.empty} and ${not resource.code.coding[0].displayElement.empty}" th:text="${resource.code.coding[0].display}"/>
<th:block th:if="${not resource.code.coding.empty} and ${resource.code.textElement.empty} and ${resource.code.coding[0].displayElement.empty}" th:text="Untitled Diagnostic Report"/>
<th:block th:each="element : ${resource.entry}">
<th:narrative th:name="diagnosticreport" th:element="${element.getResource()}"/>
</th:block>
</th:block>

View File

@ -0,0 +1,61 @@
<th:block xmlns:th="http://www.thymeleaf.org">
<table>
<tbody>
<tr th:if="${not resource.statusElement.empty}">
<td>Status</td>
<td th:text="${resource.statusElement.value}"></td>
</tr>
<tr th:if="${not resource.issuedElement.empty}">
<td>Issued</td>
<td><th:narrative th:element="${resource.issuedElement}"/></td>
</tr>
<tr th:if="${not resource.conclusionElement.empty}">
<td>Conclusion</td>
<td><th:narrative th:element="${resource.conclusionElement}"/></td>
</tr>
</tbody>
</table>
<table th:if="${not #lists.isEmpty(resource.result)} and ${resource.result[0].resource != null}">
<thead>
<tr>
<td>Name</td>
<td>Value</td>
<td>Interpretation</td>
<td>Reference Range</td>
<td>Status</td>
</tr>
</thead>
<tbody>
<th:block th:each="result,rowStat : ${resource.result}">
<tr>
<td>
<th:block th:if="${not result.resource.code.textElement.empty}" th:text="${result.resource.code.textElement.value}"/>
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${not result.resource.code.coding[0].displayElement.empty}" th:text="${result.resource.code.coding[0].display}"/>
<th:block th:if="${result.resource.code.textElement.empty} and ${not #lists.isEmpty(result.resource.code.coding)} and ${not result.resource.code.coding[0].empty} and ${result.resource.code.coding[0].displayElement.empty}" th:text="'?'"/>
</td>
<td><th:narrative th:element="${result.resource.value}"/></td>
<td>
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
</td>
<td>
<th:block th:if="${not result.resource.referenceRange.empty} and ${not result.resource.referenceRange[0].empty}">
<th:narrative th:element="${result.resource.referenceRange[0].low}"/>
-
<th:narrative th:element="${result.resource.referenceRange[0].high}"/>
</th:block>
</td>
<td th:text="${result.resource.statusElement.value}"></td>
</tr>
<tr th:if="${not result.resource.commentElement.empty}">
<td th:text="${result.resource.commentElement.value}" colspan="5"></td>
</tr>
</th:block>
</tbody>
</table>
</th:block>

View File

@ -0,0 +1,3 @@
<th:block xmlns:th="http://www.thymeleaf.org">
[[${resource.getValueAsString().replace("T", " ")}]]
</th:block>

View File

@ -0,0 +1,3 @@
<th:block xmlns:th="http://www.thymeleaf.org">
[[${resource.value}]]
</th:block>

View File

@ -0,0 +1,24 @@
################################################
# Resources
################################################
composition.resourceType = Composition
composition.contextPath = Composition.section.where(code.coding.system='2.16.840.1.113883.6.1' and code.coding.code='30954-2')
composition.style = thymeleaf
composition.narrative = classpath:narrative2/composition.html
diagnosticreport.style = thymeleaf
diagnosticreport.narrative = classpath:narrative2/diagnosticreport.html
################################################
# Datatypes
################################################
datetime.dataType = instant, dateTime, date
datetime.narrative = classpath:narrative2/dt_datetime.html
quantity_valueonly.dataType = Quantity, SimpleQuantity
quantity_valueonly.narrative = classpath:narrative2/dt_quantity.html

View File

@ -1,2 +1,2 @@
patient.class=org.hl7.fhir.dstu3.model.Patient patient.resourceType=Patient
patient.narrative=classpath:/TestPatient.html patient.narrative=classpath:/TestPatient.html

View File

@ -49,8 +49,10 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
public void setResource(IBaseResource theResource) { @Override
public BaseReference setResource(IBaseResource theResource) {
resource = theResource; resource = theResource;
return null;
} }
@Override @Override

View File

@ -64,7 +64,7 @@ public class BundleTypeInResponseHl7OrgTest {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx); RestfulServer servlet = new RestfulServer(ourCtx);
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
servlet.setResourceProviders(patientProvider); servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(servlet);

View File

@ -37,6 +37,7 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
@Override
public IBaseResource getResource() { public IBaseResource getResource() {
return resource; return resource;
} }
@ -53,8 +54,10 @@ public abstract class BaseReference extends Type implements IBaseReference, ICom
* a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property * a part of the FHIR "wire format" and is never transmitted or receieved inline, but this property
* may be changed/accessed by parsers. * may be changed/accessed by parsers.
*/ */
public void setResource(IBaseResource theResource) { @Override
public BaseReference setResource(IBaseResource theResource) {
resource = theResource; resource = theResource;
return this;
} }
@Override @Override

View File

@ -145,7 +145,8 @@ The type is the Canonical URL of Resource Definition that is the type this refer
/** /**
* @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. * @return A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.
*/ */
public String getReference() { @Override
public String getReference() {
return this.reference == null ? null : this.reference.getValue(); return this.reference == null ? null : this.reference.getValue();
} }

View File

@ -449,7 +449,7 @@ public class DateRangeParamR4Test {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx); RestfulServer servlet = new RestfulServer(ourCtx);
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
servlet.setResourceProviders(patientProvider); servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(servlet);

View File

@ -474,7 +474,7 @@ public class SearchSearchServerDstu1Test {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx); ourServlet = new RestfulServer(ourCtx);
ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx));
ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)); ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10));
ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider()); ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider());

View File

@ -17,6 +17,7 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.fhir.ucum.UcumService; import org.fhir.ucum.UcumService;
import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.validation;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.ActivityDefinition; import org.hl7.fhir.dstu3.model.ActivityDefinition;
import org.hl7.fhir.dstu3.model.ConceptMap; import org.hl7.fhir.dstu3.model.ConceptMap;

View File

@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.model.ElementDefinition; import org.hl7.fhir.dstu3.model.ElementDefinition;
import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;

View File

@ -2,7 +2,7 @@ package org.hl7.fhir.dstu3.hapi.validation;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
@ -59,7 +60,6 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.utils.FHIRPathEngine; import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;

View File

@ -9,6 +9,7 @@ import ca.uhn.fhir.validation.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;

View File

@ -6,6 +6,7 @@ import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Coding;

View File

@ -14,6 +14,7 @@ import org.apache.commons.lang3.Validate;
import org.hamcrest.core.StringContains; import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities; import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Condition.ConditionClinicalStatus; import org.hl7.fhir.dstu3.model.Condition.ConditionClinicalStatus;

View File

@ -7,6 +7,7 @@ import java.util.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.utils.StructureMapUtilities; import org.hl7.fhir.dstu3.utils.StructureMapUtilities;

View File

@ -3,7 +3,7 @@ package org.hl7.fhir.dstu3.utils;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.junit.AfterClass; import org.junit.AfterClass;

View File

@ -38,7 +38,7 @@ public class ExampleRestfulServlet extends RestfulServer {
* Two resource providers are defined. Each one handles a specific * Two resource providers are defined. Each one handles a specific
* type of resource. * type of resource.
*/ */
List<IResourceProvider> providers = new ArrayList<IResourceProvider>(); List<IResourceProvider> providers = new ArrayList<>();
providers.add(new PatientResourceProvider()); providers.add(new PatientResourceProvider());
providers.add(new OrganizationResourceProvider()); providers.add(new OrganizationResourceProvider());
setResourceProviders(providers); setResourceProviders(providers);
@ -48,7 +48,7 @@ public class ExampleRestfulServlet extends RestfulServer {
* but can be useful as it causes HAPI to generate narratives for * but can be useful as it causes HAPI to generate narratives for
* resources which don't otherwise have one. * resources which don't otherwise have one.
*/ */
INarrativeGenerator narrativeGen = new DefaultThymeleafNarrativeGenerator(); INarrativeGenerator narrativeGen = new DefaultThymeleafNarrativeGenerator(getFhirContext());
getFhirContext().setNarrativeGenerator(narrativeGen); getFhirContext().setNarrativeGenerator(narrativeGen);
/* /*