Fix #426 - Extension with datatype of ID failed to parse

This commit is contained in:
James 2016-08-25 07:32:37 -04:00
parent 9d3bd1ada4
commit acdbdc0be7
4 changed files with 129 additions and 57 deletions

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.parser;
* 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<T> {
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 @@ class ParserState<T> {
}
}
static ParserState<Bundle> getPreAtomInstance(IParser theParser, FhirContext theContext, Class<? extends IBaseResource> theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException {
static ParserState<Bundle> getPreAtomInstance(IParser theParser, FhirContext theContext, Class<? extends IBaseResource> theResourceType, boolean theJsonMode, IParserErrorHandler theErrorHandler)
throws DataFormatException {
ParserState<Bundle> retVal = new ParserState<Bundle>(theParser, theContext, theJsonMode, theErrorHandler);
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
retVal.push(retVal.new PreAtomState(theResourceType));
@ -237,7 +239,8 @@ class ParserState<T> {
* @param theResourceType
* May be null
*/
static <T extends IBaseResource> ParserState<T> getPreResourceInstance(IParser theParser, Class<T> theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) throws DataFormatException {
static <T extends IBaseResource> ParserState<T> getPreResourceInstance(IParser theParser, Class<T> theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler)
throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theParser, theContext, theJsonMode, theErrorHandler);
if (theResourceType == null) {
if (theContext.getVersion().getVersion().isRi()) {
@ -1389,7 +1392,7 @@ class ParserState<T> {
@Override
public void wereBack() {
super.wereBack();
IResource res = (IResource) getCurrentElement();
assert res != null;
if (res.getId() == null || res.getId().isEmpty()) {
@ -1580,7 +1583,7 @@ class ParserState<T> {
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
@ -1740,10 +1743,10 @@ class ParserState<T> {
}
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;
}
}
@ -1771,42 +1774,35 @@ class ParserState<T> {
}
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;
@ -1989,7 +1985,7 @@ class ParserState<T> {
@Override
public void endingElement() throws DataFormatException {
// postProcess();
// postProcess();
stitchBundleCrossReferences();
pop();
}
@ -2090,11 +2086,11 @@ class ParserState<T> {
}
}
}
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
@ -2110,7 +2106,7 @@ class ParserState<T> {
}
}
}
FhirTerser terser = myContext.newTerser();
terser.visit(myInstance, new IModelVisitor() {
@ -2146,7 +2142,8 @@ class ParserState<T> {
}
@Override
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) {
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) {
acceptElement(theNextExt.getValue(), null, null, null);
}
});
@ -2157,7 +2154,7 @@ class ParserState<T> {
private void stitchBundleCrossReferences() {
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
if (bundle) {
/*
* Stitch together resource references
*/
@ -2195,7 +2192,7 @@ class ParserState<T> {
}
}
}
}
}
@ -2224,11 +2221,11 @@ class ParserState<T> {
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
}
// @Override
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
// super.enteringNewElement(theNamespaceUri, theLocalPart);
// populateTarget();
// }
// @Override
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
// super.enteringNewElement(theNamespaceUri, theLocalPart);
// populateTarget();
// }
@Override
protected void populateTarget() {

View File

@ -802,6 +802,21 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
myPatientDao.read(new IdType("Patient/" + methodName), mySrd);
}
@Test
public void testTransactionCreateWithPutUsingAbsoluteUrl() {
String methodName = "testTransactionCreateWithPutUsingAbsoluteUrl";
Bundle request = new Bundle();
request.setType(BundleType.TRANSACTION);
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.PUT).setUrl("http://localhost/server/base/Patient/" + methodName);
mySystemDao.transaction(mySrd, request);
myPatientDao.read(new IdType("Patient/" + methodName), mySrd);
}
@Test
public void testTransactionCreateWithPutUsingUrl2() throws Exception {
String req = IOUtils.toString(FhirSystemDaoDstu3Test.class.getResourceAsStream("/bundle-dstu3.xml"), StandardCharsets.UTF_8);

View File

@ -135,7 +135,7 @@ public class XmlParserDstu3Test {
o = (Organization) rr.getResource();
assertEquals("ORG", o.getName());
}
@Test
public void testDuration() {
Encounter enc = new Encounter();
@ -186,7 +186,7 @@ public class XmlParserDstu3Test {
assertEquals("Organization/orgid", pt.getManagingOrganization().getReferenceElement().getValue());
assertSame(org, pt.getManagingOrganization().getResource());
}
@Test
public void testEncodeAndParseCompositeExtension() {
PatientWithCustomCompositeExtension pat = new PatientWithCustomCompositeExtension();
@ -203,8 +203,7 @@ public class XmlParserDstu3Test {
assertEquals("ValueA", pat.getFooParentExtension().getChildA().getValue());
assertEquals("ValueB", pat.getFooParentExtension().getChildB().getValue());
}
@Test
public void testEncodeAndParseContained() {
IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
@ -329,6 +328,7 @@ public class XmlParserDstu3Test {
ourCtx = null;
}
@Test
public void testEncodeAndParseContainedNonCustomTypes() {
ourCtx = FhirContext.forDstu3();
@ -2692,6 +2692,62 @@ public class XmlParserDstu3Test {
assertNotNull(((Reference) actual.getContent().get(0).getP()).getResource());
}
/**
* See #426
*/
@Test
public void testParseExtensionWithIdType() {
//@formatter:off
String input =
"<Patient xmlns=\"http://hl7.org/fhir\">\n" +
" <extension url=\"http://aaa.ch/fhir/Patient#mangedcare\">\n" +
" <extension url=\"http://aaa.ch/fhir/Patient#mangedcare-aaa-id\">\n" +
" <valueId value=\"mc1\"/>\n" +
" </extension>\n" +
" </extension>\n" +
" <identifier>\n" +
" <value value=\"ais111\"/>\n" +
" </identifier>\n" +
"</Patient>";
//@formatter:on
Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
List<Extension> 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 =
"<Patient xmlns=\"http://hl7.org/fhir\">\n" +
" <extension url=\"http://aaa.ch/fhir/Patient#mangedcare\">\n" +
" <extension url=\"http://aaa.ch/fhir/Patient#mangedcare-aaa-id\">\n" +
" <valueFOO value=\"mc1\"/>\n" +
" </extension>\n" +
" </extension>\n" +
" <identifier>\n" +
" <value value=\"ais111\"/>\n" +
" </identifier>\n" +
"</Patient>";
//@formatter:on
Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
List<Extension> 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
*/

View File

@ -197,6 +197,10 @@
Fix NullPointerException when encoding an extension containing CodeableConcept
with log level set to TRACE. Thanks to Bill Denton for the report!
</action>
<action type="fix" issue="426">
Parser failed to parse resources containing an extension with a value type of
"id". Thanks to Raphael Mäder for reporting!
</action>
</release>
<release version="1.6" date="2016-07-07">
<action type="fix">