Fix #814 - Don't create duplicate contained resources

This commit is contained in:
James Agnew 2018-03-02 09:46:29 -05:00
parent fe140a55dd
commit 0c0b9bd74b
3 changed files with 58 additions and 12 deletions

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.parser;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * 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 * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -38,6 +38,7 @@ import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("WeakerAccess")
public abstract class BaseParser implements IParser { public abstract class BaseParser implements IParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
@ -156,7 +157,6 @@ public abstract class BaseParser implements IParser {
} }
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) { private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
Set<String> allIds = new HashSet<String>();
Map<String, IBaseResource> existingIdToContainedResource = null; Map<String, IBaseResource> existingIdToContainedResource = null;
if (theTarget instanceof IResource) { if (theTarget instanceof IResource) {
@ -167,9 +167,8 @@ public abstract class BaseParser implements IParser {
if (!nextId.startsWith("#")) { if (!nextId.startsWith("#")) {
nextId = '#' + nextId; nextId = '#' + nextId;
} }
allIds.add(nextId);
if (existingIdToContainedResource == null) { if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>(); existingIdToContainedResource = new HashMap<>();
} }
existingIdToContainedResource.put(nextId, next); existingIdToContainedResource.put(nextId, next);
} }
@ -182,9 +181,8 @@ public abstract class BaseParser implements IParser {
if (!nextId.startsWith("#")) { if (!nextId.startsWith("#")) {
nextId = '#' + nextId; nextId = '#' + nextId;
} }
allIds.add(nextId);
if (existingIdToContainedResource == null) { if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>(); existingIdToContainedResource = new HashMap<>();
} }
existingIdToContainedResource.put(nextId, next); existingIdToContainedResource.put(nextId, next);
} }
@ -480,7 +478,7 @@ public abstract class BaseParser implements IParser {
@Override @Override
public void setPreferTypes(List<Class<? extends IBaseResource>> thePreferTypes) { public void setPreferTypes(List<Class<? extends IBaseResource>> thePreferTypes) {
if (thePreferTypes != null) { if (thePreferTypes != null) {
ArrayList<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>(); ArrayList<Class<? extends IBaseResource>> types = new ArrayList<>();
for (Class<? extends IBaseResource> next : thePreferTypes) { for (Class<? extends IBaseResource> next : thePreferTypes) {
if (Modifier.isAbstract(next.getModifiers()) == false) { if (Modifier.isAbstract(next.getModifiers()) == false) {
types.add(next); types.add(next);
@ -516,8 +514,7 @@ public abstract class BaseParser implements IParser {
} }
} }
List<T> newList = new ArrayList<T>(); List<T> newList = new ArrayList<>(theProfiles);
newList.addAll(theProfiles);
BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id"); BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1183,8 +1180,10 @@ public abstract class BaseParser implements IParser {
} }
public void addContained(IIdType theId, IBaseResource theResource) { public void addContained(IIdType theId, IBaseResource theResource) {
myResourceToId.put(theResource, theId); if (!hasId(theId)) {
myResources.add(theResource); myResourceToId.put(theResource, theId);
myResources.add(theResource);
}
} }
public List<IBaseResource> getContainedResources() { public List<IBaseResource> getContainedResources() {
@ -1195,6 +1194,15 @@ public abstract class BaseParser implements IParser {
return myResourceToId.get(theNext); return myResourceToId.get(theNext);
} }
public boolean hasId(IIdType theId) {
for (IIdType next : myResourceToId.values()) {
if (Objects.equals(next.getValue(), theId.getValue())) {
return true;
}
}
return false;
}
public boolean isEmpty() { public boolean isEmpty() {
return myResourceToId.isEmpty(); return myResourceToId.isEmpty();
} }

View File

@ -88,6 +88,39 @@ public class JsonParserR4Test {
} }
/*
* See #814
*/
@Test
public void testDuplicateContainedResourcesNotOutputtedTwiceWithManualIdsAndManualAddition() {
MedicationDispense md = new MedicationDispense();
MedicationRequest mr = new MedicationRequest();
mr.setId("#MR");
md.addAuthorizingPrescription().setResource(mr);
Medication med = new Medication();
med.setId("#MED");
Reference medRef = new Reference();
medRef.setReference("#MED");
md.setMedication(medRef);
mr.setMedication(medRef);
md.getContained().add(mr);
md.getContained().add(med);
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(md);
ourLog.info(encoded);
int idx = encoded.indexOf("\"Medication\"");
assertNotEquals(-1, idx);
idx = encoded.indexOf("\"Medication\"", idx + 1);
assertEquals(-1, idx);
}
@Test @Test
public void testExcludeNothing() { public void testExcludeNothing() {
IParser parser = ourCtx.newJsonParser().setPrettyPrint(true); IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);

View File

@ -189,6 +189,11 @@
DateParam class now has equals() and hashCode() implementations. Thanks DateParam class now has equals() and hashCode() implementations. Thanks
to Gaetano Gallo for the pull request! to Gaetano Gallo for the pull request!
</action> </action>
<action type="fix" issue="814">
Fix a bug where under certain circumstances, duplicate contained resources
could be output by the parser's encode methods. Thanks to
Frank Tao for supplying a test case!
</action>
</release> </release>
<release version="3.2.0" date="2018-01-13"> <release version="3.2.0" date="2018-01-13">
<action type="add"> <action type="add">