From 48c10bddc54aa70a4f8e3a2c3705dccc2d879afb Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 27 Feb 2019 14:59:57 -0500 Subject: [PATCH] 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 --- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 2 +- .../uhn/fhir/jpa/demo/JpaServerDemoDstu2.java | 2 +- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 2 +- examples/src/main/java/example/Narrative.java | 2 +- .../main/java/example/NarrativeGenerator.java | 6 +- .../main/java/example/ValidateDirectory.java | 2 +- .../main/java/example/ValidatorExamples.java | 1 + .../java/example/ValidatorExamplesDstu3.java | 3 +- .../composite/BaseResourceReferenceDt.java | 3 +- .../BaseThymeleafNarrativeGenerator.java | 571 +------------ .../CustomThymeleafNarrativeGenerator.java | 4 +- .../DefaultThymeleafNarrativeGenerator.java | 6 + .../fhir/narrative/INarrativeGenerator.java | 13 +- .../narrative2/BaseNarrativeGenerator.java | 221 +++++ .../fhir/narrative2/INarrativeTemplate.java | 42 + .../INarrativeTemplateManifest.java | 33 + .../fhir/narrative2/NarrativeTemplate.java | 121 +++ .../narrative2/NarrativeTemplateManifest.java | 239 ++++++ .../narrative2/NullNarrativeGenerator.java | 31 + .../uhn/fhir/narrative2/TemplateTypeEnum.java | 28 + .../ThymeleafNarrativeGenerator.java | 195 +++++ .../java/ca/uhn/fhir/parser/JsonParser.java | 2 +- .../java/ca/uhn/fhir/parser/XmlParser.java | 2 +- .../instance/model/api/IBaseReference.java | 2 +- .../narratives-hapiserver.properties | 4 +- .../uhn/fhir/narrative/narratives.properties | 38 +- .../ca/uhn/fhir/cli/ExampleDataUploader.java | 2 +- .../java/ca/uhn/fhir/cli/ValidateCommand.java | 2 +- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 2 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 25 +- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 13 +- .../jpa/term/VersionIndependentConcept.java | 4 +- .../JpaValidationSupportChainDstu3.java | 2 +- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 25 + .../BaseResourceProviderDstu2Test.java | 2 +- .../dstu3/BaseResourceProviderDstu3Test.java | 2 +- .../r4/BaseResourceProviderR4Test.java | 2 +- .../r4/CompositionDocumentR4Test.java | 47 +- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 2 +- .../uhn/fhir/jpa/demo/JpaServerDemoDstu2.java | 2 +- .../jpa/model/interceptor/api/Pointcut.java | 32 +- .../executor/InterceptorService.java | 42 +- .../executor/InterceptorServiceTest.java | 27 + .../fhir/jpa/searchparam/IndexStressTest.java | 2 +- .../SearchParamExtractorDstu3Test.java | 2 +- .../config/SubscriptionDstu3Config.java | 2 +- .../matcher/InMemorySubscriptionMatcher.java | 6 +- .../ca/uhn/fhirtest/TestRestfulServer.java | 2 +- .../fhir/dstu2016may/model/BaseReference.java | 9 +- .../fhir/dstu2016may/model/BaseResource.java | 3 +- .../hl7/fhir/dstu2016may/model/Reference.java | 643 +++++++-------- .../hl7/fhir/dstu2016may/model/Resource.java | 765 ++++++++--------- .../fhir/parser/JsonParserDstu2_1Test.java | 2 +- .../uhn/fhir/parser/XmlParserDstu2_1Test.java | 2 +- ...mThymeleafNarrativeGeneratorDstu2Test.java | 8 +- ...tThymeleafNarrativeGeneratorDstu2Test.java | 81 +- .../rest/server/BundleTypeInResponseTest.java | 2 +- .../narrative/PractitionerDstu2.html | 2 +- .../customnarrative_dstu2.properties | 7 +- .../ctx}/DefaultProfileValidationSupport.java | 3 +- .../hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java | 2 +- .../hl7/fhir/dstu3/model/BaseReference.java | 5 +- .../org/hl7/fhir/dstu3/model/Reference.java | 779 +++++++++--------- ...tThymeleafNarrativeGeneratorDstu3Test.java | 56 +- .../ThymeleafNarrativeGeneratorTest.java | 85 ++ .../uhn/fhir/parser/JsonParserDstu3Test.java | 2 +- .../uhn/fhir/parser/XmlParserDstu3Test.java | 2 +- .../resources/narrative2/composition.html | 12 + .../narrative2/diagnosticreport.html | 61 ++ .../resources/narrative2/dt_datetime.html | 3 + .../resources/narrative2/dt_quantity.html | 3 + .../narrative2/narratives.properties | 24 + .../test/resources/testnarrative.properties | 2 +- .../fhir/instance/model/BaseReference.java | 4 +- .../BundleTypeInResponseHl7OrgTest.java | 2 +- .../org/hl7/fhir/r4/model/BaseReference.java | 5 +- .../java/org/hl7/fhir/r4/model/Reference.java | 3 +- .../fhir/rest/param/DateRangeParamR4Test.java | 2 +- .../server/SearchSearchServerDstu1Test.java | 2 +- .../validation/FhirInstanceValidator.java | 1 + .../ParserWithValidationDstu3Test.java | 2 +- .../dstu3/elementmodel/PropertyDstu3Test.java | 2 +- .../DefaultProfileValidationSupportTest.java | 2 +- .../FhirInstanceValidatorDstu3Test.java | 2 +- ...estionnaireResponseValidatorDstu3Test.java | 1 + .../QuestionnaireValidatorDstu3Test.java | 1 + .../ResourceValidatorDstu3Test.java | 1 + .../hapi/validation/StructureMapTest.java | 1 + .../fhir/dstu3/utils/FhirPathEngineTest.java | 2 +- .../servlet/ExampleRestfulServlet.java | 4 +- 90 files changed, 2555 insertions(+), 1867 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplate.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplate.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NullNarrativeGenerator.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/TemplateTypeEnum.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGenerator.java rename {hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation => hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx}/DefaultProfileValidationSupport.java (99%) create mode 100644 hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/narrative2/composition.html create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/narrative2/diagnosticreport.html create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_datetime.html create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_quantity.html create mode 100644 hapi-fhir-structures-dstu3/src/test/resources/narrative2/narratives.properties diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index b6c86dc655b..bbd4d228ed8 100644 --- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -108,7 +108,7 @@ public class JpaServerDemo extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to JSON and pretty printing diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java index 3c802e6cb10..0d4121947d9 100644 --- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java +++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java @@ -108,7 +108,7 @@ public class JpaServerDemoDstu2 extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to JSON and pretty printing diff --git a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 93ae03951b4..06cfed3e03a 100644 --- a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -75,7 +75,7 @@ public class JpaServerDemo extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to JSON and pretty printing diff --git a/examples/src/main/java/example/Narrative.java b/examples/src/main/java/example/Narrative.java index d43ad1c976c..4e5acefb25c 100644 --- a/examples/src/main/java/example/Narrative.java +++ b/examples/src/main/java/example/Narrative.java @@ -21,7 +21,7 @@ patient.addAddress().addLine("742 Evergreen Terrace").setCity("Springfield").set FhirContext ctx = FhirContext.forDstu2(); // Use the narrative generator -ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); +ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ctx)); // Encode the output, including the narrative String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient); diff --git a/examples/src/main/java/example/NarrativeGenerator.java b/examples/src/main/java/example/NarrativeGenerator.java index 8cff9bc6df1..c0ad5d3ab28 100644 --- a/examples/src/main/java/example/NarrativeGenerator.java +++ b/examples/src/main/java/example/NarrativeGenerator.java @@ -10,10 +10,10 @@ public class NarrativeGenerator { public void testGenerator() throws IOException { //START SNIPPET: gen -String propFile = "classpath:/com/foo/customnarrative.properties"; -CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(propFile); - FhirContext ctx = FhirContext.forDstu2(); +String propFile = "classpath:/com/foo/customnarrative.properties"; +CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(ctx, propFile); + ctx.setNarrativeGenerator(gen); //END SNIPPET: gen diff --git a/examples/src/main/java/example/ValidateDirectory.java b/examples/src/main/java/example/ValidateDirectory.java index 34f6b367dc8..4c6ea0986ad 100644 --- a/examples/src/main/java/example/ValidateDirectory.java +++ b/examples/src/main/java/example/ValidateDirectory.java @@ -8,7 +8,7 @@ import java.util.HashMap; import java.util.Map; 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.PrePopulatedValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; diff --git a/examples/src/main/java/example/ValidatorExamples.java b/examples/src/main/java/example/ValidatorExamples.java index 17f363910c6..79c5f428799 100644 --- a/examples/src/main/java/example/ValidatorExamples.java +++ b/examples/src/main/java/example/ValidatorExamples.java @@ -8,6 +8,7 @@ import javax.servlet.ServletException; import org.apache.commons.io.IOUtils; 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.validation.*; import org.hl7.fhir.dstu3.model.*; diff --git a/examples/src/main/java/example/ValidatorExamplesDstu3.java b/examples/src/main/java/example/ValidatorExamplesDstu3.java index c86012e5445..87a13cbdfbe 100644 --- a/examples/src/main/java/example/ValidatorExamplesDstu3.java +++ b/examples/src/main/java/example/ValidatorExamplesDstu3.java @@ -1,8 +1,7 @@ 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.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import ca.uhn.fhir.context.FhirContext; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java index 7c15c55c8b8..841af6c30ea 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java @@ -128,8 +128,9 @@ public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement im } @Override - public void setResource(IBaseResource theResource) { + public BaseResourceReferenceDt setResource(IBaseResource theResource) { myResource = theResource; + return this; } @Override diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java index fb006bf3075..bbaaabd74dd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java @@ -19,588 +19,53 @@ package ca.uhn.fhir.narrative; * limitations under the License. * #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.model.api.IDatatype; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.narrative2.NarrativeTemplateManifest; +import ca.uhn.fhir.narrative2.ThymeleafNarrativeGenerator; +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 HashMap, String> myClassToName; - private boolean myCleanWhitespace = true; - private boolean myIgnoreFailures = true; - private boolean myIgnoreMissingTemplates = true; - private volatile boolean myInitialized; - private HashMap myNameToNarrativeTemplate; - private TemplateEngine myProfileTemplateEngine; - - private IMessageResolver resolver; + private boolean myInitialized; /** * Constructor */ - public BaseThymeleafNarrativeGenerator() { - super(); + public BaseThymeleafNarrativeGenerator(FhirContext theFhirContext) { + super(theFhirContext); } @Override - public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) { + public boolean populateResourceNarrative(IBaseResource theResource) { if (!myInitialized) { - initialize(theContext); + initialize(); } - - String name = myClassToName.get(theResource.getClass()); - 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("
No narrative available - Error: " + e.getMessage() + "
"); - } catch (Exception e1) { - // last resort.. - } - theNarrative.setStatusAsString("empty"); - return; - } - throw new DataFormatException(e); - } + super.populateResourceNarrative(theResource); + return false; } protected abstract List getPropertyFile(); - private synchronized void initialize(final FhirContext theContext) { + private synchronized void initialize() { if (myInitialized) { return; } - ourLog.info("Initializing narrative generator"); - - myClassToName = new HashMap, String>(); - myNameToNarrativeTemplate = new HashMap(); - List propFileName = getPropertyFile(); - try { - if (myApplyDefaultDatatypeTemplates) { - loadProperties(DefaultThymeleafNarrativeGenerator.NARRATIVES_PROPERTIES); - } - for (String next : propFileName) { - loadProperties(next); - } + NarrativeTemplateManifest manifest = NarrativeTemplateManifest.forManifestFileLocation(getFhirContext(), propFileName); + setManifest(manifest); } catch (IOException e) { - ourLog.info("Failed to load property file " + propFileName, 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 getProcessors(String theDialectPrefix) { - Set retVal = super.getProcessors(theDialectPrefix); - retVal.add(new NarrativeAttributeProcessor(theContext, theDialectPrefix)); - return retVal; - } - - }; - myProfileTemplateEngine.setDialect(dialect); - if (this.resolver != null) { - myProfileTemplateEngine.setMessageResolver(this.resolver); - } + throw new InternalErrorException(e); } myInitialized = true; } - public void setMessageResolver(IMessageResolver resolver) { - this.resolver = resolver; - if (myProfileTemplateEngine != null && resolver != null) { - myProfileTemplateEngine.setMessageResolver(resolver); - } - } - - /** - * If set to true (which is the default), most whitespace will be trimmed from the generated narrative - * before it is returned. - *

- * 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. - *

- */ - public boolean isCleanWhitespace() { - return myCleanWhitespace; - } - - /** - * If set to true, 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 true (which is the default), most whitespace will be trimmed from the generated narrative - * before it is returned. - *

- * 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. - *

- */ - public void setCleanWhitespace(boolean theCleanWhitespace) { - myCleanWhitespace = theCleanWhitespace; - } - - /** - * If set to true, 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 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) 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 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) 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 theTemplateResolutionAttributes) { - String template = myNameToNarrativeTemplate.get(theTemplate); - return template != null; - } - - @Override - protected TemplateMode computeTemplateMode(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map theTemplateResolutionAttributes) { - return TemplateMode.XML; - } - - @Override - protected ITemplateResource computeTemplateResource(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map theTemplateResolutionAttributes) { - String template = myNameToNarrativeTemplate.get(theTemplate); - return new StringTemplateResource(template); - } - - @Override - protected ICacheEntryValidity computeValidity(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map theTemplateResolutionAttributes) { - return AlwaysValidCacheEntryValidity.INSTANCE; - } - - } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGenerator.java index e3e75215abe..7e950c5601e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGenerator.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.narrative; import java.util.Arrays; import java.util.List; +import ca.uhn.fhir.context.FhirContext; import org.apache.commons.lang3.Validate; public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator { @@ -39,7 +40,8 @@ public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGen *
  • classpath:/com/package/file.properties
  • * */ - public CustomThymeleafNarrativeGenerator(String... thePropertyFile) { + public CustomThymeleafNarrativeGenerator(FhirContext theFhirContext, String... thePropertyFile) { + super(theFhirContext); setPropertyFile(thePropertyFile); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGenerator.java index e814e4aa32c..e384bfaff88 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGenerator.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.narrative; * #L% */ +import ca.uhn.fhir.context.FhirContext; + import java.util.ArrayList; import java.util.List; @@ -30,6 +32,10 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe private boolean myUseHapiServerConformanceNarrative; + public DefaultThymeleafNarrativeGenerator(FhirContext theFhirContext) { + super(theFhirContext); + } + @Override protected List getPropertyFile() { List retVal = new ArrayList(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/INarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/INarrativeGenerator.java index 5c30287bdc1..35c7dc1397b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/INarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/INarrativeGenerator.java @@ -21,12 +21,17 @@ package ca.uhn.fhir.narrative; */ 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 { - 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 Resource.text.narrative field, but for some resource types + * it can mean other fields (e.g. Composition. + * + * @return Returns true if a narrative was actually generated + */ + boolean populateResourceNarrative(IBaseResource theResource); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java new file mode 100644 index 00000000000..38941e3a88b --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java @@ -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 templateOpt = getTemplateForElement(theResource); + if (templateOpt.isPresent()) { + return applyTemplate(templateOpt.get(), theResource); + } else { + return false; + } + } + + private Optional 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 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 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 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 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(); + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplate.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplate.java new file mode 100644 index 00000000000..0f37c85435b --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplate.java @@ -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 getAppliesToProfiles(); + + Set getAppliesToResourceTypes(); + + Set> getAppliesToResourceClasses(); + + TemplateTypeEnum getTemplateType(); + + String getTemplateName(); + + String getTemplateText(); +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java new file mode 100644 index 00000000000..15b28d8cd2e --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java @@ -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 getTemplateByResourceName(TemplateTypeEnum theStyle, String theResourceName); + + Optional getTemplateByName(TemplateTypeEnum theStyle, String theName); + + Optional getTemplateByElement(TemplateTypeEnum theStyle, IBase theElementValue); +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplate.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplate.java new file mode 100644 index 00000000000..3d16becb484 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplate.java @@ -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 myAppliesToProfiles = new HashSet<>(); + private Set myAppliesToResourceTypes = new HashSet<>(); + private Set myAppliesToDataTypes = new HashSet<>(); + private Set> myAppliesToResourceClasses = new HashSet<>(); + private TemplateTypeEnum myTemplateType = TemplateTypeEnum.THYMELEAF; + private String myContextPath; + private String myTemplateName; + + public Set 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 getAppliesToProfiles() { + return Collections.unmodifiableSet(myAppliesToProfiles); + } + + void addAppliesToProfile(String theAppliesToProfile) { + myAppliesToProfiles.add(theAppliesToProfile); + } + + @Override + public Set getAppliesToResourceTypes() { + return Collections.unmodifiableSet(myAppliesToResourceTypes); + } + + void addAppliesToResourceType(String theAppliesToResourceType) { + myAppliesToResourceTypes.add(theAppliesToResourceType); + } + + @Override + public Set> getAppliesToResourceClasses() { + return Collections.unmodifiableSet(myAppliesToResourceClasses); + } + + void addAppliesToResourceClass(Class 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); + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java new file mode 100644 index 00000000000..3e5a7a4ae51 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java @@ -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> myStyleToResourceTypeToTemplate; + private final Map> myStyleToDatatypeToTemplate; + private final Map> myStyleToNameToTemplate; + private final FhirContext myCtx; + private final int myTemplateCount; + + private NarrativeTemplateManifest(FhirContext theFhirContext, Collection theTemplates) { + myCtx = theFhirContext; + Map> styleToResourceTypeToTemplate = new HashMap<>(); + Map> styleToDatatypeToTemplate = new HashMap<>(); + Map> styleToNameToTemplate = new HashMap<>(); + + for (NarrativeTemplate nextTemplate : theTemplates) { + Map resourceTypeToTemplate = styleToResourceTypeToTemplate.computeIfAbsent(nextTemplate.getTemplateType(), t -> new HashMap<>()); + Map datatypeToTemplate = styleToDatatypeToTemplate.computeIfAbsent(nextTemplate.getTemplateType(), t -> new HashMap<>()); + Map 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 getTemplateByResourceName(TemplateTypeEnum theStyle, String theResourceName) { + return getFromMap(theStyle, theResourceName.toUpperCase(), myStyleToResourceTypeToTemplate); + } + + @Override + public Optional getTemplateByName(TemplateTypeEnum theStyle, String theName) { + return getFromMap(theStyle, theName, myStyleToNameToTemplate); + } + + @Override + public Optional 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 thePropertyFilePaths) throws IOException { + ourLog.debug("Loading narrative properties file(s): {}", thePropertyFilePaths); + + List 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 theResources) throws IOException { + List templates = new ArrayList<>(); + for (String next : theResources) { + templates.addAll(loadProperties(next)); + } + return new NarrativeTemplateManifest(theFhirContext, templates); + } + + private static Collection loadProperties(String theManifestText) throws IOException { + Map 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 clazz; + try { + clazz = (Class) 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 Optional getFromMap(TemplateTypeEnum theStyle, T theResourceName, Map> theMap) { + NarrativeTemplate retVal = null; + Map resourceTypeToTemplate = theMap.get(theStyle); + if (resourceTypeToTemplate != null) { + retVal = resourceTypeToTemplate.get(theResourceName); + } + return Optional.ofNullable(retVal); + } + + private static Map> makeImmutable(Map> theStyleToResourceTypeToTemplate) { + theStyleToResourceTypeToTemplate.replaceAll((key, value) -> Collections.unmodifiableMap(value)); + return Collections.unmodifiableMap(theStyleToResourceTypeToTemplate); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NullNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NullNarrativeGenerator.java new file mode 100644 index 00000000000..ed3f3ef1c72 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NullNarrativeGenerator.java @@ -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; + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/TemplateTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/TemplateTypeEnum.java new file mode 100644 index 00000000000..89d4305cf84 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/TemplateTypeEnum.java @@ -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 + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGenerator.java new file mode 100644 index 00000000000..bfffff0f85e --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGenerator.java @@ -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 getProcessors(String theDialectPrefix) { + Set 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 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 theTemplateResolutionAttributes) { + return getManifest().getTemplateByName(getStyle(), theTemplate).isPresent(); + } + + @Override + protected TemplateMode computeTemplateMode(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map theTemplateResolutionAttributes) { + return TemplateMode.XML; + } + + @Override + protected ITemplateResource computeTemplateResource(IEngineConfiguration theConfiguration, String theOwnerTemplate, String theTemplate, Map 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 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 + * + */ + 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); + } + + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 9ff73b238ae..596e2105701 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -360,7 +360,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { narr = null; } if (narr != null && narr.isEmpty()) { - gen.generateNarrative(myContext, theResource, narr); + gen.populateResourceNarrative(theResource); if (!narr.isEmpty()) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; String childName = nextChild.getChildNameByDatatype(child.getDatatype()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 88327cecede..5995bcd490d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -371,7 +371,7 @@ public class XmlParser extends BaseParser /* implements IParser */ { } // FIXME potential null access on narr see line 623 if (gen != null && narr.isEmpty()) { - gen.generateNarrative(myContext, theResource, narr); + gen.populateResourceNarrative(theResource); } if (narr != null && narr.isEmpty() == false) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseReference.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseReference.java index a62ef55e22f..1d8f4ecc7ad 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseReference.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseReference.java @@ -25,7 +25,7 @@ public interface IBaseReference extends ICompositeType { IBaseResource getResource(); - void setResource(IBaseResource theResource); + IBaseReference setResource(IBaseResource theResource); IIdType getReferenceElement(); diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives-hapiserver.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives-hapiserver.properties index 8569d7ebdcc..d3822d3bd6e 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives-hapiserver.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives-hapiserver.properties @@ -3,6 +3,6 @@ # Resources ################################################ -conformance.class=ca.uhn.fhir.model.dstu.resource.Conformance -conformance.narrative=classpath:ca/uhn/fhir/narrative/ConformanceHapiServer.html +conformance.resourceType =Conformance +conformance.narrative =classpath:ca/uhn/fhir/narrative/ConformanceHapiServer.html diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives.properties index 58e3c0464b7..c74fc54fa32 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/narratives.properties @@ -3,66 +3,62 @@ # Primitive Datatypes ################################################ -code.class=ca.uhn.fhir.model.primitive.CodeDt +code.dataType=code 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 -# Instant uses DateTime narrative -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.dataType=string string.narrative=classpath:ca/uhn/fhir/narrative/datatype/StringDt.html ################################################ # Composite Datatypes ################################################ -address.class=ca.uhn.fhir.model.dstu.composite.AddressDt +address.dataType=Address 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 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 -identifier.class=ca.uhn.fhir.model.dstu.composite.IdentifierDt +identifier.dataType=Identifier 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 -quantity.class=ca.uhn.fhir.model.dstu.composite.QuantityDt +quantity.dataType=Quantity 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 ################################################ # Resources ################################################ -diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport +diagnosticreport.resourceType=DiagnosticReport 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 -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 -medicationprescription.class=ca.uhn.fhir.model.dstu.resource.MedicationPrescription +medicationprescription.resourceType=MedicationPrescription 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.title=classpath:ca/uhn/fhir/narrative/Medication.html diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java index 01a60cd2cfb..e928cc0e425 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java @@ -44,7 +44,7 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; 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.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleType; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java index ff4dcbb26d2..f210de305d2 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ValidateCommand.java @@ -35,7 +35,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.text.WordUtils; import org.fusesource.jansi.Ansi.Color; 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.ValidationSupportChain; import org.hl7.fhir.dstu3.model.StructureDefinition; diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index c2bff0b1cfd..ada28037c90 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -125,7 +125,7 @@ public class JpaServerDemo extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to XML and pretty printing diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 37646e2add4..3b481f7f7f8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -91,6 +91,8 @@ public abstract class BaseHapiFhirResourceDao extends B private String myResourceName; private Class myResourceType; private String mySecondaryPrimaryKeyParamName; + @Autowired + private IResourceReindexingSvc myResourceReindexingSvc; @Override public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { @@ -572,10 +574,10 @@ public abstract class BaseHapiFhirResourceDao extends B TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); - BaseHasResource entity = txTemplate.execute(t->readEntity(theId)); + BaseHasResource entity = txTemplate.execute(t -> readEntity(theId)); if (theId.hasVersionIdPart()) { BaseHasResource currentVersion; - currentVersion = txTemplate.execute(t->readEntity(theId.toVersionless())); + currentVersion = txTemplate.execute(t -> readEntity(theId.toVersionless())); 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"); } @@ -682,7 +684,7 @@ public abstract class BaseHapiFhirResourceDao extends B TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - txTemplate.execute(t->{ + txTemplate.execute(t -> { myResourceReindexingSvc.markAllResourcesForReindexing(resourceType); return null; }); @@ -694,9 +696,6 @@ public abstract class BaseHapiFhirResourceDao extends B mySearchParamRegistry.requestRefresh(); } - @Autowired - private IResourceReindexingSvc myResourceReindexingSvc; - @Override public MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) { // Notify interceptors @@ -722,7 +721,7 @@ public abstract class BaseHapiFhirResourceDao extends B 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") MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails); @@ -756,7 +755,7 @@ public abstract class BaseHapiFhirResourceDao extends B 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") MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails); @@ -918,7 +917,7 @@ public abstract class BaseHapiFhirResourceDao extends B return read(theId, theRequestDetails, false); } - @Override + @Override public T read(IIdType theId, RequestDetails theRequestDetails, boolean theDeletedOk) { validateResourceTypeAndThrowIllegalArgumentException(theId); @@ -941,6 +940,9 @@ public abstract class BaseHapiFhirResourceDao 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()); return retVal; @@ -1189,6 +1191,11 @@ public abstract class BaseHapiFhirResourceDao extends B outcome.setId(id); outcome.setResource(theResource); 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; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 968a990cb81..e65d9a4fea8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * 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. @@ -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.entity.ResourceSearchView; 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.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; @@ -137,6 +140,8 @@ public class SearchBuilder implements ISearchBuilder { private MatchUrlService myMatchUrlService; @Autowired private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao; + @Autowired + private IInterceptorBroadcaster myInterceptorBroadcaster; private List myAlsoIncludePids; private CriteriaBuilder myBuilder; 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); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java index 578fb2d03de..2f5c683b935 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * 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. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java index 5deefb44163..e8170a4732f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java @@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.validation; import javax.annotation.PostConstruct; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 9be5602552c..7c6ec741fb9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -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 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 public void testSearchTokenParam() { Patient patient = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java index 3e4e064616e..5d2c2e612a0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java @@ -74,7 +74,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test { ourRestServer.setResourceProviders((List)myResourceProviders); - ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx)); ourRestServer.setPlainProviders(mySystemProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java index f897c237199..751c0cab18c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java @@ -92,7 +92,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { ourRestServer.setResourceProviders((List) myResourceProviders); - ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx)); myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index b08bafd86a8..9d6f92504f9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -99,7 +99,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { ourRestServer.setResourceProviders((List) myResourceProviders); - ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(myFhirCtx)); myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class); ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java index 15278e50b31..0efacbe93a6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/CompositionDocumentR4Test.java @@ -1,33 +1,39 @@ package ca.uhn.fhir.jpa.provider.r4; 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.rest.api.EncodingEnum; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; 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.HttpGet; - +import org.hl7.fhir.instance.model.api.IBaseResource; 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.AfterClass; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { @@ -38,6 +44,8 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { private String encId; private String listId; private String compId; + @Captor + private ArgumentCaptor myHookParamsCaptor; @Before public void beforeDisableResultReuse() { @@ -49,6 +57,7 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { public void after() throws Exception { super.after(); + myInterceptorRegistry.clearAnonymousHookForUnitTest(); myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis()); } @@ -77,7 +86,7 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { ListResource listResource = new ListResource(); ArrayList myObs = new ArrayList<>(); - myObsIds = new ArrayList(); + myObsIds = new ArrayList<>(); for (int i = 0; i < 5; i++) { Observation obs = new Observation(); obs.getSubject().setReference(patId); @@ -125,14 +134,38 @@ public class CompositionDocumentR4Test extends BaseResourceProviderR4Test { 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 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; HttpGet get = new HttpGet(theUrl); try (CloseableHttpResponse resp = ourHttpClient.execute(get)) { String resourceString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); bundle = theEncoding.newParser(myFhirCtx).parseResource(Bundle.class, resourceString); - } + } return bundle; } diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 38506ddd976..3c4643f0bb7 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -109,7 +109,7 @@ public class JpaServerDemo extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to JSON and pretty printing diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java index 3c802e6cb10..0d4121947d9 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemoDstu2.java @@ -108,7 +108,7 @@ public class JpaServerDemoDstu2 extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * Default to JSON and pretty printing diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Pointcut.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Pointcut.java index eab60633a37..45c05f6a8a3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Pointcut.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/api/Pointcut.java @@ -29,6 +29,11 @@ import java.util.List; */ 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 * subscription processing pipeline. This method is called before the resource is placed @@ -330,7 +335,32 @@ public enum Pointcut { * Hooks should return void. *

    */ - 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. + *

    + * 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. + *

    + *

    + * 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. + *

    + * Hooks may accept the following parameters: + *
      + *
    • org.hl7.fhir.instance.model.api.IBaseResource (the resource being returned)
    • + *
    + *

    + * Hooks should return void. + *

    + */ + RESOURCE_MAY_BE_RETURNED("org.hl7.fhir.instance.model.api.IBaseResource"); private final List myParameterTypes; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java index 26e2831d361..d62f0a25417 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorService.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import com.google.common.collect.Sets; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -92,11 +93,26 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro Class interceptorClass = theInterceptor.getClass(); int typeOrder = determineOrder(interceptorClass); - if (!scanInterceptorForHookMethodsAndAddThem(theInterceptor, typeOrder)) { + List addedInvokers = scanInterceptorForHookMethods(theInterceptor, typeOrder); + if (addedInvokers.isEmpty()) { 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); + 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 // @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 scanInterceptorForHookMethods(Object theInterceptor, int theTypeOrder) { + ArrayList retVal = new ArrayList<>(); for (Method nextMethod : theInterceptor.getClass().getDeclaredMethods()) { Hook hook = AnnotationUtils.findAnnotation(nextMethod, Hook.class); if (hook != null) { - int methodOrder = theTypeOrder; Order methodOrderAnnotation = AnnotationUtils.findAnnotation(nextMethod, Order.class); if (methodOrderAnnotation != null) { methodOrder = methodOrderAnnotation.value(); } - HookInvoker invoker = new HookInvoker(hook, theInterceptor, nextMethod, methodOrder); - for (Pointcut nextPointcut : hook.value()) { - myInvokers.put(nextPointcut, invoker); - } - - retVal = true; + retVal.add(new HookInvoker(hook, theInterceptor, nextMethod, methodOrder)); } } + return retVal; } @@ -300,12 +314,14 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro private final Method myMethod; private final Class[] myParameterTypes; private final int[] myParameterIndexes; + private final Set myPointcuts; /** * Constructor */ private HookInvoker(Hook theHook, @Nonnull Object theInterceptor, @Nonnull Method theHookMethod, int theOrder) { super(theInterceptor, theOrder); + myPointcuts = Collections.unmodifiableSet(Sets.newHashSet(theHook.value())); myParameterTypes = theHookMethod.getParameterTypes(); myMethod = theHookMethod; @@ -325,6 +341,10 @@ public class InterceptorService implements IInterceptorRegistry, IInterceptorBro } } + public Set getPointcuts() { + return myPointcuts; + } + /** * @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) { throw ((RuntimeException) targetException); } else { - throw new InternalErrorException(targetException); + throw new InternalErrorException("Failure invoking interceptor for pointcut(s) " + getPointcuts(), targetException); } } catch (Exception e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java index eede79c7d30..d7878b0582d 100644 --- a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java +++ b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/interceptor/executor/InterceptorServiceTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.model.interceptor.executor; 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.r4.model.Patient; import org.junit.Before; @@ -40,6 +41,21 @@ public class InterceptorServiceTest { @Autowired 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 public void testGlobalInterceptorsAreFound() { List globalInterceptors = myInterceptorRegistry.getGlobalInterceptorsForUnitTest(); @@ -265,4 +281,15 @@ public class InterceptorServiceTest { */ private static class ResourceDeliveryMessage { } + + @Interceptor(manualRegistration = true) + public static class InterceptorThatFailsOnRegister { + + @Hook(Pointcut.REGISTERED) + public void start() throws Exception { + throw new Exception("InterceptorThatFailsOnRegister FAILED!"); + } + + } + } diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/IndexStressTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/IndexStressTest.java index ebb536ab08a..c8edbc977a9 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/IndexStressTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/IndexStressTest.java @@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.util.StopWatch; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; 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.model.Patient; import org.junit.Test; diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/SearchParamExtractorDstu3Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/SearchParamExtractorDstu3Test.java index 223d0b029e5..5e74873d84a 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/SearchParamExtractorDstu3Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/SearchParamExtractorDstu3Test.java @@ -11,7 +11,7 @@ import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.util.TestUtil; 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.junit.AfterClass; import org.junit.BeforeClass; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/SubscriptionDstu3Config.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/SubscriptionDstu3Config.java index 984056d7942..c621a4eac61 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/SubscriptionDstu3Config.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/config/SubscriptionDstu3Config.java @@ -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.SearchParamRegistryDstu3; 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.context.annotation.Bean; import org.springframework.context.annotation.Primary; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/matcher/InMemorySubscriptionMatcher.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/matcher/InMemorySubscriptionMatcher.java index 6418495dcf0..13ee5cbbaab 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/matcher/InMemorySubscriptionMatcher.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/matcher/InMemorySubscriptionMatcher.java @@ -29,9 +29,12 @@ import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.module.ResourceModifiedMessage; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class InMemorySubscriptionMatcher implements ISubscriptionMatcher { + private static final Logger ourLog = LoggerFactory.getLogger(InMemorySubscriptionMatcher.class); @Autowired private FhirContext myContext; @@ -49,7 +52,8 @@ public class InMemorySubscriptionMatcher implements ISubscriptionMatcher { try { return match(theSubscription.getCriteriaString(), theMsg.getNewPayload(myContext)); } 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); } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 392e491e9fc..8a1f91ccc09 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -159,7 +159,7 @@ public class TestRestfulServer extends RestfulServer { * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); - ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(getFhirContext())); /* * The resource and system providers (which actually implement the various FHIR diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseReference.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseReference.java index 42c8869c99e..5fbebfa84d8 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseReference.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseReference.java @@ -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 * may be changed/accessed by parsers. */ - public IBaseResource getResource() { + @Override + public IBaseResource getResource() { 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 * may be changed/accessed by parsers. */ - public void setResource(IBaseResource theResource) { + @Override + public BaseReference setResource(IBaseResource theResource) { resource = theResource; - } + return null; + } @Override public boolean isEmpty() { diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseResource.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseResource.java index acca64c20ac..a132c8e689a 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseResource.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/BaseResource.java @@ -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. */ - public BaseResource setId(IIdType value) { + @Override + public BaseResource setId(IIdType value) { if (value == null) { setIdElement((IdType)null); } else if (value instanceof IdType) { diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Reference.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Reference.java index d41b39eb232..b60e6ebefd6 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Reference.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Reference.java @@ -1,321 +1,322 @@ -package org.hl7.fhir.dstu2016may.model; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * 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 - prior written permission. - - 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 - 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, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - 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, - 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 - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 -import java.util.List; - -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseReference; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.utilities.Utilities; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.model.api.annotation.Description; -/** - * A reference from one resource to another. - */ -@DatatypeDef(name="Reference") -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. - */ - @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." ) - protected StringType 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) - @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) - protected StringType display; - - private static final long serialVersionUID = 22777321L; - - /** - * Constructor - */ - public Reference() { - super(); - } - - /** - * Constructor - * - * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") - */ - public Reference(String theReference) { - super(theReference); - } - - /** - * Constructor - * - * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") - */ - public Reference(IIdType theReference) { - super(theReference); - } - - /** - * Constructor - * - * @param theResource The resource represented by this reference - */ - public Reference(IAnyResource 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 - */ - public StringType getReferenceElement_() { - if (this.reference == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Reference.reference"); - else if (Configuration.doAutoCreate()) - this.reference = new StringType(); // bb - return this.reference; - } - - public boolean hasReferenceElement() { - return this.reference != null && !this.reference.isEmpty(); - } - - public boolean hasReference() { - 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 - */ - public Reference setReferenceElement(StringType value) { - this.reference = value; - 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. - */ - 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. - */ - public Reference setReference(String value) { - if (Utilities.noString(value)) - this.reference = null; - else { - if (this.reference == null) - this.reference = new StringType(); - this.reference.setValue(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 - */ - public StringType getDisplayElement() { - if (this.display == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Reference.display"); - else if (Configuration.doAutoCreate()) - this.display = new StringType(); // bb - return this.display; - } - - public boolean hasDisplayElement() { - 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 - */ - public Reference setDisplayElement(StringType value) { - this.display = value; - return this; - } - - /** - * @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(); - } - - /** - * @param value Plain text narrative that identifies the resource in addition to the resource reference. - */ - public Reference setDisplay(String value) { - if (Utilities.noString(value)) - this.display = null; - else { - if (this.display == null) - this.display = new StringType(); - this.display.setValue(value); - } - return this; - } - - /** - * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given - * reference. - * - * @param theReference The reference, or null - * @return - * @return Returns a reference to this - */ - public Reference setReferenceElement(IIdType theReference) { - if (theReference != null) { - setReference(theReference.getValue()); - } else { - setReference(null); - } - return this; - } - protected void listChildren(List childrenList) { - super.listChildren(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)); - 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 { - switch (hash) { - 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 - default: return super.getProperty(hash, name, checkValid); - } - - } - - @Override - public void setProperty(int hash, String name, Base value) throws FHIRException { - switch (hash) { - case -925155509: // reference - this.reference = castToString(value); // StringType - break; - case 1671764162: // display - this.display = castToString(value); // StringType - break; - default: super.setProperty(hash, name, value); - } - - } - - @Override - public void setProperty(String name, Base value) throws FHIRException { - if (name.equals("reference")) - this.reference = castToString(value); // StringType - else if (name.equals("display")) - this.display = castToString(value); // StringType - else - super.setProperty(name, value); - } - - @Override - public Base makeProperty(int hash, String name) throws FHIRException { - switch (hash) { - case -925155509: throw new FHIRException("Cannot make property reference as it is not a complex type"); // StringType - 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 { - 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 - return super.addChild(name); - } - - public String fhirType() { - return "Reference"; - - } - - public Reference copy() { - Reference dst = new Reference(); - copyValues(dst); - dst.reference = reference == null ? null : reference.copy(); - dst.display = display == null ? null : display.copy(); - return dst; - } - - protected Reference typedCopy() { - return copy(); - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof Reference)) - return false; - Reference o = (Reference) other; - return compareDeep(reference, o.reference, true) && compareDeep(display, o.display, true); - } - - @Override - public boolean equalsShallow(Base other) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof Reference)) - return false; - 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()) - ; - } - - -} - +package org.hl7.fhir.dstu2016may.model; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * 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 + prior written permission. + + 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 + 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, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + 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, + 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 + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 +import java.util.List; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.utilities.Utilities; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.model.api.annotation.Description; +/** + * A reference from one resource to another. + */ +@DatatypeDef(name="Reference") +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. + */ + @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." ) + protected StringType 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) + @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) + protected StringType display; + + private static final long serialVersionUID = 22777321L; + + /** + * Constructor + */ + public Reference() { + super(); + } + + /** + * Constructor + * + * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") + */ + public Reference(String theReference) { + super(theReference); + } + + /** + * Constructor + * + * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") + */ + public Reference(IIdType theReference) { + super(theReference); + } + + /** + * Constructor + * + * @param theResource The resource represented by this reference + */ + public Reference(IAnyResource 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 + */ + public StringType getReferenceElement_() { + if (this.reference == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Reference.reference"); + else if (Configuration.doAutoCreate()) + this.reference = new StringType(); // bb + return this.reference; + } + + public boolean hasReferenceElement() { + return this.reference != null && !this.reference.isEmpty(); + } + + public boolean hasReference() { + 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 + */ + public Reference setReferenceElement(StringType value) { + this.reference = value; + 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. + */ + @Override + 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. + */ + public Reference setReference(String value) { + if (Utilities.noString(value)) + this.reference = null; + else { + if (this.reference == null) + this.reference = new StringType(); + this.reference.setValue(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 + */ + public StringType getDisplayElement() { + if (this.display == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Reference.display"); + else if (Configuration.doAutoCreate()) + this.display = new StringType(); // bb + return this.display; + } + + public boolean hasDisplayElement() { + 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 + */ + public Reference setDisplayElement(StringType value) { + this.display = value; + return this; + } + + /** + * @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(); + } + + /** + * @param value Plain text narrative that identifies the resource in addition to the resource reference. + */ + public Reference setDisplay(String value) { + if (Utilities.noString(value)) + this.display = null; + else { + if (this.display == null) + this.display = new StringType(); + this.display.setValue(value); + } + return this; + } + + /** + * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given + * reference. + * + * @param theReference The reference, or null + * @return + * @return Returns a reference to this + */ + public Reference setReferenceElement(IIdType theReference) { + if (theReference != null) { + setReference(theReference.getValue()); + } else { + setReference(null); + } + return this; + } + protected void listChildren(List childrenList) { + super.listChildren(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)); + 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 { + switch (hash) { + 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 + default: return super.getProperty(hash, name, checkValid); + } + + } + + @Override + public void setProperty(int hash, String name, Base value) throws FHIRException { + switch (hash) { + case -925155509: // reference + this.reference = castToString(value); // StringType + break; + case 1671764162: // display + this.display = castToString(value); // StringType + break; + default: super.setProperty(hash, name, value); + } + + } + + @Override + public void setProperty(String name, Base value) throws FHIRException { + if (name.equals("reference")) + this.reference = castToString(value); // StringType + else if (name.equals("display")) + this.display = castToString(value); // StringType + else + super.setProperty(name, value); + } + + @Override + public Base makeProperty(int hash, String name) throws FHIRException { + switch (hash) { + case -925155509: throw new FHIRException("Cannot make property reference as it is not a complex type"); // StringType + 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 { + 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 + return super.addChild(name); + } + + public String fhirType() { + return "Reference"; + + } + + public Reference copy() { + Reference dst = new Reference(); + copyValues(dst); + dst.reference = reference == null ? null : reference.copy(); + dst.display = display == null ? null : display.copy(); + return dst; + } + + protected Reference typedCopy() { + return copy(); + } + + @Override + public boolean equalsDeep(Base other) { + if (!super.equalsDeep(other)) + return false; + if (!(other instanceof Reference)) + return false; + Reference o = (Reference) other; + return compareDeep(reference, o.reference, true) && compareDeep(display, o.display, true); + } + + @Override + public boolean equalsShallow(Base other) { + if (!super.equalsShallow(other)) + return false; + if (!(other instanceof Reference)) + return false; + 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()) + ; + } + + +} + diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Resource.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Resource.java index 40b167d37cf..c54fc87fb0b 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Resource.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/model/Resource.java @@ -1,382 +1,383 @@ -package org.hl7.fhir.dstu2016may.model; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * 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 - prior written permission. - - 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 - 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, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - 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, - 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 - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 -import java.util.List; - -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.utilities.Utilities; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.Description; -/** - * This is the base resource type for everything. - */ -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. - */ - @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." ) - 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. - */ - @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." ) - 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. - */ - @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." ) - protected UriType implicitRules; - - /** - * 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) - @Description(shortDefinition="Language of the resource content", formalDefinition="The base language in which the resource is written." ) - protected CodeType language; - - private static final long serialVersionUID = -559462759L; - - /** - * Constructor - */ - public Resource() { - 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 - */ - public IdType getIdElement() { - if (this.id == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Resource.id"); - else if (Configuration.doAutoCreate()) - this.id = new IdType(); // bb - return this.id; - } - - public boolean hasIdElement() { - return this.id != null && !this.id.isEmpty(); - } - - public boolean hasId() { - 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 - */ - public Resource setIdElement(IdType value) { - 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. - */ - 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. - */ - public Resource setId(String value) { - if (Utilities.noString(value)) - this.id = null; - else { - if (this.id == null) - this.id = new IdType(); - this.id.setValue(value); - } - 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.) - */ - public Meta getMeta() { - if (this.meta == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Resource.meta"); - else if (Configuration.doAutoCreate()) - this.meta = new Meta(); // cc - return this.meta; - } - - 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.) - */ - public Resource setMeta(Meta value) { - 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 - */ - public UriType getImplicitRulesElement() { - if (this.implicitRules == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Resource.implicitRules"); - else if (Configuration.doAutoCreate()) - this.implicitRules = new UriType(); // bb - return this.implicitRules; - } - - public boolean hasImplicitRulesElement() { - 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 - */ - public Resource setImplicitRulesElement(UriType value) { - 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. - */ - 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. - */ - public Resource setImplicitRules(String value) { - if (Utilities.noString(value)) - this.implicitRules = null; - else { - if (this.implicitRules == null) - this.implicitRules = new UriType(); - this.implicitRules.setValue(value); - } - 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 - */ - public CodeType getLanguageElement() { - if (this.language == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Resource.language"); - else if (Configuration.doAutoCreate()) - this.language = new CodeType(); // bb - return this.language; - } - - public boolean hasLanguageElement() { - 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 - */ - public Resource setLanguageElement(CodeType value) { - this.language = value; - return this; - } - - /** - * @return The base language in which the resource is written. - */ - public String getLanguage() { - return this.language == null ? null : this.language.getValue(); - } - - /** - * @param value The base language in which the resource is written. - */ - public Resource setLanguage(String value) { - if (Utilities.noString(value)) - this.language = null; - else { - if (this.language == null) - this.language = new CodeType(); - this.language.setValue(value); - } - return this; - } - - protected void listChildren(List 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)); - 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("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 { - switch (hash) { - case 3355: /*id*/ return this.id == null ? new Base[0] : new Base[] {this.id}; // IdType - case 3347973: /*meta*/ return this.meta == null ? new Base[0] : new Base[] {this.meta}; // Meta - case -961826286: /*implicitRules*/ return this.implicitRules == null ? new Base[0] : new Base[] {this.implicitRules}; // UriType - 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 { - switch (hash) { - case 3355: // id - this.id = castToId(value); // IdType - break; - case 3347973: // meta - this.meta = castToMeta(value); // Meta - break; - case -961826286: // implicitRules - this.implicitRules = castToUri(value); // UriType - break; - case -1613589672: // language - this.language = castToCode(value); // CodeType - break; - default: super.setProperty(hash, name, value); - } - - } - - @Override - public void setProperty(String name, Base value) throws FHIRException { - if (name.equals("id")) - this.id = castToId(value); // IdType - else if (name.equals("meta")) - this.meta = castToMeta(value); // Meta - else if (name.equals("implicitRules")) - this.implicitRules = castToUri(value); // UriType - else if (name.equals("language")) - this.language = castToCode(value); // CodeType - else - super.setProperty(name, value); - } - - @Override - public Base makeProperty(int hash, String name) throws FHIRException { - switch (hash) { - case 3355: throw new FHIRException("Cannot make property id as it is not a complex type"); // IdType - case 3347973: return getMeta(); // Meta - case -961826286: throw new FHIRException("Cannot make property implicitRules as it is not a complex type"); // UriType - 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 { - 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(); - return this.meta; - } - 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 - return super.addChild(name); - } - - public String fhirType() { - return "Resource"; - - } - - public abstract Resource copy(); - - public void copyValues(Resource dst) { - dst.id = id == null ? null : id.copy(); - dst.meta = meta == null ? null : meta.copy(); - dst.implicitRules = implicitRules == null ? null : implicitRules.copy(); - dst.language = language == null ? null : language.copy(); - } - - @Override - public boolean equalsDeep(Base other) { - if (!super.equalsDeep(other)) - return false; - if (!(other instanceof Resource)) - return false; - Resource o = (Resource) other; - 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) { - if (!super.equalsShallow(other)) - return false; - if (!(other instanceof Resource)) - return false; - 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()) - && (language == null || language.isEmpty()); - } - - public abstract ResourceType getResourceType(); - -} - +package org.hl7.fhir.dstu2016may.model; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * 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 + prior written permission. + + 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 + 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, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + 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, + 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 + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Sun, May 8, 2016 03:05+1000 for FHIR v1.4.0 +import java.util.List; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.utilities.Utilities; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.Description; +/** + * This is the base resource type for everything. + */ +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. + */ + @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." ) + 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. + */ + @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." ) + 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. + */ + @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." ) + protected UriType implicitRules; + + /** + * 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) + @Description(shortDefinition="Language of the resource content", formalDefinition="The base language in which the resource is written." ) + protected CodeType language; + + private static final long serialVersionUID = -559462759L; + + /** + * Constructor + */ + public Resource() { + 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 + */ + public IdType getIdElement() { + if (this.id == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Resource.id"); + else if (Configuration.doAutoCreate()) + this.id = new IdType(); // bb + return this.id; + } + + public boolean hasIdElement() { + return this.id != null && !this.id.isEmpty(); + } + + public boolean hasId() { + 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 + */ + @Override + public Resource setIdElement(IdType value) { + 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. + */ + 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. + */ + public Resource setId(String value) { + if (Utilities.noString(value)) + this.id = null; + else { + if (this.id == null) + this.id = new IdType(); + this.id.setValue(value); + } + 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.) + */ + public Meta getMeta() { + if (this.meta == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Resource.meta"); + else if (Configuration.doAutoCreate()) + this.meta = new Meta(); // cc + return this.meta; + } + + 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.) + */ + public Resource setMeta(Meta value) { + 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 + */ + public UriType getImplicitRulesElement() { + if (this.implicitRules == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Resource.implicitRules"); + else if (Configuration.doAutoCreate()) + this.implicitRules = new UriType(); // bb + return this.implicitRules; + } + + public boolean hasImplicitRulesElement() { + 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 + */ + public Resource setImplicitRulesElement(UriType value) { + 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. + */ + 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. + */ + public Resource setImplicitRules(String value) { + if (Utilities.noString(value)) + this.implicitRules = null; + else { + if (this.implicitRules == null) + this.implicitRules = new UriType(); + this.implicitRules.setValue(value); + } + 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 + */ + public CodeType getLanguageElement() { + if (this.language == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Resource.language"); + else if (Configuration.doAutoCreate()) + this.language = new CodeType(); // bb + return this.language; + } + + public boolean hasLanguageElement() { + 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 + */ + public Resource setLanguageElement(CodeType value) { + this.language = value; + return this; + } + + /** + * @return The base language in which the resource is written. + */ + public String getLanguage() { + return this.language == null ? null : this.language.getValue(); + } + + /** + * @param value The base language in which the resource is written. + */ + public Resource setLanguage(String value) { + if (Utilities.noString(value)) + this.language = null; + else { + if (this.language == null) + this.language = new CodeType(); + this.language.setValue(value); + } + return this; + } + + protected void listChildren(List 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)); + 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("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 { + switch (hash) { + case 3355: /*id*/ return this.id == null ? new Base[0] : new Base[] {this.id}; // IdType + case 3347973: /*meta*/ return this.meta == null ? new Base[0] : new Base[] {this.meta}; // Meta + case -961826286: /*implicitRules*/ return this.implicitRules == null ? new Base[0] : new Base[] {this.implicitRules}; // UriType + 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 { + switch (hash) { + case 3355: // id + this.id = castToId(value); // IdType + break; + case 3347973: // meta + this.meta = castToMeta(value); // Meta + break; + case -961826286: // implicitRules + this.implicitRules = castToUri(value); // UriType + break; + case -1613589672: // language + this.language = castToCode(value); // CodeType + break; + default: super.setProperty(hash, name, value); + } + + } + + @Override + public void setProperty(String name, Base value) throws FHIRException { + if (name.equals("id")) + this.id = castToId(value); // IdType + else if (name.equals("meta")) + this.meta = castToMeta(value); // Meta + else if (name.equals("implicitRules")) + this.implicitRules = castToUri(value); // UriType + else if (name.equals("language")) + this.language = castToCode(value); // CodeType + else + super.setProperty(name, value); + } + + @Override + public Base makeProperty(int hash, String name) throws FHIRException { + switch (hash) { + case 3355: throw new FHIRException("Cannot make property id as it is not a complex type"); // IdType + case 3347973: return getMeta(); // Meta + case -961826286: throw new FHIRException("Cannot make property implicitRules as it is not a complex type"); // UriType + 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 { + 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(); + return this.meta; + } + 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 + return super.addChild(name); + } + + public String fhirType() { + return "Resource"; + + } + + public abstract Resource copy(); + + public void copyValues(Resource dst) { + dst.id = id == null ? null : id.copy(); + dst.meta = meta == null ? null : meta.copy(); + dst.implicitRules = implicitRules == null ? null : implicitRules.copy(); + dst.language = language == null ? null : language.copy(); + } + + @Override + public boolean equalsDeep(Base other) { + if (!super.equalsDeep(other)) + return false; + if (!(other instanceof Resource)) + return false; + Resource o = (Resource) other; + 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) { + if (!super.equalsShallow(other)) + return false; + if (!(other instanceof Resource)) + return false; + 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()) + && (language == null || language.isEmpty()); + } + + public abstract ResourceType getResourceType(); + +} + diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java index e31cf6be5bb..5d0eb4d5a5b 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java @@ -1020,7 +1020,7 @@ public class JsonParserDstu2_1Test { Patient p = new Patient(); p.addName().addFamily("Smith").addGiven("John"); - ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); String output = ourCtx.newJsonParser().encodeResourceToString(p); ourLog.info(output); diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java index 423d63a141f..056eb96fa7f 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java @@ -1695,7 +1695,7 @@ public class XmlParserDstu2_1Test { Patient p = new Patient(); p.addName().addFamily("Smith").addGiven("John"); - ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); String output = ourCtx.newXmlParser().encodeResourceToString(p); ourLog.info(output); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorDstu2Test.java index 2790e87f1d8..5824972527f 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorDstu2Test.java @@ -7,7 +7,6 @@ import org.junit.AfterClass; import org.junit.Test; 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.util.TestUtil; @@ -27,7 +26,7 @@ public class CustomThymeleafNarrativeGeneratorDstu2Test { public void testGenerator() { // 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); Practitioner p = new Practitioner(); @@ -36,10 +35,9 @@ public class CustomThymeleafNarrativeGeneratorDstu2Test { p.addAddress().addLine("line1").addLine("line2"); p.getName().addFamily("fam1").addGiven("given"); - NarrativeDt narrative = new NarrativeDt(); - gen.generateNarrative(ourCtx, p, narrative); + gen.populateResourceNarrative(p); - String actual = narrative.getDiv().getValueAsString(); + String actual = p.getText().getDiv().getValueAsString(); ourLog.info(actual); assertThat(actual, containsString("

    Name

    given FAM1

    Address

    line1
    line2
    ")); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java index adf15b6d0e5..88509c6417e 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java @@ -17,13 +17,10 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; -import ca.uhn.fhir.model.dstu2.composite.NarrativeDt; -import ca.uhn.fhir.model.dstu2.composite.PeriodDt; import ca.uhn.fhir.model.dstu2.composite.QuantityDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.SimpleQuantityDt; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; -import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.Medication; import ca.uhn.fhir.model.dstu2.resource.MedicationOrder; import ca.uhn.fhir.model.dstu2.resource.Observation; @@ -31,7 +28,6 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.DiagnosticReportStatusEnum; -import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum; import ca.uhn.fhir.model.dstu2.valueset.MedicationOrderStatusEnum; import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; @@ -52,10 +48,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { @Before public void before() { - myGen = new DefaultThymeleafNarrativeGenerator(); + myGen = new DefaultThymeleafNarrativeGenerator(ourCtx); myGen.setUseHapiServerConformanceNarrative(true); - myGen.setIgnoreFailures(false); - myGen.setIgnoreMissingTemplates(false); ourCtx.setNarrativeGenerator(myGen); } @@ -71,9 +65,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY); - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString("
    joe john BLOW
    ")); @@ -81,50 +74,19 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { @Test public void testUnsupportedType() throws DataFormatException { - myGen.setIgnoreMissingTemplates(true); - Parameters value = new Parameters(); value.setId("123"); - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, not(containsString("narrative"))); } - @Test(expected=DataFormatException.class) - public void testUnsupportedTypeDontIgnore() throws DataFormatException { - myGen.setIgnoreMissingTemplates(false); - - Parameters value = new Parameters(); - value.setId("123"); - - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, value, narrative); - } - - @Test - @Ignore - public void testGenerateEncounter() throws DataFormatException { - Encounter enc = new Encounter(); - - enc.addIdentifier().setSystem("urn:visits").setValue("1234567"); - enc.setClassElement(EncounterClassEnum.AMBULATORY); - enc.setPeriod(new PeriodDt().setStart(new DateTimeDt("2001-01-02T11:11:00"))); - - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, enc, narrative); - - assertEquals("", narrative.getDivAsString()); - } - - @Test public void testGenerateOperationOutcome() { - //@formatter:off - String parse = "\n" + + String parse = "\n" + " \n" + " \n" + " \n" + @@ -134,17 +96,11 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { " \n" + " \n" + ""; - //@formatter:on OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, parse); - // String output = gen.generateTitle(oo); - // ourLog.info(output); - // assertEquals("Operation Outcome (2 issues)", output); - - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, oo, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(oo); + String output = oo.getText().getDiv().getValueAsString(); ourLog.info(output); @@ -181,9 +137,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { value.addResult().setResource(obs); } - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString("
    Some & Diagnostic Report
    ")); @@ -207,11 +162,11 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { mp.setStatus(MedicationOrderStatusEnum.ACTIVE); mp.setDateWritten(new DateTimeDt("2014-09-01")); - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, mp, narrative); + myGen.populateResourceNarrative(mp); + 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 string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1); + assertTrue("Expected medication name of ciprofloaxin within narrative: " + output, output.indexOf("ciprofloaxin") > -1); + assertTrue("Expected string status of ACTIVE within narrative: " + output, output.indexOf("ACTIVE") > -1); } @@ -220,11 +175,9 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { Medication med = new Medication(); med.getCode().setText("ciproflaxin"); - NarrativeDt narrative = new NarrativeDt(); - myGen.generateNarrative(ourCtx, med, narrative); - - String string = narrative.getDiv().toString(); - assertThat(string, containsString("ciproflaxin")); + myGen.populateResourceNarrative(med); + String output = med.getText().getDiv().getValueAsString(); + assertThat(output, containsString("ciproflaxin")); } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java index f5e8c9ed6dc..139a6209183 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java @@ -71,7 +71,7 @@ public class BundleTypeInResponseTest { ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); servlet.setResourceProviders(patientProvider); ServletHolder servletHolder = new ServletHolder(servlet); diff --git a/hapi-fhir-structures-dstu2/src/test/resources/narrative/PractitionerDstu2.html b/hapi-fhir-structures-dstu2/src/test/resources/narrative/PractitionerDstu2.html index ef226534b01..59262bc332c 100644 --- a/hapi-fhir-structures-dstu2/src/test/resources/narrative/PractitionerDstu2.html +++ b/hapi-fhir-structures-dstu2/src/test/resources/narrative/PractitionerDstu2.html @@ -21,4 +21,4 @@
    - \ No newline at end of file + diff --git a/hapi-fhir-structures-dstu2/src/test/resources/narrative/customnarrative_dstu2.properties b/hapi-fhir-structures-dstu2/src/test/resources/narrative/customnarrative_dstu2.properties index 7edb1758dda..a7b5de5a289 100644 --- a/hapi-fhir-structures-dstu2/src/test/resources/narrative/customnarrative_dstu2.properties +++ b/hapi-fhir-structures-dstu2/src/test/resources/narrative/customnarrative_dstu2.properties @@ -8,9 +8,12 @@ # template file. # 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 # 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 + +address.dataType=Address +address.narrative=classpath:ca/uhn/fhir/narrative/datatype/AddressDt.html diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java similarity index 99% rename from hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java rename to hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java index 5bb439aee83..446c10f117f 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java @@ -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 org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; 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.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java index 291d999b79f..6dc6d3b74df 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/FhirDstu3.java @@ -51,7 +51,7 @@ public class FhirDstu3 implements IFhirVersion { @Override public IContextValidationSupport createValidationSupport() { - String className = "org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport"; + String className = "org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport"; try { return (IContextValidationSupport) Class.forName(className).newInstance(); } catch (Exception theE) { diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/BaseReference.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/BaseReference.java index 90aea76bd17..b72a7afaae3 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/BaseReference.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/BaseReference.java @@ -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 * may be changed/accessed by parsers. */ + @Override public IBaseResource getResource() { 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 * may be changed/accessed by parsers. */ - public void setResource(IBaseResource theResource) { + @Override + public BaseReference setResource(IBaseResource theResource) { resource = theResource; + return this; } @Override diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Reference.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Reference.java index f946201085d..37d15b7cae5 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Reference.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/Reference.java @@ -1,389 +1,390 @@ -package org.hl7.fhir.dstu3.model; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * 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 - prior written permission. - - 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 - 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, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - 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, - 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 - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Fri, Mar 16, 2018 15:21+1100 for FHIR v3.0.1 - -import java.util.*; - -import org.hl7.fhir.utilities.Utilities; -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.ChildOrder; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.model.api.annotation.Block; -import org.hl7.fhir.instance.model.api.*; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -/** - * A reference from one resource to another. - */ -@DatatypeDef(name="Reference") -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. - */ - @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." ) - 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. - */ - @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." ) - protected Identifier identifier; - - /** - * 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) - @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) - protected StringType display; - - private static final long serialVersionUID = -909353281L; - - /** - * Constructor - */ - public Reference() { - super(); - } - - /** - * Constructor - * - * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") - */ - public Reference(String theReference) { - super(theReference); - } - - /** - * Constructor - * - * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") - */ - public Reference(IIdType theReference) { - super(theReference); - } - - /** - * Constructor - * - * @param theResource The resource represented by this reference - */ - public Reference(IAnyResource 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 - */ - public StringType getReferenceElement_() { - if (this.reference == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Reference.reference"); - else if (Configuration.doAutoCreate()) - this.reference = new StringType(); // bb - return this.reference; - } - - public boolean hasReferenceElement() { - return this.reference != null && !this.reference.isEmpty(); - } - - public boolean hasReference() { - 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 - */ - public Reference setReferenceElement(StringType value) { - this.reference = value; - 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. - */ - 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. - */ - public Reference setReference(String value) { - if (Utilities.noString(value)) - this.reference = null; - else { - if (this.reference == null) - this.reference = new StringType(); - this.reference.setValue(value); - } - 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.) - */ - public Identifier getIdentifier() { - if (this.identifier == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Reference.identifier"); - else if (Configuration.doAutoCreate()) - this.identifier = new Identifier(); // cc - return this.identifier; - } - - 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.) - */ - public Reference setIdentifier(Identifier value) { - 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 - */ - public StringType getDisplayElement() { - if (this.display == null) - if (Configuration.errorOnAutoCreate()) - throw new Error("Attempt to auto-create Reference.display"); - else if (Configuration.doAutoCreate()) - this.display = new StringType(); // bb - return this.display; - } - - public boolean hasDisplayElement() { - 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 - */ - public Reference setDisplayElement(StringType value) { - this.display = value; - return this; - } - - /** - * @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(); - } - - /** - * @param value Plain text narrative that identifies the resource in addition to the resource reference. - */ - public Reference setDisplay(String value) { - if (Utilities.noString(value)) - this.display = null; - else { - if (this.display == null) - this.display = new StringType(); - this.display.setValue(value); - } - return this; - } - - /** - * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given - * reference. - * - * @param theReference The reference, or null - * @return - * @return Returns a reference to this - */ - public Reference setReferenceElement(IIdType theReference) { - if (theReference != null) { - setReference(theReference.getValue()); - } else { - setReference(null); - } - return this; - } - protected void listChildren(List children) { - super.listChildren(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)); - 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 { - switch (_hash) { - 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 -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 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 { - switch (hash) { - case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType - case -1618432855: /*identifier*/ return this.identifier == null ? new Base[0] : new Base[] {this.identifier}; // Identifier - 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 { - switch (hash) { - case -925155509: // reference - this.reference = castToString(value); // StringType - return value; - case -1618432855: // identifier - this.identifier = castToIdentifier(value); // Identifier - return value; - case 1671764162: // display - this.display = castToString(value); // StringType - return value; - default: return super.setProperty(hash, name, value); - } - - } - - @Override - public Base setProperty(String name, Base value) throws FHIRException { - if (name.equals("reference")) { - this.reference = castToString(value); // StringType - } else if (name.equals("identifier")) { - this.identifier = castToIdentifier(value); // Identifier - } else if (name.equals("display")) { - this.display = castToString(value); // StringType - } else - return super.setProperty(name, value); - return value; - } - - @Override - public Base makeProperty(int hash, String name) throws FHIRException { - switch (hash) { - case -925155509: return getReferenceElement_(); - case -1618432855: return getIdentifier(); - case 1671764162: return getDisplayElement(); - default: return super.makeProperty(hash, name); - } - - } - - @Override - public String[] getTypesForProperty(int hash, String name) throws FHIRException { - switch (hash) { - case -925155509: /*reference*/ return new String[] {"string"}; - case -1618432855: /*identifier*/ return new String[] {"Identifier"}; - case 1671764162: /*display*/ return new String[] {"string"}; - default: return super.getTypesForProperty(hash, name); - } - - } - - @Override - public Base addChild(String name) throws FHIRException { - 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(); - return this.identifier; - } - else if (name.equals("display")) { - throw new FHIRException("Cannot call addChild on a primitive type Reference.display"); - } - else - return super.addChild(name); - } - - public String fhirType() { - return "Reference"; - - } - - public Reference copy() { - Reference dst = new Reference(); - copyValues(dst); - dst.reference = reference == null ? null : reference.copy(); - dst.identifier = identifier == null ? null : identifier.copy(); - dst.display = display == null ? null : display.copy(); - return dst; - } - - protected Reference typedCopy() { - return copy(); - } - - @Override - public boolean equalsDeep(Base other_) { - if (!super.equalsDeep(other_)) - return false; - if (!(other_ instanceof Reference)) - return false; - Reference o = (Reference) other_; - return compareDeep(reference, o.reference, true) && compareDeep(identifier, o.identifier, true) - && compareDeep(display, o.display, true); - } - - @Override - public boolean equalsShallow(Base other_) { - if (!super.equalsShallow(other_)) - return false; - if (!(other_ instanceof Reference)) - return false; - 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 - ); - } - - -} - +package org.hl7.fhir.dstu3.model; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * 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 + prior written permission. + + 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 + 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, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + 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, + 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 + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Fri, Mar 16, 2018 15:21+1100 for FHIR v3.0.1 + +import java.util.*; + +import org.hl7.fhir.utilities.Utilities; +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.ChildOrder; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.model.api.annotation.Block; +import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +/** + * A reference from one resource to another. + */ +@DatatypeDef(name="Reference") +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. + */ + @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." ) + 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. + */ + @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." ) + protected Identifier identifier; + + /** + * 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) + @Description(shortDefinition="Text alternative for the resource", formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference." ) + protected StringType display; + + private static final long serialVersionUID = -909353281L; + + /** + * Constructor + */ + public Reference() { + super(); + } + + /** + * Constructor + * + * @param theReference The given reference string (e.g. "Patient/123" or "http://example.com/Patient/123") + */ + public Reference(String theReference) { + super(theReference); + } + + /** + * Constructor + * + * @param theReference The given reference as an IdType (e.g. "Patient/123" or "http://example.com/Patient/123") + */ + public Reference(IIdType theReference) { + super(theReference); + } + + /** + * Constructor + * + * @param theResource The resource represented by this reference + */ + public Reference(IAnyResource 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 + */ + public StringType getReferenceElement_() { + if (this.reference == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Reference.reference"); + else if (Configuration.doAutoCreate()) + this.reference = new StringType(); // bb + return this.reference; + } + + public boolean hasReferenceElement() { + return this.reference != null && !this.reference.isEmpty(); + } + + public boolean hasReference() { + 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 + */ + public Reference setReferenceElement(StringType value) { + this.reference = value; + 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. + */ + @Override + 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. + */ + public Reference setReference(String value) { + if (Utilities.noString(value)) + this.reference = null; + else { + if (this.reference == null) + this.reference = new StringType(); + this.reference.setValue(value); + } + 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.) + */ + public Identifier getIdentifier() { + if (this.identifier == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Reference.identifier"); + else if (Configuration.doAutoCreate()) + this.identifier = new Identifier(); // cc + return this.identifier; + } + + 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.) + */ + public Reference setIdentifier(Identifier value) { + 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 + */ + public StringType getDisplayElement() { + if (this.display == null) + if (Configuration.errorOnAutoCreate()) + throw new Error("Attempt to auto-create Reference.display"); + else if (Configuration.doAutoCreate()) + this.display = new StringType(); // bb + return this.display; + } + + public boolean hasDisplayElement() { + 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 + */ + public Reference setDisplayElement(StringType value) { + this.display = value; + return this; + } + + /** + * @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(); + } + + /** + * @param value Plain text narrative that identifies the resource in addition to the resource reference. + */ + public Reference setDisplay(String value) { + if (Utilities.noString(value)) + this.display = null; + else { + if (this.display == null) + this.display = new StringType(); + this.display.setValue(value); + } + return this; + } + + /** + * Convenience setter which sets the reference to the complete {@link IIdType#getValue() value} of the given + * reference. + * + * @param theReference The reference, or null + * @return + * @return Returns a reference to this + */ + public Reference setReferenceElement(IIdType theReference) { + if (theReference != null) { + setReference(theReference.getValue()); + } else { + setReference(null); + } + return this; + } + protected void listChildren(List children) { + super.listChildren(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)); + 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 { + switch (_hash) { + 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 -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 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 { + switch (hash) { + case -925155509: /*reference*/ return this.reference == null ? new Base[0] : new Base[] {this.reference}; // StringType + case -1618432855: /*identifier*/ return this.identifier == null ? new Base[0] : new Base[] {this.identifier}; // Identifier + 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 { + switch (hash) { + case -925155509: // reference + this.reference = castToString(value); // StringType + return value; + case -1618432855: // identifier + this.identifier = castToIdentifier(value); // Identifier + return value; + case 1671764162: // display + this.display = castToString(value); // StringType + return value; + default: return super.setProperty(hash, name, value); + } + + } + + @Override + public Base setProperty(String name, Base value) throws FHIRException { + if (name.equals("reference")) { + this.reference = castToString(value); // StringType + } else if (name.equals("identifier")) { + this.identifier = castToIdentifier(value); // Identifier + } else if (name.equals("display")) { + this.display = castToString(value); // StringType + } else + return super.setProperty(name, value); + return value; + } + + @Override + public Base makeProperty(int hash, String name) throws FHIRException { + switch (hash) { + case -925155509: return getReferenceElement_(); + case -1618432855: return getIdentifier(); + case 1671764162: return getDisplayElement(); + default: return super.makeProperty(hash, name); + } + + } + + @Override + public String[] getTypesForProperty(int hash, String name) throws FHIRException { + switch (hash) { + case -925155509: /*reference*/ return new String[] {"string"}; + case -1618432855: /*identifier*/ return new String[] {"Identifier"}; + case 1671764162: /*display*/ return new String[] {"string"}; + default: return super.getTypesForProperty(hash, name); + } + + } + + @Override + public Base addChild(String name) throws FHIRException { + 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(); + return this.identifier; + } + else if (name.equals("display")) { + throw new FHIRException("Cannot call addChild on a primitive type Reference.display"); + } + else + return super.addChild(name); + } + + public String fhirType() { + return "Reference"; + + } + + public Reference copy() { + Reference dst = new Reference(); + copyValues(dst); + dst.reference = reference == null ? null : reference.copy(); + dst.identifier = identifier == null ? null : identifier.copy(); + dst.display = display == null ? null : display.copy(); + return dst; + } + + protected Reference typedCopy() { + return copy(); + } + + @Override + public boolean equalsDeep(Base other_) { + if (!super.equalsDeep(other_)) + return false; + if (!(other_ instanceof Reference)) + return false; + Reference o = (Reference) other_; + return compareDeep(reference, o.reference, true) && compareDeep(identifier, o.identifier, true) + && compareDeep(display, o.display, true); + } + + @Override + public boolean equalsShallow(Base other_) { + if (!super.equalsShallow(other_)) + return false; + if (!(other_ instanceof Reference)) + return false; + 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 + ); + } + + +} + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java index aa427a35a67..017a9d1494a 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java @@ -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.MedicationRequest; 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.ObservationStatus; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -47,10 +46,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { @Before public void before() { - myGen = new DefaultThymeleafNarrativeGenerator(); + myGen = new DefaultThymeleafNarrativeGenerator(ourCtx); myGen.setUseHapiServerConformanceNarrative(true); - myGen.setIgnoreFailures(false); - myGen.setIgnoreMissingTemplates(false); ourCtx.setNarrativeGenerator(myGen); } @@ -67,18 +64,15 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { Patient value = new Patient(); value.addIdentifier().setSystem("urn:names").setValue("123456"); - value.addName().setFamily("blow").addGiven("joe").addGiven((String) null).addGiven("john"); - //@formatter:off + value.addName().setFamily("blow").addGiven("joe").addGiven(null).addGiven("john"); value.addAddress() .addLine("123 Fake Street").addLine("Unit 1") .setCity("Toronto").setState("ON").setCountry("Canada"); - //@formatter:on value.setBirthDate(new Date()); - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString("
    joe john BLOW
    ")); @@ -86,9 +80,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { @Test public void testTranslations() throws DataFormatException { - CustomThymeleafNarrativeGenerator customGen = new CustomThymeleafNarrativeGenerator("classpath:/testnarrative.properties"); - customGen.setIgnoreFailures(false); - customGen.setIgnoreMissingTemplates(false); + CustomThymeleafNarrativeGenerator customGen = new CustomThymeleafNarrativeGenerator(ourCtx, "classpath:/testnarrative.properties"); FhirContext ctx = FhirContext.forDstu3(); ctx.setNarrativeGenerator(customGen); @@ -96,7 +88,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { Patient value = new Patient(); 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() .addLine("123 Fake Street").addLine("Unit 1") @@ -106,7 +98,6 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { value.setBirthDate(new Date()); Transformer transformer = new Transformer() { - @Override public Object transform(Object input) { return "UNTRANSLATED:" + input; @@ -123,9 +114,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { } }); - Narrative narrative = new Narrative(); - customGen.generateNarrative(ctx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + customGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString("Some beautiful proze")); assertThat(output, StringContains.containsString("UNTRANSLATED:other_text")); @@ -140,9 +130,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { value.addResult().setReference("Observation/2"); value.addResult().setReference("Observation/3"); - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString(value.getCode().getTextElement().getValue())); @@ -169,9 +158,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { // ourLog.info(output); // assertEquals("Operation Outcome (2 issues)", output); - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, oo, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(oo); + String output = oo.getText().getDiv().getValueAsString(); ourLog.info(output); @@ -209,9 +197,8 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { value.addResult().setResource(obs); } - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, value, narrative); - String output = narrative.getDiv().getValueAsString(); + myGen.populateResourceNarrative(value); + String output = value.getText().getDiv().getValueAsString(); ourLog.info(output); assertThat(output, StringContains.containsString("
    Some & Diagnostic Report
    ")); @@ -230,11 +217,11 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { mp.setStatus(MedicationRequestStatus.ACTIVE); mp.setAuthoredOnElement(new DateTimeType("2014-09-01")); - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, mp, narrative); + myGen.populateResourceNarrative(mp); + 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 string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1); + assertTrue("Expected medication name of ciprofloaxin within narrative: "+output, output.contains("ciprofloaxin")); + assertTrue("Expected string status of ACTIVE within narrative: " +output, output.contains("ACTIVE")); } @@ -243,11 +230,10 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { Medication med = new Medication(); med.getCode().setText("ciproflaxin"); - Narrative narrative = new Narrative(); - myGen.generateNarrative(ourCtx, med, narrative); + myGen.populateResourceNarrative(med); - String string = narrative.getDiv().getValueAsString(); - assertThat(string, containsString("ciproflaxin")); + String output = med.getText().getDiv().getValueAsString(); + assertThat(output, containsString("ciproflaxin")); } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java new file mode 100644 index 00000000000..56d54527bf1 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java @@ -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("NameValue")); + assertThat(narrative, containsString(" 4.2 - 6.0 ")); + } + + @Test + public void testTemplateCount() throws IOException { + NarrativeTemplateManifest manifest = NarrativeTemplateManifest.forManifestFileLocation(myCtx, "classpath:narrative2/narratives.properties"); + assertEquals(4, manifest.getNamedTemplateCount()); + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java index b39adde2ac4..ec4485267be 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java @@ -1241,7 +1241,7 @@ public class JsonParserDstu3Test { Patient p = new Patient(); p.addName().setFamily("Smith").addGiven("John"); - ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); String output = ourCtx.newJsonParser().encodeResourceToString(p); ourLog.info(output); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java index bcd36c10200..988af50b906 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java @@ -1943,7 +1943,7 @@ public class XmlParserDstu3Test { Patient p = new Patient(); p.addName().setFamily("Smith").addGiven("John"); - ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); String output = ourCtx.newXmlParser().encodeResourceToString(p); ourLog.info(output); diff --git a/hapi-fhir-structures-dstu3/src/test/resources/narrative2/composition.html b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/composition.html new file mode 100644 index 00000000000..83e70d50310 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/composition.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/hapi-fhir-structures-dstu3/src/test/resources/narrative2/diagnosticreport.html b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/diagnosticreport.html new file mode 100644 index 00000000000..4032aa29741 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/diagnosticreport.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + +
    Status
    Issued
    Conclusion
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameValueInterpretationReference RangeStatus
    + + + + + + + + + + - + + +
    + +
    diff --git a/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_datetime.html b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_datetime.html new file mode 100644 index 00000000000..576bc9b5ee3 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_datetime.html @@ -0,0 +1,3 @@ + + [[${resource.getValueAsString().replace("T", " ")}]] + diff --git a/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_quantity.html b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_quantity.html new file mode 100644 index 00000000000..3023bbe9c32 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/dt_quantity.html @@ -0,0 +1,3 @@ + + [[${resource.value}]] + diff --git a/hapi-fhir-structures-dstu3/src/test/resources/narrative2/narratives.properties b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/narratives.properties new file mode 100644 index 00000000000..ab1844a2336 --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/resources/narrative2/narratives.properties @@ -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 diff --git a/hapi-fhir-structures-dstu3/src/test/resources/testnarrative.properties b/hapi-fhir-structures-dstu3/src/test/resources/testnarrative.properties index 22f3b3c51b0..f03016cff8a 100644 --- a/hapi-fhir-structures-dstu3/src/test/resources/testnarrative.properties +++ b/hapi-fhir-structures-dstu3/src/test/resources/testnarrative.properties @@ -1,2 +1,2 @@ -patient.class=org.hl7.fhir.dstu3.model.Patient +patient.resourceType=Patient patient.narrative=classpath:/TestPatient.html diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BaseReference.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BaseReference.java index 73b8e1e7816..5bc6f62222c 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BaseReference.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BaseReference.java @@ -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 * may be changed/accessed by parsers. */ - public void setResource(IBaseResource theResource) { + @Override + public BaseReference setResource(IBaseResource theResource) { resource = theResource; + return null; } @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java index 81c6fefdab5..67d1a2ccdf6 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java @@ -64,7 +64,7 @@ public class BundleTypeInResponseHl7OrgTest { ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); servlet.setResourceProviders(patientProvider); ServletHolder servletHolder = new ServletHolder(servlet); diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseReference.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseReference.java index 4de5811b713..fc35981e94e 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseReference.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/BaseReference.java @@ -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 * may be changed/accessed by parsers. */ + @Override public IBaseResource getResource() { 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 * may be changed/accessed by parsers. */ - public void setResource(IBaseResource theResource) { + @Override + public BaseReference setResource(IBaseResource theResource) { resource = theResource; + return this; } @Override diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/Reference.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/Reference.java index 34b30c157ca..811456d3a5a 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/Reference.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/model/Reference.java @@ -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. */ - public String getReference() { + @Override + public String getReference() { return this.reference == null ? null : this.reference.getValue(); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java index f756ff0de84..33104f239f7 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java @@ -449,7 +449,7 @@ public class DateRangeParamR4Test { ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); servlet.setResourceProviders(patientProvider); ServletHolder servletHolder = new ServletHolder(servlet); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java index 840cb4d2de7..9b31d2fad74 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerDstu1Test.java @@ -474,7 +474,7 @@ public class SearchSearchServerDstu1Test { ServletHandler proxyHandler = new ServletHandler(); ourServlet = new RestfulServer(ourCtx); - ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator(ourCtx)); ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)); ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider()); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index fa7b2f4c83a..39b964da8e2 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -17,6 +17,7 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.time.DateUtils; import org.fhir.ucum.UcumService; 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.IValidationSupport; import org.hl7.fhir.dstu3.model.*; diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/validation/ParserWithValidationDstu3Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/validation/ParserWithValidationDstu3Test.java index 869a0063b49..5de30ec4b74 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/validation/ParserWithValidationDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/validation/ParserWithValidationDstu3Test.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; 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.model.ActivityDefinition; import org.hl7.fhir.dstu3.model.ConceptMap; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/elementmodel/PropertyDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/elementmodel/PropertyDstu3Test.java index 1fa340f11cd..d6464ee3af7 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/elementmodel/PropertyDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/elementmodel/PropertyDstu3Test.java @@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; import org.apache.commons.io.IOUtils; 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.StructureDefinition; import org.hl7.fhir.exceptions.DefinitionException; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupportTest.java index 6e053bbcde4..378c1095459 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupportTest.java @@ -2,7 +2,7 @@ package org.hl7.fhir.dstu3.hapi.validation; 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.Test; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index 132c32c55d7..fc3780ded04 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; import com.google.common.base.Charsets; 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.IValidationSupport; 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.utils.FHIRPathEngine; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index e50b14f40bc..4acd69abfe9 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; 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.CodeValidationResult; import org.hl7.fhir.dstu3.model.*; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java index 1d61012055b..b1d3d2c4ad3 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ValidationResult; 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.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java index 5c419e6ac56..fca5d67377a 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java @@ -14,6 +14,7 @@ import org.apache.commons.lang3.Validate; import org.hamcrest.core.StringContains; import org.hl7.fhir.dstu3.conformance.ProfileUtilities; 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.model.*; import org.hl7.fhir.dstu3.model.Condition.ConditionClinicalStatus; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/StructureMapTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/StructureMapTest.java index 2423f3c083a..0c974214ace 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/StructureMapTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/StructureMapTest.java @@ -7,6 +7,7 @@ import java.util.*; import javax.annotation.Nullable; 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.model.*; import org.hl7.fhir.dstu3.utils.StructureMapUtilities; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java index 659e68f3a8d..2d9fdd7527b 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java @@ -3,7 +3,7 @@ package org.hl7.fhir.dstu3.utils; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.util.TestUtil; 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.exceptions.FHIRException; import org.junit.AfterClass; diff --git a/restful-server-example/src/main/java/ca/uhn/example/servlet/ExampleRestfulServlet.java b/restful-server-example/src/main/java/ca/uhn/example/servlet/ExampleRestfulServlet.java index 3103aaa2634..97f8c81a375 100644 --- a/restful-server-example/src/main/java/ca/uhn/example/servlet/ExampleRestfulServlet.java +++ b/restful-server-example/src/main/java/ca/uhn/example/servlet/ExampleRestfulServlet.java @@ -38,7 +38,7 @@ public class ExampleRestfulServlet extends RestfulServer { * Two resource providers are defined. Each one handles a specific * type of resource. */ - List providers = new ArrayList(); + List providers = new ArrayList<>(); providers.add(new PatientResourceProvider()); providers.add(new OrganizationResourceProvider()); setResourceProviders(providers); @@ -48,7 +48,7 @@ public class ExampleRestfulServlet extends RestfulServer { * but can be useful as it causes HAPI to generate narratives for * resources which don't otherwise have one. */ - INarrativeGenerator narrativeGen = new DefaultThymeleafNarrativeGenerator(); + INarrativeGenerator narrativeGen = new DefaultThymeleafNarrativeGenerator(getFhirContext()); getFhirContext().setNarrativeGenerator(narrativeGen); /*