5401 add terserutil clear by fhirpath (#5402)
* begin with failing test * test passes * test passes * changelog * moar testor
This commit is contained in:
parent
84e3becf1c
commit
a7a446903f
|
@ -20,6 +20,7 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
|
||||
|
@ -78,6 +79,10 @@ public abstract class BaseRuntimeChildDefinition {
|
|||
this.myReplacedParentDefinition = myReplacedParentDefinition;
|
||||
}
|
||||
|
||||
public boolean isMultipleCardinality() {
|
||||
return this.getMax() > 1 || this.getMax() == Child.MAX_UNLIMITED;
|
||||
}
|
||||
|
||||
public interface IAccessor {
|
||||
List<IBase> getValues(IBase theTarget);
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
|||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -659,9 +658,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
theEventWriter.endArray();
|
||||
}
|
||||
BaseRuntimeChildDefinition replacedParentDefinition = nextChild.getReplacedParentDefinition();
|
||||
if (isMultipleCardinality(nextChild.getMax())
|
||||
|| (replacedParentDefinition != null
|
||||
&& isMultipleCardinality(replacedParentDefinition.getMax()))) {
|
||||
if (nextChild.isMultipleCardinality()
|
||||
|| (replacedParentDefinition != null && replacedParentDefinition.isMultipleCardinality())) {
|
||||
beginArray(theEventWriter, nextChildSpecificName);
|
||||
inArray = true;
|
||||
encodeChildElementToStreamWriter(
|
||||
|
@ -728,14 +726,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
List<HeldExtension> heldModExts = Collections.emptyList();
|
||||
if (extensions.size() > i
|
||||
&& extensions.get(i) != null
|
||||
&& extensions.get(i).isEmpty() == false) {
|
||||
&& !extensions.get(i).isEmpty()) {
|
||||
haveContent = true;
|
||||
heldExts = extensions.get(i);
|
||||
}
|
||||
|
||||
if (modifierExtensions.size() > i
|
||||
&& modifierExtensions.get(i) != null
|
||||
&& modifierExtensions.get(i).isEmpty() == false) {
|
||||
&& !modifierExtensions.get(i).isEmpty()) {
|
||||
haveContent = true;
|
||||
heldModExts = modifierExtensions.get(i);
|
||||
}
|
||||
|
@ -746,7 +744,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
} else {
|
||||
nextComments = null;
|
||||
}
|
||||
if (nextComments != null && nextComments.isEmpty() == false) {
|
||||
if (nextComments != null && !nextComments.isEmpty()) {
|
||||
haveContent = true;
|
||||
}
|
||||
|
||||
|
@ -804,10 +802,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
return myIsSupportsFhirComment;
|
||||
}
|
||||
|
||||
private boolean isMultipleCardinality(int maxCardinality) {
|
||||
return maxCardinality > 1 || maxCardinality == Child.MAX_UNLIMITED;
|
||||
}
|
||||
|
||||
private void encodeCompositeElementToStreamWriter(
|
||||
RuntimeResourceDefinition theResDef,
|
||||
IBaseResource theResource,
|
||||
|
@ -917,10 +911,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
// Undeclared extensions
|
||||
extractUndeclaredExtensions(
|
||||
theResourceId, extensions, modifierExtensions, null, null, theEncodeContext, theContainedResource);
|
||||
boolean haveExtension = false;
|
||||
if (!extensions.isEmpty()) {
|
||||
haveExtension = true;
|
||||
}
|
||||
boolean haveExtension = !extensions.isEmpty();
|
||||
|
||||
if (theResourceId.hasFormatComment() || haveExtension) {
|
||||
beginObject(theEventWriter, "_id");
|
||||
|
|
|
@ -351,6 +351,29 @@ public final class TerserUtil {
|
|||
clear(childDefinition.getAccessor().getValues(theResource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified field on the resource provided by the FHIRPath. If more than one value matches
|
||||
* the FHIRPath, all values will be cleared.
|
||||
*
|
||||
* @param theFhirContext
|
||||
* @param theResource
|
||||
* @param theFhirPath
|
||||
*/
|
||||
public static void clearFieldByFhirPath(FhirContext theFhirContext, IBaseResource theResource, String theFhirPath) {
|
||||
|
||||
if (theFhirPath.contains(".")) {
|
||||
String parentPath = theFhirPath.substring(0, theFhirPath.lastIndexOf("."));
|
||||
String fieldName = theFhirPath.substring(theFhirPath.lastIndexOf(".") + 1);
|
||||
FhirTerser terser = theFhirContext.newTerser();
|
||||
List<IBase> parents = terser.getValues(theResource, parentPath);
|
||||
for (IBase parent : parents) {
|
||||
clearField(theFhirContext, fieldName, parent);
|
||||
}
|
||||
} else {
|
||||
clearField(theFhirContext, theResource, theFhirPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified field on the element provided
|
||||
*
|
||||
|
@ -362,7 +385,16 @@ public final class TerserUtil {
|
|||
BaseRuntimeElementDefinition definition = theFhirContext.getElementDefinition(theBase.getClass());
|
||||
BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName);
|
||||
Validate.notNull(childDefinition);
|
||||
clear(childDefinition.getAccessor().getValues(theBase));
|
||||
BaseRuntimeChildDefinition.IAccessor accessor = childDefinition.getAccessor();
|
||||
clear(accessor.getValues(theBase));
|
||||
List<IBase> newValue = accessor.getValues(theBase);
|
||||
|
||||
if (newValue != null && !newValue.isEmpty()) {
|
||||
// Our clear failed, probably because it was an immutable SingletonList returned by a FieldPlainAccessor
|
||||
// that cannot be cleared.
|
||||
// Let's just null it out instead.
|
||||
childDefinition.getMutator().setValue(theBase, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 5401
|
||||
title: "Previously, it was only possible to clear a top-level field on a resource using TerserUtil. A new method has been added
|
||||
to TerserUtil to support clearing a value by FhirPath."
|
|
@ -13,6 +13,7 @@ import org.hl7.fhir.r4.model.Identifier;
|
|||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.PrimitiveType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -469,6 +470,7 @@ class TerserUtilTest {
|
|||
assertEquals(1, p2.getName().size());
|
||||
assertEquals("Doe", p2.getName().get(0).getFamily());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceFieldByEmptyValue() {
|
||||
Patient p1 = new Patient();
|
||||
|
@ -507,6 +509,7 @@ class TerserUtilTest {
|
|||
{
|
||||
Patient p1 = new Patient();
|
||||
p1.addName().setFamily("Doe");
|
||||
assertEquals(1, p1.getName().size());
|
||||
|
||||
TerserUtil.clearField(ourFhirContext, p1, "name");
|
||||
|
||||
|
@ -517,6 +520,7 @@ class TerserUtilTest {
|
|||
Address a1 = new Address();
|
||||
a1.addLine("Line 1");
|
||||
a1.addLine("Line 2");
|
||||
assertEquals(2, a1.getLine().size());
|
||||
a1.setCity("Test");
|
||||
TerserUtil.clearField(ourFhirContext, "line", a1);
|
||||
|
||||
|
@ -525,6 +529,47 @@ class TerserUtilTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearFieldByFhirPath() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addName().setFamily("Doe");
|
||||
assertEquals(1, p1.getName().size());
|
||||
|
||||
TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "name");
|
||||
|
||||
assertEquals(0, p1.getName().size());
|
||||
|
||||
Address a1 = new Address();
|
||||
a1.addLine("Line 1");
|
||||
a1.addLine("Line 2");
|
||||
assertEquals(2, a1.getLine().size());
|
||||
a1.setCity("Test");
|
||||
a1.getPeriod().setStartElement(new DateTimeType("2021-01-01"));
|
||||
p1.addAddress(a1);
|
||||
|
||||
assertEquals("2021-01-01", p1.getAddress().get(0).getPeriod().getStartElement().toHumanDisplay());
|
||||
assertNotNull(p1.getAddress().get(0).getPeriod().getStart());
|
||||
|
||||
Address a2 = new Address();
|
||||
a2.addLine("Line 1");
|
||||
a2.addLine("Line 2");
|
||||
a2.setCity("Test");
|
||||
a2.getPeriod().setStartElement(new DateTimeType("2021-01-01"));
|
||||
p1.addAddress(a2);
|
||||
|
||||
|
||||
TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "address.line");
|
||||
TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "address.period.start");
|
||||
|
||||
assertNull(p1.getAddress().get(0).getPeriod().getStart());
|
||||
|
||||
assertEquals(2, p1.getAddress().size());
|
||||
assertEquals(0, p1.getAddress().get(0).getLine().size());
|
||||
assertEquals(0, p1.getAddress().get(1).getLine().size());
|
||||
assertEquals("Test", p1.getAddress().get(0).getCity());
|
||||
assertEquals("Test", p1.getAddress().get(1).getCity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetField() {
|
||||
Patient p1 = new Patient();
|
||||
|
@ -551,6 +596,16 @@ class TerserUtilTest {
|
|||
assertEquals("CITY", p1.getAddress().get(0).getCity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetFieldByCompositeFhirPath() {
|
||||
Patient p1 = new Patient();
|
||||
|
||||
TerserUtil.setFieldByFhirPath(ourFhirContext, "address.city", p1, new StringType("CITY"));
|
||||
|
||||
assertEquals(1, p1.getAddress().size());
|
||||
assertEquals("CITY", p1.getAddress().get(0).getCity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClone() {
|
||||
Patient p1 = new Patient();
|
||||
|
|
Loading…
Reference in New Issue