diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 740a49f4005..c542cc2967e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -27,10 +27,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.text.WordUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -185,9 +183,12 @@ public class FhirContext { /** * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed * for extending the core library. + *

+ * Note that this method is case insensitive! + *

*/ public BaseRuntimeElementDefinition getElementDefinition(String theElementName) { - return myNameToElementDefinition.get(theElementName); + return myNameToElementDefinition.get(theElementName.toLowerCase()); } /** For unit tests only */ @@ -275,23 +276,15 @@ public class FhirContext { /** * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed * for extending the core library. + *

+ * Note that this method is case insensitive! + *

*/ @SuppressWarnings("unchecked") public RuntimeResourceDefinition getResourceDefinition(String theResourceName) { Validate.notBlank(theResourceName, "theResourceName must not be blank"); - String resourceName = theResourceName; - - /* - * TODO: this is a bit of a hack, really we should have a translation table based on a property file or - * something so that we can detect names like diagnosticreport - */ - if (Character.isLowerCase(resourceName.charAt(0))) { - resourceName = WordUtils.capitalize(resourceName); - } - - Validate.notBlank(resourceName, "Resource name must not be blank"); - + String resourceName = theResourceName.toLowerCase(); RuntimeResourceDefinition retVal = myNameToResourceDefinition.get(resourceName); if (retVal == null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 461abc41be1..51c904bd2b1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -319,7 +319,7 @@ class ModelScanner { resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass)); } myClassToElementDefinitions.put(theClass, resourceDef); - myNameToElementDefinitions.put(resourceDef.getName(), resourceDef); + myNameToElementDefinitions.put(resourceDef.getName().toLowerCase(), resourceDef); scanCompositeElementForChildren(theClass, resourceDef); } @@ -709,7 +709,7 @@ class ModelScanner { myClassToElementDefinitions.put(theClass, resourceDef); if (primaryNameProvider) { if (resourceDef.getStructureVersion() == myVersion) { - myNameToResourceDefinitions.put(resourceName, resourceDef); + myNameToResourceDefinitions.put(resourceName.toLowerCase(), resourceDef); } } scanCompositeElementForChildren(theClass, resourceDef); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 51f71c70c13..efe5504c589 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.parser; * #L% */ import static org.apache.commons.lang3.StringUtils.defaultIfBlank; +import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotEmpty; @@ -2090,6 +2091,9 @@ class ParserState { } RuntimeResourceDefinition def = (RuntimeResourceDefinition) definition; + if (!definition.getName().equals(theLocalPart) && definition.getName().equalsIgnoreCase(theLocalPart)) { + throw new DataFormatException("Unknown resource type '" + theLocalPart + "': Resource names are case sensitive, found similar name: '" + definition.getName() + "'"); + } myInstance = def.newInstance(); String resourceName = def.getName(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java index 1f19c8623da..2f6f7c76434 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/FhirContextDstu3Test.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.StructureDefinition; import org.junit.AfterClass; import org.junit.Test; @@ -22,7 +24,28 @@ public class FhirContextDstu3Test { public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } + + /** + * See #344 + */ + @Test + public void testGetElementDefinitionCaseInsensitive() { + assertEquals(Reference.class, ourCtx.getElementDefinition("reference").getImplementingClass()); + assertEquals(Reference.class, ourCtx.getElementDefinition("Reference").getImplementingClass()); + assertEquals(Reference.class, ourCtx.getElementDefinition("REFerence").getImplementingClass()); + } + /** + * See #344 + */ + @Test + public void testGetResourceDefinitionCaseInsensitive() { + assertEquals(Patient.class, ourCtx.getResourceDefinition("patient").getImplementingClass()); + assertEquals(Patient.class, ourCtx.getResourceDefinition("Patient").getImplementingClass()); + assertEquals(Patient.class, ourCtx.getResourceDefinition("PATient").getImplementingClass()); + assertEquals(StructureDefinition.class, ourCtx.getResourceDefinition("structuredefinition").getImplementingClass()); + } + @Test public void testCustomTypeDoesntBecomeDefault() { FhirContext ctx = FhirContext.forDstu3(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java index a26bf977348..b02ee0fb0de 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java @@ -57,6 +57,8 @@ import org.hl7.fhir.dstu3.model.PrimitiveType; import org.hl7.fhir.dstu3.model.Quantity; import org.hl7.fhir.dstu3.model.QuestionnaireResponse; import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.SampledData; +import org.hl7.fhir.dstu3.model.SimpleQuantity; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.Type; import org.hl7.fhir.dstu3.model.UriType; @@ -86,6 +88,41 @@ public class JsonParserDstu3Test { ourCtx.setNarrativeGenerator(null); } + /** + * See #344 + */ + @Test + public void testParserIsCaseSensitive() { + Observation obs = new Observation(); + SampledData data = new SampledData(); + data.setData("1 2 3"); + data.setOrigin((SimpleQuantity) new SimpleQuantity().setValue(0L)); + data.setPeriod(1000L); + obs.setValue(data); + + IParser p = ourCtx.newJsonParser().setPrettyPrint(true).setParserErrorHandler(new StrictErrorHandler()); + String encoded = p.encodeResourceToString(obs); + ourLog.info(encoded); + + p.parseResource(encoded); + + try { + p.parseResource(encoded.replace("Observation", "observation")); + fail(); + } catch (DataFormatException e) { + assertEquals("Unknown resource type 'observation': Resource names are case sensitive, found similar name: 'Observation'", e.getMessage()); + } + + try { + p.parseResource(encoded.replace("valueSampledData", "valueSampleddata")); + fail(); + } catch (DataFormatException e) { + assertEquals("Unknown element 'valueSampleddata' found during parse", e.getMessage()); + } + } + + + @Test public void testEncodeAndParseExtensions() throws Exception { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java index 9ab68cfbbd0..03544844afa 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java @@ -82,6 +82,7 @@ import org.hl7.fhir.dstu3.model.PrimitiveType; import org.hl7.fhir.dstu3.model.Quantity; import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.dstu3.model.SampledData; import org.hl7.fhir.dstu3.model.SimpleQuantity; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.UriType; @@ -114,27 +115,10 @@ public class XmlParserDstu3Test { ourCtx.setNarrativeGenerator(null); } - /** - * See #347 - */ - @Test - public void testEncodeAndParseMedicationOrder() { - MedicationOrder mo = new MedicationOrder(); - mo.getDateWrittenElement().setValueAsString("2015-10-05"); - - String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(mo); - ourLog.info(encoded); - - mo = ourCtx.newXmlParser().parseResource(MedicationOrder.class, encoded); - assertEquals("2015-10-05", mo.getDateWrittenElement().getValueAsString()); - } - - @Test public void testBundleWithBinary() { //@formatter:off String bundle = "\n" + - " \n" + " \n" + " \n" + " \n" + @@ -184,6 +168,8 @@ public class XmlParserDstu3Test { assertEquals("ORG", o.getName()); } + + @Test public void testDuration() { Encounter enc = new Encounter(); @@ -197,7 +183,7 @@ public class XmlParserDstu3Test { assertThat(str, not(containsString("meta"))); assertThat(str, containsString("")); } - + @Test public void testEncodeAndParseBundleWithResourceRefs() { @@ -234,7 +220,7 @@ public class XmlParserDstu3Test { assertEquals("Organization/orgid", pt.getManagingOrganization().getReferenceElement().getValue()); assertSame(org, pt.getManagingOrganization().getResource()); } - + @Test public void testEncodeAndParseContained() { IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true); @@ -463,6 +449,21 @@ public class XmlParserDstu3Test { assertEquals("MR", patient.getIdentifier().get(0).getType().getCoding().get(0).getCode()); } + /** + * See #347 + */ + @Test + public void testEncodeAndParseMedicationOrder() { + MedicationOrder mo = new MedicationOrder(); + mo.getDateWrittenElement().setValueAsString("2015-10-05"); + + String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(mo); + ourLog.info(encoded); + + mo = ourCtx.newXmlParser().parseResource(MedicationOrder.class, encoded); + assertEquals("2015-10-05", mo.getDateWrittenElement().getValueAsString()); + } + @Test public void testEncodeAndParseMetaProfileAndTags() { Patient p = new Patient(); @@ -1213,8 +1214,6 @@ public class XmlParserDstu3Test { assertEquals("", xml); } - - @Test public void testEncodeReferenceUsingUnqualifiedResourceWorksCorrectly() { @@ -1252,6 +1251,8 @@ public class XmlParserDstu3Test { assertThat(str, containsString("")); } + + @Test public void testEncodeSummary() { Patient patient = new Patient(); @@ -1290,7 +1291,7 @@ public class XmlParserDstu3Test { assertThat(encoded, containsString("family")); assertThat(encoded, not(containsString("maritalStatus"))); } - + @Test public void testEncodeUndeclaredExtensionWithEnumerationContent() { IParser parser = ourCtx.newXmlParser(); @@ -1311,7 +1312,7 @@ public class XmlParserDstu3Test { assertEquals("home", ref.getValue().toCode()); } - + @Test public void testEncodeWithContained() { List contained = new ArrayList(); @@ -2559,6 +2560,39 @@ public class XmlParserDstu3Test { assertEquals("Patient", reincarnatedPatient.getIdElement().getResourceType()); } + /** + * See #344 + */ + @Test + public void testParserIsCaseSensitive() { + Observation obs = new Observation(); + SampledData data = new SampledData(); + data.setData("1 2 3"); + data.setOrigin((SimpleQuantity) new SimpleQuantity().setValue(0L)); + data.setPeriod(1000L); + obs.setValue(data); + + IParser p = ourCtx.newXmlParser().setPrettyPrint(true).setParserErrorHandler(new StrictErrorHandler()); + String encoded = p.encodeResourceToString(obs); + ourLog.info(encoded); + + p.parseResource(encoded); + + try { + p.parseResource(encoded.replace("Observation", "observation")); + fail(); + } catch (DataFormatException e) { + assertEquals("DataFormatException at [[row,col {unknown-source}]: [1,1]]: Unknown resource type 'observation': Resource names are case sensitive, found similar name: 'Observation'", e.getMessage()); + } + + try { + p.parseResource(encoded.replace("valueSampledData", "valueSampleddata")); + fail(); + } catch (DataFormatException e) { + assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Unknown element 'valueSampleddata' found during parse", e.getMessage()); + } + } + /** * See #339 *