diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java index 00deaf72057a..712adc33db2c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java @@ -40,7 +40,7 @@ public enum FhirVersionEnum { DSTU2_HL7ORG("org.hl7.fhir.instance.FhirDstu2Hl7Org", DSTU2, true, "1.0.2"), - DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, "1.5.0"); + DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, "1.6.0"); private final FhirVersionEnum myEquivalent; private final String myFhirVersionString; 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 6b1e4b1bd9f8..37bcc2a21837 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 @@ -10,7 +10,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -105,6 +105,7 @@ class ParserState { private final IParser myParser; private IBase myPreviousElement; private BaseState myState; + private ParserState(IParser theParser, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) { myParser = theParser; myContext = theContext; @@ -223,7 +224,8 @@ public void xmlEvent(XMLEvent theNextEvent) { } } - static ParserState getPreAtomInstance(IParser theParser, FhirContext theContext, Class theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException { + static ParserState getPreAtomInstance(IParser theParser, FhirContext theContext, Class theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler) + throws DataFormatException { ParserState retVal = new ParserState(theParser, theContext, theJsonMode, theErrorHandler); if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { retVal.push(retVal.new PreAtomState(theResourceType)); @@ -237,7 +239,8 @@ static ParserState getPreAtomInstance(IParser theParser, FhirContext the * @param theResourceType * May be null */ - static ParserState getPreResourceInstance(IParser theParser, Class theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException { + static ParserState getPreResourceInstance(IParser theParser, Class theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) + throws DataFormatException { ParserState retVal = new ParserState(theParser, theContext, theJsonMode, theErrorHandler); if (theResourceType == null) { if (theContext.getVersion().getVersion().isRi()) { @@ -1389,7 +1392,7 @@ protected void populateTarget() { @Override public void wereBack() { super.wereBack(); - + IResource res = (IResource) getCurrentElement(); assert res != null; if (res.getId() == null || res.getId().isEmpty()) { @@ -1582,7 +1585,7 @@ public void enteringNewElement(String theNamespace, String theChildName) throws return; } } - + /* * This means we've found an element that doesn't exist on the structure. If the error handler doesn't throw * an exception, swallow the element silently along with any child elements @@ -1742,10 +1745,10 @@ public void attributeValue(String theName, String theValue) throws DataFormatExc } if ("id".equals(theName)) { if (getCurrentElement() instanceof IBaseElement) { - ((IBaseElement)getCurrentElement()).setId(theValue); + ((IBaseElement) getCurrentElement()).setId(theValue); return; } else if (getCurrentElement() instanceof IIdentifiableElement) { - ((IIdentifiableElement)getCurrentElement()).setElementSpecificId(theValue); + ((IIdentifiableElement) getCurrentElement()).setElementSpecificId(theValue); return; } } @@ -1773,42 +1776,35 @@ public void enteringNewElement(String theNamespaceUri, String theLocalPart) thro } BaseRuntimeElementDefinition target = myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(theLocalPart); - if (target == null) { - myErrorHandler.unknownElement(null, theLocalPart); - push(new SwallowChildrenWholeState(getPreResourceState())); - return; + + if (target != null) { + switch (target.getChildType()) { + case COMPOSITE_DATATYPE: { + BaseRuntimeElementCompositeDefinition compositeTarget = (BaseRuntimeElementCompositeDefinition) target; + ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(); + myExtension.setValue(newChildInstance); + ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); + push(newState); + return; + } + case ID_DATATYPE: + case PRIMITIVE_DATATYPE: { + RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target; + IPrimitiveType newChildInstance = primitiveTarget.newInstance(); + myExtension.setValue(newChildInstance); + PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance); + push(newState); + return; + } + } } - switch (target.getChildType()) { - case COMPOSITE_DATATYPE: { - BaseRuntimeElementCompositeDefinition compositeTarget = (BaseRuntimeElementCompositeDefinition) target; - ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(); - myExtension.setValue(newChildInstance); - ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); - push(newState); - return; - } - case PRIMITIVE_DATATYPE: { - RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target; - IPrimitiveType newChildInstance = primitiveTarget.newInstance(); - myExtension.setValue(newChildInstance); - PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance); - push(newState); - return; - } - case PRIMITIVE_XHTML: - case RESOURCE: - case RESOURCE_BLOCK: - case UNDECL_EXT: - case EXTENSION_DECLARED: - case CONTAINED_RESOURCES: - case CONTAINED_RESOURCE_LIST: - case ID_DATATYPE: - case PRIMITIVE_XHTML_HL7ORG: - break; - } + // We hit an invalid type for the extension + myErrorHandler.unknownElement(null, theLocalPart); + push(new SwallowChildrenWholeState(getPreResourceState())); + return; } - + @Override protected IBaseExtension getCurrentElement() { return myExtension; @@ -2135,11 +2131,11 @@ private void postProcess() { } } } - + if (wantedProfileType != null && !wantedProfileType.equals(myInstance.getClass())) { if (myResourceType == null || myResourceType.isAssignableFrom(wantedProfileType)) { - ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[] {myInstance.getClass().getName(), usedProfile, wantedProfileType}); - + ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[] { myInstance.getClass().getName(), usedProfile, wantedProfileType }); + /* * This isn't the most efficient thing really.. If we want a specific * type we just re-parse into that type. The problem is that we don't know @@ -2162,7 +2158,7 @@ private void postProcess() { private void stitchBundleCrossReferences() { final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName()); if (bundle) { - + /* * Stitch together resource references */ @@ -2200,7 +2196,7 @@ private void stitchBundleCrossReferences() { } } } - + } } @@ -2229,11 +2225,11 @@ public PreResourceStateHapi(Object theTarget, IMutator theMutator, Class\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input); + + List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare"); + extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id"); + Extension ext = extList.get(0); + IdType value = (IdType) ext.getValue(); + assertEquals("mc1", value.getValueAsString()); + } + + /** + * See #426 + * + * Value type of FOO isn't a valid datatype + */ + @Test + public void testParseExtensionWithInvalidType() { + //@formatter:off + String input = + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + //@formatter:on + Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input); + + List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare"); + extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id"); + Extension ext = extList.get(0); + IdType value = (IdType) ext.getValue(); + assertEquals(null, value); + } + /** * See #342 */ diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1df1aa2e5c5a..89d2b92381bd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -31,7 +31,7 @@ STU3 structure definitions have been updated to the - STU3 ballot candidate versions (1.5.0 - SVN 9395) + STU3 ballot candidate versions (1.6.0 - SVN 9395) Both client and server now support the new Content Types decided in @@ -206,6 +206,10 @@ Improve performance when parsing resources containing contained resources by eliminating a step where references were woven twice + + Parser failed to parse resources containing an extension with a value type of + "id". Thanks to Raphael Mäder for reporting! + @@ -1457,7 +1461,7 @@ search. This is experimental, since it is not a part of the core FHIR specification. - + Process "Accept: text/xml" and "Accept: text/json" headers was wanting the equivalent FHIR encoding styles. These are not correct, but the intention is clear so we will honour them @@ -1468,13 +1472,13 @@ codes (specifically, ValueSets which defined concepts containing child concepts did not result in Enum values for the child concepts) - + In the JPA server, order of transaction processing should be DELETE, POST, PUT, GET, and the order should not matter within entries with the same verb. Thanks to Bill de Beaubien for reporting! - + Add the ability to wire JPA conformance providers using Spring (basically, add default constructors and setters to the conformance providers). Thanks @@ -1571,11 +1575,11 @@ JPA server now implements the $validate-code operation - + HAPI-FHIR now has support for _summary and _elements parameters, in server, client, and JPA server. - + _revinclude results from JPA server should have a Bundle.entry.search.mode of "include" and not "match". Thanks to Josh Mandel for reporting! @@ -1785,7 +1789,7 @@ Generic/fluent client and JPA server now both support _lastUpdated search parameter which was added in DSTU2 - + JPA server now supports sorting on reference parameters. Thanks to Vishal Kachroo for reporting that this wasn't working! @@ -1869,7 +1873,7 @@ the new syntax required in DSTU2: [resource name]:[search param NAME] insead of the DSTU1 style [resource name].[search param PATH] - + When encoding resources, the parser will now convert any resource references to versionless references automatically (i.e. it will omit the version part automatically if one is present in the reference) @@ -1878,7 +1882,7 @@ reference if the base matches the base for the server giving the response. - + Narrative generator incorrectly sets the Resource.text.status to 'generated' even if the given resource type does not have a template (and therefore no narrative is actually generated). Thanks to Bill de Beaubien for reporting! @@ -1886,7 +1890,7 @@ Searching in JPA server with no search parameter returns deleted resources when it should exclude them. - + Remove Eclipse and IntelliJ artifacts (.project, *.iml, etc) from version control. Thanks to Doug Martin for the suggestion! @@ -2075,7 +2079,7 @@ the method type was a custom resource definition type (instead of a built-in HAPI type). Thanks to Neal Acharya for the analysis. - + JPA server module now supports _include]]> value of