From da9efaea0b289d8882d89842946d4996eb100b66 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 1 Oct 2019 15:20:48 -0400 Subject: [PATCH 1/6] Credit for https://github.com/hapifhir/hapi-fhir-jpaserver-starter/pull/54 --- pom.xml | 4 ++++ src/changes/changes.xml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index 6245738f0d7..97d930586b7 100755 --- a/pom.xml +++ b/pom.xml @@ -555,6 +555,10 @@ jaferkhan + + CodeAndChoke + Long Nguyen + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 450c6c75460..27af222dde2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -296,6 +296,10 @@ project has been fixed. Thanks to GitHub user @jaferkhan for the pull request! + + A docker compose script for the hapi-fhir-jpaserver-starter project was added. Thanks to + Long Nguyen for the pull request! + From 08825a81d908db1a40fcb343ae5b4181154d0b4d Mon Sep 17 00:00:00 2001 From: Clayton Bodendein Date: Fri, 6 Sep 2019 10:56:49 -0500 Subject: [PATCH 2/6] Add missing @Override annotations Added missing @Override annotations so that if/when methods in superclasses change there will be a compile-time error if subclasses haven't been changed accordingly. This was done using IntelliJ IDEA's "Missing @Override annotation" inspection. --- .../rest/client/apache/ApacheHttpClient.java | 4 + .../interceptor/LoggingInterceptor.java | 2 + .../igpacks/parser/IgPackParserDstu2.java | 2 + .../igpacks/parser/IgPackParserDstu3.java | 2 + .../WebsocketWithCriteriaDstu2Test.java | 2 + .../WebsocketWithSubscriptionIdDstu2Test.java | 2 + .../validation/InstanceValidator.java | 4 + .../model/dstu2/composite/ContainedDt.java | 1 + .../model/dstu2/composite/NarrativeDt.java | 6 +- .../dstu2/composite/ResourceReferenceDt.java | 10 +- .../model/dstu2/resource/BaseResource.java | 3 + .../ca/uhn/fhir/ctx/FhirContextDstu2Test.java | 1 + .../parser/CustomMedicationOrderDstu2.java | 1 + .../parser/jsonlike/JsonLikeParserTest.java | 1469 +++++++++-------- .../validation/InstanceValidator.java | 7 + .../instance/validation/ProfileValidator.java | 1 + .../fhir/model/dstu3/composite/CodingDt.java | 29 +- .../model/dstu3/composite/NarrativeDt.java | 3 +- .../model/dstu3/composite/QuantityDt.java | 23 +- .../dstu3/composite/ResourceReferenceDt.java | 10 +- .../uhn/fhir/tinder/model/ResourceBlock.java | 1 + .../ca/uhn/fhir/tinder/util/XMLUtils.java | 261 +-- .../org/hl7/fhir/instance/model/IIdType.java | 1 + 23 files changed, 956 insertions(+), 889 deletions(-) diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java index ea568b9fc09..036e72a2205 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java @@ -87,11 +87,13 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient { } + @Override protected IHttpRequest createHttpRequest() { IHttpRequest retVal = createHttpRequest((HttpEntity)null); return retVal; } + @Override protected IHttpRequest createHttpRequest(byte[] content) { /* * Note: Be careful about changing which constructor we use for @@ -109,6 +111,7 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient { return result; } + @Override protected IHttpRequest createHttpRequest(Map> theParams) { List parameters = new ArrayList(); for (Entry> nextParam : theParams.entrySet()) { @@ -124,6 +127,7 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient { } + @Override protected IHttpRequest createHttpRequest(String theContents) { /* * We aren't using a StringEntity here because the constructors diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java index aa8ae708225..d46fdaf6b0b 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/LoggingInterceptor.java @@ -76,6 +76,7 @@ public class LoggingInterceptor implements IClientInterceptor { } } + @Override @Hook(Pointcut.CLIENT_REQUEST) public void interceptRequest(IHttpRequest theRequest) { if (myLogRequestSummary) { @@ -101,6 +102,7 @@ public class LoggingInterceptor implements IClientInterceptor { } } + @Override @Hook(Pointcut.CLIENT_RESPONSE) public void interceptResponse(IHttpResponse theResponse) throws IOException { if (myLogResponseSummary) { diff --git a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu2.java b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu2.java index f863d6f6a7f..cb8219541aa 100644 --- a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu2.java +++ b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu2.java @@ -35,10 +35,12 @@ public class IgPackParserDstu2 extends BaseIgPackParser { super(massage(theCtx)); } + @Override protected IValidationSupport createValidationSupport(Map theIgResources) { return new IgPackValidationSupportDstu2(theIgResources); } + @Override protected FhirVersionEnum provideExpectedVersion() { return FhirVersionEnum.DSTU2_HL7ORG; } diff --git a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu3.java b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu3.java index be2ba32dcbc..5ea1fe0aa86 100644 --- a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu3.java +++ b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackParserDstu3.java @@ -37,10 +37,12 @@ public class IgPackParserDstu3 extends BaseIgPackParser { super(theCtx); } + @Override protected IValidationSupport createValidationSupport(Map theIgResources) { return new IgPackValidationSupportDstu3(theIgResources); } + @Override protected FhirVersionEnum provideExpectedVersion() { return FhirVersionEnum.DSTU3; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithCriteriaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithCriteriaDstu2Test.java index 54e1f616fff..6efb25987eb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithCriteriaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithCriteriaDstu2Test.java @@ -41,11 +41,13 @@ public class WebsocketWithCriteriaDstu2Test extends BaseResourceProviderDstu2Tes private WebSocketClient myWebSocketClient; private SocketImplementation mySocketImplementation; + @Override @After public void after() throws Exception { super.after(); } + @Override @Before public void before() throws Exception { super.before(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java index 692848a5260..5b292157efe 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java @@ -58,6 +58,7 @@ public class WebsocketWithSubscriptionIdDstu2Test extends BaseResourceProviderDs @Autowired private SubscriptionTestUtil mySubscriptionTestUtil; + @Override @After public void after() throws Exception { super.after(); @@ -70,6 +71,7 @@ public class WebsocketWithSubscriptionIdDstu2Test extends BaseResourceProviderDs myWebSocketClient.stop(); } + @Override @Before public void before() throws Exception { super.before(); diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/validation/InstanceValidator.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/validation/InstanceValidator.java index f369bbcf2c4..76850ebea67 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/validation/InstanceValidator.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/validation/InstanceValidator.java @@ -1021,6 +1021,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return Utilities.appendSlash(base) + type + "/" + id; } + @Override public BestPracticeWarningLevel getBasePracticeWarningLevel() { return bpWarnings; } @@ -1416,6 +1417,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.anyExtensionsAllowed = anyExtensionsAllowed; } + @Override public void setBestPracticeWarningLevel(BestPracticeWarningLevel value) { bpWarnings = value; } @@ -1429,10 +1431,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.suppressLoincSnomedMessages = suppressLoincSnomedMessages; } + @Override public IdStatus getResourceIdRule() { return resourceIdRule; } + @Override public void setResourceIdRule(IdStatus resourceIdRule) { this.resourceIdRule = resourceIdRule; } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ContainedDt.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ContainedDt.java index 01875435ced..987d1676252 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ContainedDt.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ContainedDt.java @@ -35,6 +35,7 @@ public class ContainedDt extends BaseContainedDt { @Child(name = "resource", type = IResource.class, order = 0, min = 0, max = Child.MAX_UNLIMITED) private List myContainedResources; + @Override public List getContainedResources() { if (myContainedResources == null) { myContainedResources = new ArrayList(); diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java index 28555dd3a46..ce404c61268 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/NarrativeDt.java @@ -114,7 +114,8 @@ public class NarrativeDt extends BaseNarrativeDt { * The status of the narrative - whether it's entirely generated (from just the defined data or the extensions too), or whether a human authored it and it may contain additional data *

*/ - public BoundCodeDt getStatus() { + @Override + public BoundCodeDt getStatus() { if (myStatus == null) { myStatus = new BoundCodeDt(NarrativeStatusEnum.VALUESET_BINDER); } @@ -170,7 +171,8 @@ public class NarrativeDt extends BaseNarrativeDt { * The actual narrative content, a stripped down version of XHTML *

*/ - public XhtmlDt getDiv() { + @Override + public XhtmlDt getDiv() { if (myDiv == null) { myDiv = new XhtmlDt(); } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ResourceReferenceDt.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ResourceReferenceDt.java index aafc9e886ca..58aa4b93a99 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ResourceReferenceDt.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/composite/ResourceReferenceDt.java @@ -162,7 +162,8 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 IdDt getReference() { + @Override + public IdDt getReference() { if (myReference == null) { myReference = new IdDt(); } @@ -183,6 +184,7 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 ResourceReferenceDt setReference(IdDt theValue) { myReference = theValue; return this; @@ -196,7 +198,8 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 ResourceReferenceDt setReference( String theId) { + @Override + public ResourceReferenceDt setReference(String theId) { myReference = new IdDt(theId); return this; } @@ -240,7 +243,8 @@ public class ResourceReferenceDt * Plain text narrative that identifies the resource in addition to the resource reference *

*/ - public ResourceReferenceDt setDisplay( String theString) { + @Override + public ResourceReferenceDt setDisplay(String theString) { myDisplay = new StringDt(theString); return this; } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java index fe058d3eae8..8c03b43d360 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/resource/BaseResource.java @@ -313,10 +313,12 @@ public abstract class BaseResource extends BaseElement implements IResource { myContained = theContained; } + @Override public void setId(IdDt theId) { myId = theId; } + @Override public BaseResource setId(IIdType theId) { if (theId instanceof IdDt) { myId = (IdDt) theId; @@ -328,6 +330,7 @@ public abstract class BaseResource extends BaseElement implements IResource { return this; } + @Override public BaseResource setId(String theId) { if (theId == null) { myId = null; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java index 97a17c3770c..79fc042d5ec 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/ctx/FhirContextDstu2Test.java @@ -142,6 +142,7 @@ public class FhirContextDstu2Test { final CountDownLatch allDone = new CountDownLatch(numThreads); for (final Runnable submittedTestRunnable : runnables) { threadPool.submit(new Runnable() { + @Override public void run() { allExecutorThreadsReady.countDown(); try { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/CustomMedicationOrderDstu2.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/CustomMedicationOrderDstu2.java index bc1ca425775..e2120ba5f49 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/CustomMedicationOrderDstu2.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/CustomMedicationOrderDstu2.java @@ -17,6 +17,7 @@ public class CustomMedicationOrderDstu2 extends MedicationOrder { @Child(name = "medication", order = Child.REPLACE_PARENT, min = 1, max = 1, summary = false, modifier = false, type = { Medication.class }) private ResourceReferenceDt myMedication; + @Override public ResourceReferenceDt getMedication() { return myMedication; } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/jsonlike/JsonLikeParserTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/jsonlike/JsonLikeParserTest.java index f82b2ce3153..72841e5450a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/jsonlike/JsonLikeParserTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/jsonlike/JsonLikeParserTest.java @@ -1,732 +1,737 @@ -package ca.uhn.fhir.parser.jsonlike; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.Writer; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.IntegerType; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.Extension; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Test; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.parser.IJsonLikeParser; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.parser.json.GsonStructure; -import ca.uhn.fhir.parser.json.JsonLikeArray; -import ca.uhn.fhir.parser.json.JsonLikeObject; -import ca.uhn.fhir.parser.json.JsonLikeStructure; -import ca.uhn.fhir.parser.json.JsonLikeValue; -import ca.uhn.fhir.parser.json.JsonLikeWriter; -import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType; -import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; -import ca.uhn.fhir.parser.view.ExtPatient; -import ca.uhn.fhir.util.TestUtil; - -public class JsonLikeParserTest { - private static FhirContext ourCtx = FhirContext.forR4(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonLikeParserTest.class); - - /** - * Test for JSON Parser with user-supplied JSON-like structure (use default GSON) - */ - @Test - public void testJsonLikeParseAndEncodeResourceFromXmlToJson() throws Exception { - String content = IOUtils.toString(JsonLikeParserTest.class.getResourceAsStream("/extension-on-line.txt")); - - IBaseResource parsed = ourCtx.newJsonParser().parseResource(content); - - String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed); - ourLog.info(encoded); - - JsonLikeStructure jsonLikeStructure = new GsonStructure(); - jsonLikeStructure.load(new StringReader(encoded)); - - IJsonLikeParser jsonLikeparser = (IJsonLikeParser)ourCtx.newJsonParser(); - - IBaseResource resource = jsonLikeparser.parseResource(jsonLikeStructure); - Assert.assertEquals("reparsed resource classes not equal", parsed.getClass().getName(), resource.getClass().getName()); - } - - /** - * Test JSON-Like writer using custom stream writer - * - */ - @Test - public void testJsonLikeParseWithCustomJSONStreamWriter() throws Exception { - String refVal = "http://my.org/FooBar"; - - Patient fhirPat = new Patient(); - fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal)); - - IJsonLikeParser jsonLikeParser = (IJsonLikeParser)ourCtx.newJsonParser(); - JsonLikeMapWriter jsonLikeWriter = new JsonLikeMapWriter(); - - jsonLikeParser.encodeResourceToJsonLikeWriter(fhirPat, jsonLikeWriter); - Map jsonLikeMap = jsonLikeWriter.getResultMap(); - - System.out.println("encoded map: " + jsonLikeMap.toString()); - - Assert.assertNotNull("Encoded resource missing 'resourceType' element", jsonLikeMap.get("resourceType")); - Assert.assertEquals("Expecting 'resourceType'='Patient'; found '"+jsonLikeMap.get("resourceType")+"'", jsonLikeMap.get("resourceType"), "Patient"); - - Assert.assertNotNull("Encoded resource missing 'extension' element", jsonLikeMap.get("extension")); - Assert.assertTrue("'extension' element is not a List", (jsonLikeMap.get("extension") instanceof List)); - - List extensions = (List)jsonLikeMap.get("extension"); - Assert.assertEquals("'extnesion' array has more than one entry", 1, extensions.size()); - Assert.assertTrue("'extension' array entry is not a Map", (extensions.get(0) instanceof Map)); - - Map extension = (Map)extensions.get(0); - Assert.assertNotNull("'extension' entry missing 'url' member", extension.get("url")); - Assert.assertTrue("'extension' entry 'url' member is not a String", (extension.get("url") instanceof String)); - Assert.assertEquals("Expecting '/extension[]/url' = 'x1'; found '"+extension.get("url")+"'", "x1", (String)extension.get("url")); - - } - - /** - * Repeat the "View" tests with custom JSON-Like structure - */ - @Test - public void testViewJson() throws Exception { - - ExtPatient src = new ExtPatient(); - src.addIdentifier().setSystem("urn:sys").setValue("id1"); - src.addIdentifier().setSystem("urn:sys").setValue("id2"); - src.getExt().setValue(100); - src.getModExt().setValue(200); - - IJsonLikeParser jsonLikeParser = (IJsonLikeParser)ourCtx.newJsonParser(); - JsonLikeMapWriter jsonLikeWriter = new JsonLikeMapWriter(); - jsonLikeParser.encodeResourceToJsonLikeWriter(src, jsonLikeWriter); - Map jsonLikeMap = jsonLikeWriter.getResultMap(); - - - ourLog.info("encoded: "+jsonLikeMap); - - JsonLikeStructure jsonStructure = new JsonLikeMapStructure(jsonLikeMap); - IJsonLikeParser parser = (IJsonLikeParser)ourCtx.newJsonParser(); - Patient nonExt = parser.parseResource(Patient.class, jsonStructure); - - Assert.assertEquals(Patient.class, nonExt.getClass()); - Assert.assertEquals("urn:sys", nonExt.getIdentifier().get(0).getSystem()); - Assert.assertEquals("id1", nonExt.getIdentifier().get(0).getValue()); - Assert.assertEquals("urn:sys", nonExt.getIdentifier().get(1).getSystem()); - Assert.assertEquals("id2", nonExt.getIdentifier().get(1).getValue()); - - List ext = nonExt.getExtensionsByUrl("urn:ext"); - Assert.assertEquals(1, ext.size()); - Assert.assertEquals("urn:ext", ext.get(0).getUrl()); - Assert.assertEquals(IntegerType.class, ext.get(0).getValueAsPrimitive().getClass()); - Assert.assertEquals("100", ext.get(0).getValueAsPrimitive().getValueAsString()); - - List modExt = nonExt.getExtensionsByUrl("urn:modExt"); - Assert.assertEquals(1, modExt.size()); - Assert.assertEquals("urn:modExt", modExt.get(0).getUrl()); - Assert.assertEquals(IntegerType.class, modExt.get(0).getValueAsPrimitive().getClass()); - Assert.assertEquals("200", modExt.get(0).getValueAsPrimitive().getValueAsString()); - - ExtPatient va = ourCtx.newViewGenerator().newView(nonExt, ExtPatient.class); - Assert.assertEquals("urn:sys", va.getIdentifier().get(0).getSystem()); - Assert.assertEquals("id1", va.getIdentifier().get(0).getValue()); - Assert.assertEquals("urn:sys", va.getIdentifier().get(1).getSystem()); - Assert.assertEquals("id2", va.getIdentifier().get(1).getValue()); - Assert.assertEquals(100, va.getExt().getValue().intValue()); - Assert.assertEquals(200, va.getModExt().getValue().intValue()); - - Assert.assertEquals(0, va.getExtension().size()); - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - - - public static class JsonLikeMapWriter extends JsonLikeWriter { - - private Map target; - - private static class Block { - private BlockType type; - private String name; - private Map object; - private List array; - public Block(BlockType type) { - this.type = type; - } - public BlockType getType() { - return type; - } - public String getName() { - return name; - } - public void setName(String currentName) { - this.name = currentName; - } - public Map getObject() { - return object; - } - public void setObject(Map currentObject) { - this.object = currentObject; - } - public List getArray() { - return array; - } - public void setArray(List currentArray) { - this.array = currentArray; - } - } - private enum BlockType { - NONE, OBJECT, ARRAY - } - private Block currentBlock = new Block(BlockType.NONE); - private Stack blockStack = new Stack(); - - public JsonLikeMapWriter () { - super(); - } - - public Map getResultMap() { - return target; - } - public void setResultMap(Map target) { - this.target = target; - } - - @Override - public JsonLikeWriter init() throws IOException { - if (target != null) { - target.clear(); - } - currentBlock = new Block(BlockType.NONE); - blockStack.clear(); - return this; - } - - @Override - public JsonLikeWriter flush() throws IOException { - if (currentBlock.getType() != BlockType.NONE) { - throw new IOException("JsonLikeStreamWriter.flush() called but JSON document is not finished"); - } - return this; - } - - @Override - public void close() { - // nothing to do - } - - @Override - public JsonLikeWriter beginObject() throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - Map newObject = null; - if (currentBlock.getType() == BlockType.NONE) { - if (null == target) { - // for this test, we don't care about ordering of map elements - // target = new EntryOrderedMap(); - target = new HashMap(); - } - newObject = target; - } else { - // for this test, we don't care about ordering of map elements - // newObject = new EntryOrderedMap(); - newObject = new HashMap(); - } - blockStack.push(currentBlock); - currentBlock = new Block(BlockType.OBJECT); - currentBlock.setObject(newObject); - return this; - } - - @Override - public JsonLikeWriter beginArray() throws IOException { - if (currentBlock.getType() == BlockType.NONE) { - throw new IOException("JsonLikeStreamWriter.beginArray() called but only beginObject() is allowed here."); - } - blockStack.push(currentBlock); - currentBlock = new Block(BlockType.ARRAY); - currentBlock.setArray(new ArrayList()); - return this; - } - - @Override - public JsonLikeWriter beginObject(String name) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - blockStack.push(currentBlock); - currentBlock = new Block(BlockType.OBJECT); - currentBlock.setName(name); - // for this test, we don't care about ordering of map elements - // currentBlock.setObject(new EntryOrderedMap()); - currentBlock.setObject(new HashMap()); - return this; - } - - @Override - public JsonLikeWriter beginArray(String name) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - blockStack.push(currentBlock); - currentBlock = new Block(BlockType.ARRAY); - currentBlock.setName(name); - currentBlock.setArray(new ArrayList()); - return this; - } - - @Override - public JsonLikeWriter write(String value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(value); - return this; - } - - @Override - public JsonLikeWriter write(BigInteger value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(value); - return this; - } - - @Override - public JsonLikeWriter write(BigDecimal value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(value); - return this; - } - - @Override - public JsonLikeWriter write(long value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(Long.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter write(double value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(Double.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter write(Boolean value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(value); - return this; - } - - @Override - public JsonLikeWriter write(boolean value) throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(Boolean.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter writeNull() throws IOException { - if (currentBlock.getType() == BlockType.OBJECT) { - throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); - } - currentBlock.getArray().add(null); - return this; - } - - @Override - public JsonLikeWriter write(String name, String value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, value); - return this; - } - - @Override - public JsonLikeWriter write(String name, BigInteger value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, value); - return this; - } - @Override - public JsonLikeWriter write(String name, BigDecimal value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, value); - return this; - } - - @Override - public JsonLikeWriter write(String name, long value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, Long.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter write(String name, double value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, Double.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter write(String name, Boolean value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, value); - return this; - } - - @Override - public JsonLikeWriter write(String name, boolean value) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, Boolean.valueOf(value)); - return this; - } - - @Override - public JsonLikeWriter writeNull(String name) throws IOException { - if (currentBlock.getType() == BlockType.ARRAY) { - throw new IOException("Named JSON elements can only be created in JSON objects"); - } - currentBlock.getObject().put(name, null); - return this; - } - - @Override - public JsonLikeWriter endObject() throws IOException { - if (currentBlock.getType() == BlockType.NONE) { - ourLog.error("JsonLikeStreamWriter.endObject(); called with no active JSON document"); - } else { - if (currentBlock.getType() != BlockType.OBJECT) { - ourLog.error("JsonLikeStreamWriter.endObject(); called outside a JSON object. (Use endArray() instead?)"); - } - endBlock(); - } - return this; - } - - @Override - public JsonLikeWriter endArray() throws IOException { - if (currentBlock.getType() == BlockType.NONE) { - ourLog.error("JsonLikeStreamWriter.endArray(); called with no active JSON document"); - } else { - if (currentBlock.getType() != BlockType.ARRAY) { - ourLog.error("JsonLikeStreamWriter.endArray(); called outside a JSON array. (Use endObject() instead?)"); - } - endBlock(); - } - return this; - } - - @Override - public JsonLikeWriter endBlock() throws IOException { - if (currentBlock.getType() == BlockType.NONE) { - ourLog.error("JsonLikeStreamWriter.endBlock(); called with no active JSON document"); - } else { - Object toPut = null; - if (currentBlock.getType() == BlockType.ARRAY) { - toPut = currentBlock.getArray(); - } else { - toPut = currentBlock.getObject(); - } - Block parentBlock = blockStack.pop(); - if (parentBlock.getType() == BlockType.OBJECT) { - parentBlock.getObject().put(currentBlock.getName(), toPut); - } else - if (parentBlock.getType() == BlockType.ARRAY) { - parentBlock.getArray().add(toPut); - } - currentBlock = parentBlock; - } - return this; - } - - } - - public static class JsonLikeMapStructure implements JsonLikeStructure { - - private Map nativeObject; - private JsonLikeObject jsonLikeObject = null; - private JsonLikeMapWriter jsonLikeWriter = null; - - public JsonLikeMapStructure() { - super(); - } - - public JsonLikeMapStructure (Map json) { - super(); - setNativeObject(json); - } - - public void setNativeObject (Map json) { - this.nativeObject = json; - } - - @Override - public JsonLikeStructure getInstance() { - return new JsonLikeMapStructure(); - } - - @Override - public JsonLikeWriter getJsonLikeWriter (Writer ignored) { - return getJsonLikeWriter(); - } - - @Override - public JsonLikeWriter getJsonLikeWriter () { - if (null == jsonLikeWriter) { - jsonLikeWriter = new JsonLikeMapWriter(); - } - return jsonLikeWriter; - } - - @Override - public void load(Reader reader) throws DataFormatException { - this.load(reader, true); - } - - @Override - public void load(Reader theReader, boolean allowArray) throws DataFormatException { - throw new DataFormatException("JSON structure loading is not supported for native Java Map structures"); - } - - @Override - public JsonLikeObject getRootObject() { - if (null == jsonLikeObject) { - jsonLikeObject = new JsonMapObject(nativeObject); - } - return jsonLikeObject; - } - - @Override - public JsonLikeArray getRootArray() throws DataFormatException { - throw new DataFormatException("JSON document must be an object not an array for native Java Map structures"); - } - - private class JsonMapObject extends JsonLikeObject { - private Map nativeObject; - private Map jsonLikeMap = new LinkedHashMap(); - - public JsonMapObject (Map json) { - this.nativeObject = json; - } - - @Override - public Object getValue() { - return nativeObject; - } - - @Override - public Set keySet() { - return nativeObject.keySet(); - } - - @Override - public JsonLikeValue get(String key) { - JsonLikeValue result = null; - if (jsonLikeMap.containsKey(key)) { - result = jsonLikeMap.get(key); - } else { - Object child = nativeObject.get(key); - if (child != null) { - result = new JsonMapValue(child); - } - jsonLikeMap.put(key, result); - } - return result; - } - } - - private class JsonMapArray extends JsonLikeArray { - private List nativeArray; - private Map jsonLikeMap = new LinkedHashMap(); - - public JsonMapArray (List json) { - this.nativeArray = json; - } - - @Override - public Object getValue() { - return nativeArray; - } - - @Override - public int size() { - return nativeArray.size(); - } - - @Override - public JsonLikeValue get(int index) { - Integer key = Integer.valueOf(index); - JsonLikeValue result = null; - if (jsonLikeMap.containsKey(key)) { - result = jsonLikeMap.get(key); - } else { - Object child = nativeArray.get(index); - if (child != null) { - result = new JsonMapValue(child); - } - jsonLikeMap.put(key, result); - } - return result; - } - } - - private class JsonMapValue extends JsonLikeValue { - private Object nativeValue; - private JsonLikeObject jsonLikeObject = null; - private JsonLikeArray jsonLikeArray = null; - - public JsonMapValue (Object json) { - this.nativeValue = json; - } - - @Override - public Object getValue() { - return nativeValue; - } - - @Override - public ValueType getJsonType() { - if (isNull()) { - return ValueType.NULL; - } - if (isObject()) { - return ValueType.OBJECT; - } - if (isArray()) { - return ValueType.ARRAY; - } - return ValueType.SCALAR; - } - - @Override - public ScalarType getDataType() { - if (isString()) { - return ScalarType.STRING; - } - if (isNumber()) { - return ScalarType.NUMBER; - } - if (isBoolean()) { - return ScalarType.BOOLEAN; - } - return null; - } - - @SuppressWarnings("unchecked") - @Override - public JsonLikeArray getAsArray() { - if (nativeValue != null && isArray()) { - if (null == jsonLikeArray) { - jsonLikeArray = new JsonMapArray((List)nativeValue); - } - } - return jsonLikeArray; - } - - @SuppressWarnings("unchecked") - @Override - public JsonLikeObject getAsObject() { - if (nativeValue != null && isObject()) { - if (null == jsonLikeObject) { - jsonLikeObject = new JsonMapObject((Map)nativeValue); - } - } - return jsonLikeObject; - } - - @Override - public String getAsString() { - String result = null; - if (nativeValue != null) { - result = nativeValue.toString(); - } - return result; - } - - @Override - public boolean getAsBoolean() { - if (nativeValue != null && isBoolean()) { - return ((Boolean)nativeValue).booleanValue(); - } - return super.getAsBoolean(); - } - - public boolean isObject () { - return (nativeValue != null) - && ( (nativeValue instanceof Map) || Map.class.isAssignableFrom(nativeValue.getClass()) ); - } - - public boolean isArray () { - return (nativeValue != null) - && ( (nativeValue instanceof List) || List.class.isAssignableFrom(nativeValue.getClass())); - } - - public boolean isString () { - return (nativeValue != null) - && ( (nativeValue instanceof String) || String.class.isAssignableFrom(nativeValue.getClass())); - } - - public boolean isNumber () { - return (nativeValue != null) - && ( (nativeValue instanceof Number) || Number.class.isAssignableFrom(nativeValue.getClass()) ); - } - - public boolean isBoolean () { - return (nativeValue != null) - && ( (nativeValue instanceof Boolean) || Boolean.class.isAssignableFrom(nativeValue.getClass()) ); - } - - public boolean isNull () { - return (null == nativeValue); - } - } - } -} +package ca.uhn.fhir.parser.jsonlike; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Extension; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.DataFormatException; +import ca.uhn.fhir.parser.IJsonLikeParser; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.parser.json.GsonStructure; +import ca.uhn.fhir.parser.json.JsonLikeArray; +import ca.uhn.fhir.parser.json.JsonLikeObject; +import ca.uhn.fhir.parser.json.JsonLikeStructure; +import ca.uhn.fhir.parser.json.JsonLikeValue; +import ca.uhn.fhir.parser.json.JsonLikeWriter; +import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType; +import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType; +import ca.uhn.fhir.parser.view.ExtPatient; +import ca.uhn.fhir.util.TestUtil; + +public class JsonLikeParserTest { + private static FhirContext ourCtx = FhirContext.forR4(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonLikeParserTest.class); + + /** + * Test for JSON Parser with user-supplied JSON-like structure (use default GSON) + */ + @Test + public void testJsonLikeParseAndEncodeResourceFromXmlToJson() throws Exception { + String content = IOUtils.toString(JsonLikeParserTest.class.getResourceAsStream("/extension-on-line.txt")); + + IBaseResource parsed = ourCtx.newJsonParser().parseResource(content); + + String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed); + ourLog.info(encoded); + + JsonLikeStructure jsonLikeStructure = new GsonStructure(); + jsonLikeStructure.load(new StringReader(encoded)); + + IJsonLikeParser jsonLikeparser = (IJsonLikeParser)ourCtx.newJsonParser(); + + IBaseResource resource = jsonLikeparser.parseResource(jsonLikeStructure); + Assert.assertEquals("reparsed resource classes not equal", parsed.getClass().getName(), resource.getClass().getName()); + } + + /** + * Test JSON-Like writer using custom stream writer + * + */ + @Test + public void testJsonLikeParseWithCustomJSONStreamWriter() throws Exception { + String refVal = "http://my.org/FooBar"; + + Patient fhirPat = new Patient(); + fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal)); + + IJsonLikeParser jsonLikeParser = (IJsonLikeParser)ourCtx.newJsonParser(); + JsonLikeMapWriter jsonLikeWriter = new JsonLikeMapWriter(); + + jsonLikeParser.encodeResourceToJsonLikeWriter(fhirPat, jsonLikeWriter); + Map jsonLikeMap = jsonLikeWriter.getResultMap(); + + System.out.println("encoded map: " + jsonLikeMap.toString()); + + Assert.assertNotNull("Encoded resource missing 'resourceType' element", jsonLikeMap.get("resourceType")); + Assert.assertEquals("Expecting 'resourceType'='Patient'; found '"+jsonLikeMap.get("resourceType")+"'", jsonLikeMap.get("resourceType"), "Patient"); + + Assert.assertNotNull("Encoded resource missing 'extension' element", jsonLikeMap.get("extension")); + Assert.assertTrue("'extension' element is not a List", (jsonLikeMap.get("extension") instanceof List)); + + List extensions = (List)jsonLikeMap.get("extension"); + Assert.assertEquals("'extnesion' array has more than one entry", 1, extensions.size()); + Assert.assertTrue("'extension' array entry is not a Map", (extensions.get(0) instanceof Map)); + + Map extension = (Map)extensions.get(0); + Assert.assertNotNull("'extension' entry missing 'url' member", extension.get("url")); + Assert.assertTrue("'extension' entry 'url' member is not a String", (extension.get("url") instanceof String)); + Assert.assertEquals("Expecting '/extension[]/url' = 'x1'; found '"+extension.get("url")+"'", "x1", (String)extension.get("url")); + + } + + /** + * Repeat the "View" tests with custom JSON-Like structure + */ + @Test + public void testViewJson() throws Exception { + + ExtPatient src = new ExtPatient(); + src.addIdentifier().setSystem("urn:sys").setValue("id1"); + src.addIdentifier().setSystem("urn:sys").setValue("id2"); + src.getExt().setValue(100); + src.getModExt().setValue(200); + + IJsonLikeParser jsonLikeParser = (IJsonLikeParser)ourCtx.newJsonParser(); + JsonLikeMapWriter jsonLikeWriter = new JsonLikeMapWriter(); + jsonLikeParser.encodeResourceToJsonLikeWriter(src, jsonLikeWriter); + Map jsonLikeMap = jsonLikeWriter.getResultMap(); + + + ourLog.info("encoded: "+jsonLikeMap); + + JsonLikeStructure jsonStructure = new JsonLikeMapStructure(jsonLikeMap); + IJsonLikeParser parser = (IJsonLikeParser)ourCtx.newJsonParser(); + Patient nonExt = parser.parseResource(Patient.class, jsonStructure); + + Assert.assertEquals(Patient.class, nonExt.getClass()); + Assert.assertEquals("urn:sys", nonExt.getIdentifier().get(0).getSystem()); + Assert.assertEquals("id1", nonExt.getIdentifier().get(0).getValue()); + Assert.assertEquals("urn:sys", nonExt.getIdentifier().get(1).getSystem()); + Assert.assertEquals("id2", nonExt.getIdentifier().get(1).getValue()); + + List ext = nonExt.getExtensionsByUrl("urn:ext"); + Assert.assertEquals(1, ext.size()); + Assert.assertEquals("urn:ext", ext.get(0).getUrl()); + Assert.assertEquals(IntegerType.class, ext.get(0).getValueAsPrimitive().getClass()); + Assert.assertEquals("100", ext.get(0).getValueAsPrimitive().getValueAsString()); + + List modExt = nonExt.getExtensionsByUrl("urn:modExt"); + Assert.assertEquals(1, modExt.size()); + Assert.assertEquals("urn:modExt", modExt.get(0).getUrl()); + Assert.assertEquals(IntegerType.class, modExt.get(0).getValueAsPrimitive().getClass()); + Assert.assertEquals("200", modExt.get(0).getValueAsPrimitive().getValueAsString()); + + ExtPatient va = ourCtx.newViewGenerator().newView(nonExt, ExtPatient.class); + Assert.assertEquals("urn:sys", va.getIdentifier().get(0).getSystem()); + Assert.assertEquals("id1", va.getIdentifier().get(0).getValue()); + Assert.assertEquals("urn:sys", va.getIdentifier().get(1).getSystem()); + Assert.assertEquals("id2", va.getIdentifier().get(1).getValue()); + Assert.assertEquals(100, va.getExt().getValue().intValue()); + Assert.assertEquals(200, va.getModExt().getValue().intValue()); + + Assert.assertEquals(0, va.getExtension().size()); + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + + + public static class JsonLikeMapWriter extends JsonLikeWriter { + + private Map target; + + private static class Block { + private BlockType type; + private String name; + private Map object; + private List array; + public Block(BlockType type) { + this.type = type; + } + public BlockType getType() { + return type; + } + public String getName() { + return name; + } + public void setName(String currentName) { + this.name = currentName; + } + public Map getObject() { + return object; + } + public void setObject(Map currentObject) { + this.object = currentObject; + } + public List getArray() { + return array; + } + public void setArray(List currentArray) { + this.array = currentArray; + } + } + private enum BlockType { + NONE, OBJECT, ARRAY + } + private Block currentBlock = new Block(BlockType.NONE); + private Stack blockStack = new Stack(); + + public JsonLikeMapWriter () { + super(); + } + + public Map getResultMap() { + return target; + } + public void setResultMap(Map target) { + this.target = target; + } + + @Override + public JsonLikeWriter init() throws IOException { + if (target != null) { + target.clear(); + } + currentBlock = new Block(BlockType.NONE); + blockStack.clear(); + return this; + } + + @Override + public JsonLikeWriter flush() throws IOException { + if (currentBlock.getType() != BlockType.NONE) { + throw new IOException("JsonLikeStreamWriter.flush() called but JSON document is not finished"); + } + return this; + } + + @Override + public void close() { + // nothing to do + } + + @Override + public JsonLikeWriter beginObject() throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + Map newObject = null; + if (currentBlock.getType() == BlockType.NONE) { + if (null == target) { + // for this test, we don't care about ordering of map elements + // target = new EntryOrderedMap(); + target = new HashMap(); + } + newObject = target; + } else { + // for this test, we don't care about ordering of map elements + // newObject = new EntryOrderedMap(); + newObject = new HashMap(); + } + blockStack.push(currentBlock); + currentBlock = new Block(BlockType.OBJECT); + currentBlock.setObject(newObject); + return this; + } + + @Override + public JsonLikeWriter beginArray() throws IOException { + if (currentBlock.getType() == BlockType.NONE) { + throw new IOException("JsonLikeStreamWriter.beginArray() called but only beginObject() is allowed here."); + } + blockStack.push(currentBlock); + currentBlock = new Block(BlockType.ARRAY); + currentBlock.setArray(new ArrayList()); + return this; + } + + @Override + public JsonLikeWriter beginObject(String name) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + blockStack.push(currentBlock); + currentBlock = new Block(BlockType.OBJECT); + currentBlock.setName(name); + // for this test, we don't care about ordering of map elements + // currentBlock.setObject(new EntryOrderedMap()); + currentBlock.setObject(new HashMap()); + return this; + } + + @Override + public JsonLikeWriter beginArray(String name) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + blockStack.push(currentBlock); + currentBlock = new Block(BlockType.ARRAY); + currentBlock.setName(name); + currentBlock.setArray(new ArrayList()); + return this; + } + + @Override + public JsonLikeWriter write(String value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(value); + return this; + } + + @Override + public JsonLikeWriter write(BigInteger value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(value); + return this; + } + + @Override + public JsonLikeWriter write(BigDecimal value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(value); + return this; + } + + @Override + public JsonLikeWriter write(long value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(Long.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter write(double value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(Double.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter write(Boolean value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(value); + return this; + } + + @Override + public JsonLikeWriter write(boolean value) throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(Boolean.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter writeNull() throws IOException { + if (currentBlock.getType() == BlockType.OBJECT) { + throw new IOException("Unnamed JSON elements can only be created in JSON arrays"); + } + currentBlock.getArray().add(null); + return this; + } + + @Override + public JsonLikeWriter write(String name, String value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, value); + return this; + } + + @Override + public JsonLikeWriter write(String name, BigInteger value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, value); + return this; + } + @Override + public JsonLikeWriter write(String name, BigDecimal value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, value); + return this; + } + + @Override + public JsonLikeWriter write(String name, long value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, Long.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter write(String name, double value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, Double.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter write(String name, Boolean value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, value); + return this; + } + + @Override + public JsonLikeWriter write(String name, boolean value) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, Boolean.valueOf(value)); + return this; + } + + @Override + public JsonLikeWriter writeNull(String name) throws IOException { + if (currentBlock.getType() == BlockType.ARRAY) { + throw new IOException("Named JSON elements can only be created in JSON objects"); + } + currentBlock.getObject().put(name, null); + return this; + } + + @Override + public JsonLikeWriter endObject() throws IOException { + if (currentBlock.getType() == BlockType.NONE) { + ourLog.error("JsonLikeStreamWriter.endObject(); called with no active JSON document"); + } else { + if (currentBlock.getType() != BlockType.OBJECT) { + ourLog.error("JsonLikeStreamWriter.endObject(); called outside a JSON object. (Use endArray() instead?)"); + } + endBlock(); + } + return this; + } + + @Override + public JsonLikeWriter endArray() throws IOException { + if (currentBlock.getType() == BlockType.NONE) { + ourLog.error("JsonLikeStreamWriter.endArray(); called with no active JSON document"); + } else { + if (currentBlock.getType() != BlockType.ARRAY) { + ourLog.error("JsonLikeStreamWriter.endArray(); called outside a JSON array. (Use endObject() instead?)"); + } + endBlock(); + } + return this; + } + + @Override + public JsonLikeWriter endBlock() throws IOException { + if (currentBlock.getType() == BlockType.NONE) { + ourLog.error("JsonLikeStreamWriter.endBlock(); called with no active JSON document"); + } else { + Object toPut = null; + if (currentBlock.getType() == BlockType.ARRAY) { + toPut = currentBlock.getArray(); + } else { + toPut = currentBlock.getObject(); + } + Block parentBlock = blockStack.pop(); + if (parentBlock.getType() == BlockType.OBJECT) { + parentBlock.getObject().put(currentBlock.getName(), toPut); + } else + if (parentBlock.getType() == BlockType.ARRAY) { + parentBlock.getArray().add(toPut); + } + currentBlock = parentBlock; + } + return this; + } + + } + + public static class JsonLikeMapStructure implements JsonLikeStructure { + + private Map nativeObject; + private JsonLikeObject jsonLikeObject = null; + private JsonLikeMapWriter jsonLikeWriter = null; + + public JsonLikeMapStructure() { + super(); + } + + public JsonLikeMapStructure (Map json) { + super(); + setNativeObject(json); + } + + public void setNativeObject (Map json) { + this.nativeObject = json; + } + + @Override + public JsonLikeStructure getInstance() { + return new JsonLikeMapStructure(); + } + + @Override + public JsonLikeWriter getJsonLikeWriter (Writer ignored) { + return getJsonLikeWriter(); + } + + @Override + public JsonLikeWriter getJsonLikeWriter () { + if (null == jsonLikeWriter) { + jsonLikeWriter = new JsonLikeMapWriter(); + } + return jsonLikeWriter; + } + + @Override + public void load(Reader reader) throws DataFormatException { + this.load(reader, true); + } + + @Override + public void load(Reader theReader, boolean allowArray) throws DataFormatException { + throw new DataFormatException("JSON structure loading is not supported for native Java Map structures"); + } + + @Override + public JsonLikeObject getRootObject() { + if (null == jsonLikeObject) { + jsonLikeObject = new JsonMapObject(nativeObject); + } + return jsonLikeObject; + } + + @Override + public JsonLikeArray getRootArray() throws DataFormatException { + throw new DataFormatException("JSON document must be an object not an array for native Java Map structures"); + } + + private class JsonMapObject extends JsonLikeObject { + private Map nativeObject; + private Map jsonLikeMap = new LinkedHashMap(); + + public JsonMapObject (Map json) { + this.nativeObject = json; + } + + @Override + public Object getValue() { + return nativeObject; + } + + @Override + public Set keySet() { + return nativeObject.keySet(); + } + + @Override + public JsonLikeValue get(String key) { + JsonLikeValue result = null; + if (jsonLikeMap.containsKey(key)) { + result = jsonLikeMap.get(key); + } else { + Object child = nativeObject.get(key); + if (child != null) { + result = new JsonMapValue(child); + } + jsonLikeMap.put(key, result); + } + return result; + } + } + + private class JsonMapArray extends JsonLikeArray { + private List nativeArray; + private Map jsonLikeMap = new LinkedHashMap(); + + public JsonMapArray (List json) { + this.nativeArray = json; + } + + @Override + public Object getValue() { + return nativeArray; + } + + @Override + public int size() { + return nativeArray.size(); + } + + @Override + public JsonLikeValue get(int index) { + Integer key = Integer.valueOf(index); + JsonLikeValue result = null; + if (jsonLikeMap.containsKey(key)) { + result = jsonLikeMap.get(key); + } else { + Object child = nativeArray.get(index); + if (child != null) { + result = new JsonMapValue(child); + } + jsonLikeMap.put(key, result); + } + return result; + } + } + + private class JsonMapValue extends JsonLikeValue { + private Object nativeValue; + private JsonLikeObject jsonLikeObject = null; + private JsonLikeArray jsonLikeArray = null; + + public JsonMapValue (Object json) { + this.nativeValue = json; + } + + @Override + public Object getValue() { + return nativeValue; + } + + @Override + public ValueType getJsonType() { + if (isNull()) { + return ValueType.NULL; + } + if (isObject()) { + return ValueType.OBJECT; + } + if (isArray()) { + return ValueType.ARRAY; + } + return ValueType.SCALAR; + } + + @Override + public ScalarType getDataType() { + if (isString()) { + return ScalarType.STRING; + } + if (isNumber()) { + return ScalarType.NUMBER; + } + if (isBoolean()) { + return ScalarType.BOOLEAN; + } + return null; + } + + @SuppressWarnings("unchecked") + @Override + public JsonLikeArray getAsArray() { + if (nativeValue != null && isArray()) { + if (null == jsonLikeArray) { + jsonLikeArray = new JsonMapArray((List)nativeValue); + } + } + return jsonLikeArray; + } + + @SuppressWarnings("unchecked") + @Override + public JsonLikeObject getAsObject() { + if (nativeValue != null && isObject()) { + if (null == jsonLikeObject) { + jsonLikeObject = new JsonMapObject((Map)nativeValue); + } + } + return jsonLikeObject; + } + + @Override + public String getAsString() { + String result = null; + if (nativeValue != null) { + result = nativeValue.toString(); + } + return result; + } + + @Override + public boolean getAsBoolean() { + if (nativeValue != null && isBoolean()) { + return ((Boolean)nativeValue).booleanValue(); + } + return super.getAsBoolean(); + } + + @Override + public boolean isObject () { + return (nativeValue != null) + && ( (nativeValue instanceof Map) || Map.class.isAssignableFrom(nativeValue.getClass()) ); + } + + @Override + public boolean isArray () { + return (nativeValue != null) + && ( (nativeValue instanceof List) || List.class.isAssignableFrom(nativeValue.getClass())); + } + + @Override + public boolean isString () { + return (nativeValue != null) + && ( (nativeValue instanceof String) || String.class.isAssignableFrom(nativeValue.getClass())); + } + + @Override + public boolean isNumber () { + return (nativeValue != null) + && ( (nativeValue instanceof Number) || Number.class.isAssignableFrom(nativeValue.getClass()) ); + } + + public boolean isBoolean () { + return (nativeValue != null) + && ( (nativeValue instanceof Boolean) || Boolean.class.isAssignableFrom(nativeValue.getClass()) ); + } + + @Override + public boolean isNull () { + return (null == nativeValue); + } + } + } +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java index ce5e31c524c..1b602b020b3 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java @@ -787,6 +787,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return Utilities.appendSlash(base) + type + "/" + id; } + @Override public BestPracticeWarningLevel getBasePracticeWarningLevel() { return bpWarnings; } @@ -941,10 +942,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + type); } + @Override public IdStatus getResourceIdRule() { return resourceIdRule; } + @Override public void setResourceIdRule(IdStatus resourceIdRule) { this.resourceIdRule = resourceIdRule; } @@ -1209,6 +1212,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return sd.getSnapshot().getElement().get(0); } + @Override public void setBestPracticeWarningLevel(BestPracticeWarningLevel value) { bpWarnings = value; } @@ -2071,6 +2075,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat this.typeProfile = typeProfile; } + @Override public boolean equalsDeep(Base other) { if (!super.equalsDeep(other) || !fhirType().equals(other.fhirType())) return false; @@ -2153,6 +2158,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; } + @Override public boolean isPrimitive() { String t = fhirType(); return t.equalsIgnoreCase("boolean") || t.equalsIgnoreCase("integer") || t.equalsIgnoreCase("string") || t.equalsIgnoreCase("decimal") || t.equalsIgnoreCase("uri") || t.equalsIgnoreCase("base64Binary") || @@ -2188,6 +2194,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return list; } + @Override public String primitiveValue() { return wrapper.getAttribute("value"); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/ProfileValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/ProfileValidator.java index c8ef973d36d..d54770d7444 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/ProfileValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/validation/ProfileValidator.java @@ -23,6 +23,7 @@ public class ProfileValidator extends BaseValidator { this.context = context; } + @Override protected boolean rule(List errors, IssueType type, String path, boolean b, String msg) { String rn = path.contains(".") ? path.substring(0, path.indexOf(".")) : path; return super.rule(errors, type, path, b, msg, ""+rn+": "+Utilities.escapeXml(msg)); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/CodingDt.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/CodingDt.java index 2cd41a2461e..f0c7432bca7 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/CodingDt.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/CodingDt.java @@ -126,7 +126,8 @@ public class CodingDt * The identification of the code system that defines the meaning of the symbol in the code. *

*/ - public UriDt getSystemElement() { + @Override + public UriDt getSystemElement() { if (mySystem == null) { mySystem = new UriDt(); } @@ -144,7 +145,8 @@ public class CodingDt * The identification of the code system that defines the meaning of the symbol in the code. *

*/ - public String getSystem() { + @Override + public String getSystem() { return getSystemElement().getValue(); } @@ -171,7 +173,8 @@ public class CodingDt * The identification of the code system that defines the meaning of the symbol in the code. *

*/ - public CodingDt setSystem( String theUri) { + @Override + public CodingDt setSystem(String theUri) { mySystem = new UriDt(theUri); return this; } @@ -248,7 +251,8 @@ public class CodingDt * A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post-coordination) *

*/ - public CodeDt getCodeElement() { + @Override + public CodeDt getCodeElement() { if (myCode == null) { myCode = new CodeDt(); } @@ -266,7 +270,8 @@ public class CodingDt * A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post-coordination) *

*/ - public String getCode() { + @Override + public String getCode() { return getCodeElement().getValue(); } @@ -293,7 +298,8 @@ public class CodingDt * A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post-coordination) *

*/ - public CodingDt setCode( String theCode) { + @Override + public CodingDt setCode(String theCode) { myCode = new CodeDt(theCode); return this; } @@ -309,7 +315,8 @@ public class CodingDt * A representation of the meaning of the code in the system, following the rules of the system *

*/ - public StringDt getDisplayElement() { + @Override + public StringDt getDisplayElement() { if (myDisplay == null) { myDisplay = new StringDt(); } @@ -327,7 +334,8 @@ public class CodingDt * A representation of the meaning of the code in the system, following the rules of the system *

*/ - public String getDisplay() { + @Override + public String getDisplay() { return getDisplayElement().getValue(); } @@ -354,7 +362,8 @@ public class CodingDt * A representation of the meaning of the code in the system, following the rules of the system *

*/ - public CodingDt setDisplay( String theString) { + @Override + public CodingDt setDisplay(String theString) { myDisplay = new StringDt(theString); return this; } @@ -423,4 +432,4 @@ public class CodingDt -} \ No newline at end of file +} diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/NarrativeDt.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/NarrativeDt.java index 036f0a75e99..a0dc4c371d6 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/NarrativeDt.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/NarrativeDt.java @@ -104,7 +104,8 @@ public class NarrativeDt extends BaseNarrativeDt { * The actual narrative content, a stripped down version of XHTML *

*/ - public XhtmlDt getDiv() { + @Override + public XhtmlDt getDiv() { if (myDiv == null) { myDiv = new XhtmlDt(); } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/QuantityDt.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/QuantityDt.java index bfcb6dc9d6d..b9222b542a9 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/QuantityDt.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/QuantityDt.java @@ -193,7 +193,8 @@ public class QuantityDt * The value of the measured amount. The value includes an implicit precision in the presentation of the value *

*/ - public DecimalDt getValueElement() { + @Override + public DecimalDt getValueElement() { if (myValue == null) { myValue = new DecimalDt(); } @@ -264,7 +265,8 @@ public class QuantityDt * The value of the measured amount. The value includes an implicit precision in the presentation of the value *

*/ - public QuantityDt setValue( java.math.BigDecimal theValue) { + @Override + public QuantityDt setValue(java.math.BigDecimal theValue) { myValue = new DecimalDt(theValue); return this; } @@ -280,7 +282,8 @@ public class QuantityDt * How the value should be understood and represented - whether the actual value is greater or less than the stated value due to measurement issues; e.g. if the comparator is \"<\" , then the real value is < stated value *

*/ - public BoundCodeDt getComparatorElement() { + @Override + public BoundCodeDt getComparatorElement() { if (myComparator == null) { myComparator = new BoundCodeDt(QuantityComparatorEnum.VALUESET_BINDER); } @@ -406,7 +409,8 @@ public class QuantityDt * The identification of the system that provides the coded form of the unit *

*/ - public UriDt getSystemElement() { + @Override + public UriDt getSystemElement() { if (mySystem == null) { mySystem = new UriDt(); } @@ -451,7 +455,8 @@ public class QuantityDt * The identification of the system that provides the coded form of the unit *

*/ - public QuantityDt setSystem( String theUri) { + @Override + public QuantityDt setSystem(String theUri) { mySystem = new UriDt(theUri); return this; } @@ -467,7 +472,8 @@ public class QuantityDt * A computer processable form of the unit in some unit representation system *

*/ - public CodeDt getCodeElement() { + @Override + public CodeDt getCodeElement() { if (myCode == null) { myCode = new CodeDt(); } @@ -512,7 +518,8 @@ public class QuantityDt * A computer processable form of the unit in some unit representation system *

*/ - public QuantityDt setCode( String theCode) { + @Override + public QuantityDt setCode(String theCode) { myCode = new CodeDt(theCode); return this; } @@ -520,4 +527,4 @@ public class QuantityDt -} \ No newline at end of file +} diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/ResourceReferenceDt.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/ResourceReferenceDt.java index 0f28995cf43..6446a7d2952 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/ResourceReferenceDt.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu3/composite/ResourceReferenceDt.java @@ -162,7 +162,8 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 IdDt getReference() { + @Override + public IdDt getReference() { if (myReference == null) { myReference = new IdDt(); } @@ -183,6 +184,7 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 ResourceReferenceDt setReference(IdDt theValue) { myReference = theValue; return this; @@ -196,7 +198,8 @@ public class ResourceReferenceDt * A reference to a location at which the other resource is found. The reference may 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 ResourceReferenceDt setReference( String theId) { + @Override + public ResourceReferenceDt setReference(String theId) { myReference = new IdDt(theId); return this; } @@ -240,7 +243,8 @@ public class ResourceReferenceDt * Plain text narrative that identifies the resource in addition to the resource reference *

*/ - public ResourceReferenceDt setDisplay( String theString) { + @Override + public ResourceReferenceDt setDisplay(String theString) { myDisplay = new StringDt(theString); return this; } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ResourceBlock.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ResourceBlock.java index a3deb3bd79b..857a6c37444 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ResourceBlock.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/model/ResourceBlock.java @@ -59,6 +59,7 @@ public class ResourceBlock extends Child { return getClassName(); } + @Override public boolean isBlock() { return true; } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/util/XMLUtils.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/util/XMLUtils.java index f6b558a3767..c5273731245 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/util/XMLUtils.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/util/XMLUtils.java @@ -1,130 +1,131 @@ - -package ca.uhn.fhir.tinder.util; - -import java.io.InputStream; -import java.io.StringWriter; -import java.io.Writer; - -import org.w3c.dom.DOMConfiguration; -import org.w3c.dom.DOMErrorHandler; -import org.w3c.dom.DOMImplementation; -import org.w3c.dom.Document; -import org.w3c.dom.bootstrap.DOMImplementationRegistry; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSInput; -import org.w3c.dom.ls.LSOutput; -import org.w3c.dom.ls.LSParser; -import org.w3c.dom.ls.LSResourceResolver; -import org.w3c.dom.ls.LSSerializer; - -public class XMLUtils { - - private static DOMImplementation IMPL; - - @SuppressWarnings("unchecked") - public synchronized static T getDOMImpl() { - if (IMPL == null) { - try { - DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); - IMPL = registry.getDOMImplementation("LS 3.0"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return (T) IMPL; - } - - @SuppressWarnings("unchecked") - public static T getDOMImplUncached() { - try { - DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); - return (T) registry.getDOMImplementation("LS 3.0"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static Document parse(String s) { - return parse(s, false); - } - - public static Document parse(String s, boolean validateIfSchema) { - DOMImplementationLS impl = getDOMImpl(); - LSInput input = impl.createLSInput(); - input.setStringData(s); - return parse(input, validateIfSchema); - } - - public static Document parse(InputStream s, boolean validateIfSchema) { - DOMImplementationLS impl = getDOMImpl(); - LSInput input = impl.createLSInput(); - input.setByteStream(s); - return parse(input, validateIfSchema); - } - - private static Document parse(LSInput input, boolean validateIfSchema) { - DOMImplementationLS impl = getDOMImpl(); - LSParser parser = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); - DOMConfiguration config = parser.getDomConfig(); - config.setParameter("element-content-whitespace", false); - config.setParameter("namespaces", true); - config.setParameter("validate-if-schema", validateIfSchema); - return parser.parse(input); - } - - public static void validate(Document d, String schema, DOMErrorHandler handler) { - DOMConfiguration config = d.getDomConfig(); - config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema"); - config.setParameter("validate", true); - config.setParameter("schema-location", schema); - config.setParameter("resource-resolver", new ClasspathResourceResolver()); - config.setParameter("error-handler", handler); - d.normalizeDocument(); - } - - public static String serialize(Document document, boolean prettyPrint) { - DOMImplementationLS impl = getDOMImpl(); - LSSerializer serializer = impl.createLSSerializer(); - // document.normalizeDocument(); - DOMConfiguration config = serializer.getDomConfig(); - if (prettyPrint && config.canSetParameter("format-pretty-print", Boolean.TRUE)) { - config.setParameter("format-pretty-print", true); - } - config.setParameter("xml-declaration", true); - LSOutput output = impl.createLSOutput(); - output.setEncoding("UTF-8"); - Writer writer = new StringWriter(); - output.setCharacterStream(writer); - serializer.write(document, output); - return writer.toString(); - } - - public static Document emptyDocument(String title) { - DOMImplementation impl = getDOMImpl(); - Document doc = impl.createDocument("urn:hl7-org:v2xml", title, null); - return doc; - } - - /** - * This is an implementation of LSResourceResolver that can resolve XML schemas from the - * classpath - */ - private static class ClasspathResourceResolver implements LSResourceResolver { - private DOMImplementationLS impl; - - ClasspathResourceResolver() { - impl = getDOMImpl(); - } - - public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, - String baseURI) { - LSInput lsInput = impl.createLSInput(); - InputStream is = getClass().getResourceAsStream("/" + systemId); - if (is == null) - return null; - lsInput.setByteStream(is); - return lsInput; - } - } - -} + +package ca.uhn.fhir.tinder.util; + +import java.io.InputStream; +import java.io.StringWriter; +import java.io.Writer; + +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMErrorHandler; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSParser; +import org.w3c.dom.ls.LSResourceResolver; +import org.w3c.dom.ls.LSSerializer; + +public class XMLUtils { + + private static DOMImplementation IMPL; + + @SuppressWarnings("unchecked") + public synchronized static T getDOMImpl() { + if (IMPL == null) { + try { + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + IMPL = registry.getDOMImplementation("LS 3.0"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return (T) IMPL; + } + + @SuppressWarnings("unchecked") + public static T getDOMImplUncached() { + try { + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + return (T) registry.getDOMImplementation("LS 3.0"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Document parse(String s) { + return parse(s, false); + } + + public static Document parse(String s, boolean validateIfSchema) { + DOMImplementationLS impl = getDOMImpl(); + LSInput input = impl.createLSInput(); + input.setStringData(s); + return parse(input, validateIfSchema); + } + + public static Document parse(InputStream s, boolean validateIfSchema) { + DOMImplementationLS impl = getDOMImpl(); + LSInput input = impl.createLSInput(); + input.setByteStream(s); + return parse(input, validateIfSchema); + } + + private static Document parse(LSInput input, boolean validateIfSchema) { + DOMImplementationLS impl = getDOMImpl(); + LSParser parser = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration config = parser.getDomConfig(); + config.setParameter("element-content-whitespace", false); + config.setParameter("namespaces", true); + config.setParameter("validate-if-schema", validateIfSchema); + return parser.parse(input); + } + + public static void validate(Document d, String schema, DOMErrorHandler handler) { + DOMConfiguration config = d.getDomConfig(); + config.setParameter("schema-type", "http://www.w3.org/2001/XMLSchema"); + config.setParameter("validate", true); + config.setParameter("schema-location", schema); + config.setParameter("resource-resolver", new ClasspathResourceResolver()); + config.setParameter("error-handler", handler); + d.normalizeDocument(); + } + + public static String serialize(Document document, boolean prettyPrint) { + DOMImplementationLS impl = getDOMImpl(); + LSSerializer serializer = impl.createLSSerializer(); + // document.normalizeDocument(); + DOMConfiguration config = serializer.getDomConfig(); + if (prettyPrint && config.canSetParameter("format-pretty-print", Boolean.TRUE)) { + config.setParameter("format-pretty-print", true); + } + config.setParameter("xml-declaration", true); + LSOutput output = impl.createLSOutput(); + output.setEncoding("UTF-8"); + Writer writer = new StringWriter(); + output.setCharacterStream(writer); + serializer.write(document, output); + return writer.toString(); + } + + public static Document emptyDocument(String title) { + DOMImplementation impl = getDOMImpl(); + Document doc = impl.createDocument("urn:hl7-org:v2xml", title, null); + return doc; + } + + /** + * This is an implementation of LSResourceResolver that can resolve XML schemas from the + * classpath + */ + private static class ClasspathResourceResolver implements LSResourceResolver { + private DOMImplementationLS impl; + + ClasspathResourceResolver() { + impl = getDOMImpl(); + } + + @Override + public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, + String baseURI) { + LSInput lsInput = impl.createLSInput(); + InputStream is = getClass().getResourceAsStream("/" + systemId); + if (is == null) + return null; + lsInput.setByteStream(is); + return lsInput; + } + } + +} diff --git a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java index 4bcc816756e..e0d19110aae 100644 --- a/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java +++ b/hapi-tinder-plugin/src/main/java/org/hl7/fhir/instance/model/IIdType.java @@ -24,6 +24,7 @@ package org.hl7.fhir.dstu2.model; public interface IIdType extends IBase { + @Override boolean isEmpty(); /** From 45602c81355e6fa4a0e6041cc128e317a25b7091 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 2 Oct 2019 05:47:27 -0400 Subject: [PATCH 3/6] Credit for #1476 --- src/changes/changes.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 27af222dde2..4bda1a86db4 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -300,6 +300,10 @@ A docker compose script for the hapi-fhir-jpaserver-starter project was added. Thanks to Long Nguyen for the pull request! + + A number of overridden methods in the HAPI FHIR codebase did not have the + @Override annotation. Thanks to Clayton Bodendein for cleaning this up! + From 8e9b7b590c88c09bc4e1c2f77eaa78bc2596671e Mon Sep 17 00:00:00 2001 From: Petro Mykhaylyshyn Date: Tue, 9 Jul 2019 14:45:01 +0300 Subject: [PATCH 4/6] [(servermethodbinding)] Fix method binding to handle special search names(_id:[modifier], _language:[modifier]) --- .../server/method/SearchMethodBinding.java | 9 +- .../uhn/fhir/rest/server/SearchDstu2Test.java | 99 +++++++++++++++++-- 2 files changed, 101 insertions(+), 7 deletions(-) diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java index 066a7f751a7..55a98a8fba1 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java @@ -219,7 +219,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { } } for (String next : theRequest.getParameters().keySet()) { - if (next.startsWith("_") && !SPECIAL_SEARCH_PARAMS.contains(next)) { + if (next.startsWith("_") && !SPECIAL_SEARCH_PARAMS.contains(truncModifierPart(next))) { methodParamsTemp.add(next); } } @@ -235,6 +235,13 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { return true; } + private String truncModifierPart(String param) { + int indexOfSeparator = param.indexOf(":"); + if (indexOfSeparator != -1) { + return param.substring(0, indexOfSeparator); + } + return param; + } @Override public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index dbe6897d299..6febbfbdb52 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -7,10 +7,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.RequiredParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -41,6 +38,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -407,6 +405,57 @@ public class SearchDstu2Test { assertEquals("Patient", ourLastRef.getResourceType()); } + /** + * Verifies proper method binding to handle special search names(_id:[modifier], _language:[modifier]) + */ + @Test + public void testSearchByIdExact() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&reference=value"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("idProvider", ourLastMethod); + } + + @Test + public void testSearchByQualifiedIdQualifiedString() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam:exact=value"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("stringParam:true:true", ourLastMethod); + } + + @Test + public void testSearchByQualifiedString() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam:exact=value"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("stringParam:false:true", ourLastMethod); + } + + @Test + public void testSearchByQualifiedIdString() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam=value"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("stringParam:true:false", ourLastMethod); + } + @Test public void testSearchWhitelist01Failing() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref=value"); @@ -439,12 +488,13 @@ public class SearchDstu2Test { ourServer = new Server(0); DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + DummyPatientResourceNoIdProvider patientResourceNoIdProviderProvider = new DummyPatientResourceNoIdProvider(); ServletHandler proxyHandler = new ServletHandler(); ourServlet = new RestfulServer(ourCtx); ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setResourceProviders(patientProvider); + ourServlet.setResourceProviders(patientResourceNoIdProviderProvider, patientProvider); ServletHolder servletHolder = new ServletHolder(ourServlet); proxyHandler.addServletWithMapping(servletHolder, "/*"); @@ -459,6 +509,23 @@ public class SearchDstu2Test { } + public static class DummyPatientResourceNoIdProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + //@formatter:off + @Search() + public List searchByRef( + @RequiredParam(name = "reference") ReferenceParam theParam) { + ourLastMethod = "noIdProvider"; + return Collections.emptyList(); + } + //@formatter:on + } + public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -482,7 +549,27 @@ public class SearchDstu2Test { public MethodOutcome create(@ResourceParam Patient thePatient) { throw new UnsupportedOperationException(); } - + + //@formatter:off + @Search() + public List searchByIdRef( + @RequiredParam(name="_id") StringParam id, + @OptionalParam(name = "reference") ReferenceParam theParam) { + ourLastMethod = "idProvider"; + return Collections.emptyList(); + } + //@formatter:on + + //@formatter:off + @Search() + public List searchByQualifiedString( + @RequiredParam(name="_id") StringParam id, + @RequiredParam(name = "stringParam") StringParam stringParam) { + ourLastMethod = "stringParam:" + id.isExact() + ":" + stringParam.isExact(); + return Collections.emptyList(); + } + //@formatter:on + //@formatter:off @Search() public List searchDateAndList( From 04e280214fd525e6b6705ffa1c23ed2b08327a9c Mon Sep 17 00:00:00 2001 From: Petro Mykhaylyshyn Date: Wed, 10 Jul 2019 11:41:38 +0300 Subject: [PATCH 5/6] [(servermethodbinding)] Extend tests. --- .../ca/uhn/fhir/rest/server/SearchDstu2Test.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index 6febbfbdb52..4756f7bfa1f 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -456,6 +456,19 @@ public class SearchDstu2Test { assertEquals("stringParam:true:false", ourLastMethod); } + @Test + public void testSearchByIdString() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam=value"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("stringParam:false:false", ourLastMethod); + } + + @Test public void testSearchWhitelist01Failing() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref=value"); From 9172c7d3a4b62503bc2009bec5a2bf33e3a7abb2 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 2 Oct 2019 05:53:47 -0400 Subject: [PATCH 6/6] Credit for #1373 --- src/changes/changes.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4bda1a86db4..a7e3014b25f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -304,6 +304,12 @@ A number of overridden methods in the HAPI FHIR codebase did not have the @Override annotation. Thanks to Clayton Bodendein for cleaning this up! + + Plain server resource providers were not correctly matching methods that + had the _id search parameter if a client performed a request using a modifier + such as :not or :exact. Thanks to Petro Mykhailyshyn + for the pull request! +