diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java
index dcb81b5ea54..8295ea113f8 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseResourceReference.java
@@ -30,6 +30,7 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseClient;
@@ -49,6 +50,14 @@ public abstract class BaseResourceReference extends BaseElement {
// nothing
}
+ /**
+ * Constructor
+ */
+ public BaseResourceReference(Class extends IResource> theResourceType, IdDt theResourceId) {
+ myResourceType = theResourceType;
+ myResourceId = theResourceId.getValue();
+ }
+
/**
* Constructor
*/
@@ -57,11 +66,6 @@ public abstract class BaseResourceReference extends BaseElement {
myResourceId = theResourceId;
}
- @Override
- protected boolean isBaseEmpty() {
- return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
- }
-
public BaseResourceReference(IResource theResource) {
myResource=theResource;
}
@@ -102,6 +106,11 @@ public abstract class BaseResourceReference extends BaseElement {
return getReference().getValue();
}
+ @Override
+ protected boolean isBaseEmpty() {
+ return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
+ }
+
/**
* Returns the referenced resource, fetching it if it has not already been loaded. This method invokes the HTTP client to retrieve the resource unless it has already been loaded, or was a
* contained resource in which case it is simply returned.
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java
index 360e5625ac2..5d9d129bc5a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/ResourceReferenceDt.java
@@ -45,6 +45,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
/**
@@ -81,6 +82,16 @@ public class ResourceReferenceDt
public ResourceReferenceDt(Class extends IResource> theResourceType, String theResourceId) {
super(theResourceType, theResourceId);
}
+
+ /**
+ * Constructor which creates a normal resource reference
+ *
+ * @param theResourceType The resource type
+ * @param theResourceId The resource ID
+ */
+ public ResourceReferenceDt(Class extends IResource> theResourceType, IdDt theResourceId) {
+ super(theResourceType, theResourceId);
+ }
/**
* Constructor which creates a resource reference containing the actual
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/resource/Observation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/resource/Observation.java
index 1f8b9ab86ce..9934df7ddf4 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/resource/Observation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/resource/Observation.java
@@ -1082,6 +1082,9 @@ public class Observation extends BaseResource implements IResource {
*
*/
public java.util.List getPerformer() {
+ if (myPerformer == null) {
+ myPerformer = new java.util.ArrayList();
+ }
return myPerformer;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java
new file mode 100644
index 00000000000..be59c28b368
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java
@@ -0,0 +1,19 @@
+package ca.uhn.fhir.rest.param;
+
+import ca.uhn.fhir.model.api.IQueryParameterType;
+
+public class ReferenceParam implements IQueryParameterType {
+
+ private String myValue;
+
+ @Override
+ public void setValueAsQueryToken(String theParameter) {
+ myValue=theParameter;
+ }
+
+ @Override
+ public String getValueAsQueryToken() {
+ return myValue;
+ }
+
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java
index 7cc7030c429..ad49c4e0ba2 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java
@@ -25,7 +25,7 @@ import ca.uhn.fhir.rest.server.Constants;
/**
* Represents an HTTP 400 Bad Request response.
* This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource
- * per the specifications), as opposed to the {@link InvalidRequestException} which indicates
+ * per the specifications), as opposed to the {@link UnprocessableEntityException} which indicates
* that data does not pass business rule validation on the server.
*
*
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDao.java
index 9a4d34fbf8c..c6d171a63d2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDao.java
@@ -21,6 +21,7 @@ import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@@ -42,6 +43,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
+import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -54,6 +56,7 @@ import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
+import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
@@ -62,8 +65,10 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
+import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.FhirTerser;
@@ -76,205 +81,108 @@ public class FhirResourceDao
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
+ @Autowired
+ private List> myResourceDaos;
+ private String myResourceName;
private Class myResourceType;
private Class myTableType;
- private String myResourceName;
+ private Map, Class extends BaseResourceTable>>> myResourceTypeToDao;
- @Transactional(propagation = Propagation.SUPPORTS)
- @Override
- public MethodOutcome create(T theResource) {
+ private Set addPredicateDate(Set thePids, List theOrParams) {
+ if (theOrParams == null || theOrParams.isEmpty()) {
+ return thePids;
+ }
- final X entity = toEntity(theResource);
+ CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
+ CriteriaQuery cq = builder.createQuery(Long.class);
+ Root from = cq.from(ResourceIndexedSearchParamDate.class);
+ cq.select(from.get("myResourcePid").as(Long.class));
- entity.setPublished(new Date());
- entity.setUpdated(entity.getPublished());
+ List codePredicates = new ArrayList();
+ for (IQueryParameterType nextOr : theOrParams) {
+ IQueryParameterType params = nextOr;
- final List stringParams = extractSearchParamStrings(entity, theResource);
- final List tokenParams = extractSearchParamTokens(entity, theResource);
- final List numberParams = extractSearchParamNumber(entity, theResource);
- final List dateParams = extractSearchParamDates(entity, theResource);
-
- TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
- template.execute(new TransactionCallback() {
- @Override
- public X doInTransaction(TransactionStatus theStatus) {
- myEntityManager.persist(entity);
- for (ResourceIndexedSearchParamString next : stringParams) {
- myEntityManager.persist(next);
- }
- for (ResourceIndexedSearchParamToken next : tokenParams) {
- myEntityManager.persist(next);
- }
- for (ResourceIndexedSearchParamNumber next : numberParams) {
- myEntityManager.persist(next);
- }
- for (ResourceIndexedSearchParamDate next : dateParams) {
- myEntityManager.persist(next);
- }
- return entity;
+ if (params instanceof QualifiedDateParam) {
+ QualifiedDateParam id = (QualifiedDateParam) params;
+ DateRangeParam range = new DateRangeParam(id);
+ addPredicateDateFromRange(builder, from, codePredicates, range);
+ } else if (params instanceof DateRangeParam) {
+ DateRangeParam range = (DateRangeParam) params;
+ addPredicateDateFromRange(builder, from, codePredicates, range);
+ } else {
+ throw new IllegalArgumentException("Invalid token type: " + params.getClass());
}
- });
- MethodOutcome outcome = toMethodOutcome(entity);
- return outcome;
- }
-
- @Transactional(propagation = Propagation.REQUIRED)
- @Override
- public List history(IdDt theId) {
- ArrayList retVal = new ArrayList();
-
- String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
- TypedQuery q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
- q.setParameter("PID", theId.asLong());
- q.setParameter("RESTYPE", resourceType);
-
- // TypedQuery query =
- // myEntityManager.createQuery(criteriaQuery);
- List results = q.getResultList();
- for (ResourceHistoryTable next : results) {
- retVal.add(toResource(next));
}
- try {
- retVal.add(read(theId));
- } catch (ResourceNotFoundException e) {
- // ignore
+ Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
+
+ Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
+ if (thePids.size() > 0) {
+ Predicate inPids = (from.get("myResourcePid").in(thePids));
+ cq.where(builder.and(type, inPids, masterCodePredicate));
+ } else {
+ cq.where(builder.and(type, masterCodePredicate));
}
- if (retVal.isEmpty()) {
- throw new ResourceNotFoundException(theId);
+ TypedQuery q = myEntityManager.createQuery(cq);
+ return new HashSet(q.getResultList());
+ }
+
+ private Set addPredicateReference(Set thePids, List theOrParams) {
+ if (theOrParams == null || theOrParams.isEmpty()) {
+ return thePids;
}
- return retVal;
- }
+ CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
+ CriteriaQuery cq = builder.createQuery(Long.class);
+ Root from = cq.from(ResourceIndexedSearchParamDate.class);
+ cq.select(from.get("myResourcePid").as(Long.class));
- @PostConstruct
- public void postConstruct() throws Exception {
- myResourceType = myTableType.newInstance().getResourceType();
- myCtx = new FhirContext(myResourceType);
- myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
- }
+ List codePredicates = new ArrayList();
+ for (IQueryParameterType nextOr : theOrParams) {
+ IQueryParameterType params = nextOr;
- @Transactional(propagation = Propagation.REQUIRED)
- @Override
- public T read(IdDt theId) {
- X entity = readEntity(theId);
-
- T retVal = toResource(entity);
- return retVal;
- }
-
- @Override
- public List search(Map theParams) {
- Map>> map = new HashMap>>();
- for (Entry nextEntry : theParams.entrySet()) {
- map.put(nextEntry.getKey(), new ArrayList>());
- map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
- }
- return searchWithAndOr(map);
- }
-
- @Override
- public List search(String theSpName, IQueryParameterType theValue) {
- return search(Collections.singletonMap(theSpName, theValue));
- }
-
- @Override
- public List searchWithAndOr(Map>> theParams) {
- Map>> params = theParams;
- if (params == null) {
- params = Collections.emptyMap();
- }
-
- RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
-
- Set pids = new HashSet();
-
- for (Entry>> nextParamEntry : params.entrySet()) {
- String nextParamName = nextParamEntry.getKey();
- RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
- if (nextParamDef != null) {
- if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
- for (List nextAnd : nextParamEntry.getValue()) {
- pids = addPredicateToken(pids, nextAnd);
- if (pids.isEmpty()) {
- return new ArrayList();
- }
- }
- } else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
- for (List nextAnd : nextParamEntry.getValue()) {
- pids = addPredicateString(pids, nextAnd);
- if (pids.isEmpty()) {
- return new ArrayList();
- }
- }
- } else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
- for (List nextAnd : nextParamEntry.getValue()) {
- pids = addPredicateQuantity(pids, nextAnd);
- if (pids.isEmpty()) {
- return new ArrayList();
- }
- }
- } else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
- for (List nextAnd : nextParamEntry.getValue()) {
- pids = addPredicateDate(pids, nextAnd);
- if (pids.isEmpty()) {
- return new ArrayList();
- }
- }
- } else {
- throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
- }
+ if (params instanceof ReferenceParam) {
+ ReferenceParam id = (ReferenceParam) params;
+ } else {
+ throw new IllegalArgumentException("Invalid token type: " + params.getClass());
}
+
}
- // Execute the query and make sure we return distinct results
- {
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(myTableType);
- Root from = cq.from(myTableType);
- if (!params.isEmpty()) {
- cq.where(from.get("myId").in(pids));
- }
- TypedQuery q = myEntityManager.createQuery(cq);
+ Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
- List retVal = new ArrayList<>();
- for (X next : q.getResultList()) {
- T resource = toResource(next);
- retVal.add(resource);
- }
- return retVal;
+ Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
+ if (thePids.size() > 0) {
+ Predicate inPids = (from.get("myResourcePid").in(thePids));
+ cq.where(builder.and(type, inPids, masterCodePredicate));
+ } else {
+ cq.where(builder.and(type, masterCodePredicate));
}
+
+ TypedQuery q = myEntityManager.createQuery(cq);
+ return new HashSet(q.getResultList());
}
- @Required
- public void setTableType(Class theTableType) {
- myTableType = theTableType;
+ private void addPredicateDateFromRange(CriteriaBuilder builder, Root from, List codePredicates, DateRangeParam range) {
+ Predicate singleCode;
+ Date lowerBound = range.getLowerBoundAsInstant();
+ Date upperBound = range.getUpperBoundAsInstant();
+
+ if (lowerBound != null && upperBound != null) {
+ Predicate low = builder.greaterThanOrEqualTo(from. get("myValueLow"), lowerBound);
+ Predicate high = builder.lessThanOrEqualTo(from. get("myValueHigh"), upperBound);
+ singleCode = builder.and(low, high);
+ } else if (lowerBound != null) {
+ singleCode = builder.greaterThanOrEqualTo(from. get("myValueLow"), lowerBound);
+ } else {
+ singleCode = builder.lessThanOrEqualTo(from. get("myValueHigh"), upperBound);
+ }
+
+ codePredicates.add(singleCode);
}
-
- @Transactional(propagation = Propagation.SUPPORTS)
- @Override
- public MethodOutcome update(final T theResource, final IdDt theId) {
- TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
- X savedEntity = template.execute(new TransactionCallback() {
- @Override
- public X doInTransaction(TransactionStatus theStatus) {
- final X entity = readEntity(theId);
- final ResourceHistoryTable existing = entity.toHistory(myCtx);
-
- populateResourceIntoEntity(theResource, entity);
- myEntityManager.persist(existing);
-
- entity.setUpdated(new Date());
- myEntityManager.persist(entity);
- return entity;
- }
- });
-
- return toMethodOutcome(savedEntity);
- }
-
+
private Set addPredicateQuantity(Set thePids, List theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) {
return thePids;
@@ -359,65 +267,6 @@ public class FhirResourceDao
return new HashSet(q.getResultList());
}
- private Set addPredicateDate(Set thePids, List theOrParams) {
- if (theOrParams == null || theOrParams.isEmpty()) {
- return thePids;
- }
-
- CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
- CriteriaQuery cq = builder.createQuery(Long.class);
- Root from = cq.from(ResourceIndexedSearchParamDate.class);
- cq.select(from.get("myResourcePid").as(Long.class));
-
- List codePredicates = new ArrayList();
- for (IQueryParameterType nextOr : theOrParams) {
- IQueryParameterType params = nextOr;
-
- if (params instanceof QualifiedDateParam) {
- QualifiedDateParam id = (QualifiedDateParam) params;
- DateRangeParam range = new DateRangeParam(id);
- addPredicateDateFromRange(builder, from, codePredicates, range);
- } else if (params instanceof DateRangeParam) {
- DateRangeParam range = (DateRangeParam) params;
- addPredicateDateFromRange(builder, from, codePredicates, range);
- } else {
- throw new IllegalArgumentException("Invalid token type: " + params.getClass());
- }
-
- }
-
- Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
-
- Predicate type = builder.equal(from.get("myResourceType"), myResourceName);
- if (thePids.size() > 0) {
- Predicate inPids = (from.get("myResourcePid").in(thePids));
- cq.where(builder.and(type, inPids, masterCodePredicate));
- } else {
- cq.where(builder.and(type, masterCodePredicate));
- }
-
- TypedQuery q = myEntityManager.createQuery(cq);
- return new HashSet(q.getResultList());
- }
-
- private void addPredicateDateFromRange(CriteriaBuilder builder, Root from, List codePredicates, DateRangeParam range) {
- Predicate singleCode;
- Date lowerBound = range.getLowerBoundAsInstant();
- Date upperBound = range.getUpperBoundAsInstant();
-
- if (lowerBound != null && upperBound != null) {
- Predicate low = builder.greaterThanOrEqualTo(from. get("myValueLow"), lowerBound);
- Predicate high = builder.lessThanOrEqualTo(from. get("myValueHigh"), upperBound);
- singleCode = builder.and(low, high);
- } else if (lowerBound != null) {
- singleCode = builder.greaterThanOrEqualTo(from. get("myValueLow"), lowerBound);
- } else {
- singleCode = builder.lessThanOrEqualTo(from. get("myValueHigh"), upperBound);
- }
-
- codePredicates.add(singleCode);
- }
-
private Set addPredicateString(Set thePids, List theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) {
return thePids;
@@ -511,6 +360,203 @@ public class FhirResourceDao
return new HashSet(q.getResultList());
}
+ @Transactional(propagation = Propagation.REQUIRED, readOnly=true)
+ @Override
+ public MethodOutcome create(T theResource) {
+
+ final X entity = toEntity(theResource);
+
+ entity.setPublished(new Date());
+ entity.setUpdated(entity.getPublished());
+
+ final List stringParams = extractSearchParamStrings(entity, theResource);
+ final List tokenParams = extractSearchParamTokens(entity, theResource);
+ final List numberParams = extractSearchParamNumber(entity, theResource);
+ final List dateParams = extractSearchParamDates(entity, theResource);
+ final List links = extractResourceLinks(entity, theResource);
+
+ TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
+ template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
+ template.setReadOnly(false);
+ template.execute(new TransactionCallback() {
+ @Override
+ public X doInTransaction(TransactionStatus theStatus) {
+ myEntityManager.persist(entity);
+ for (ResourceIndexedSearchParamString next : stringParams) {
+ myEntityManager.persist(next);
+ }
+ for (ResourceIndexedSearchParamToken next : tokenParams) {
+ myEntityManager.persist(next);
+ }
+ for (ResourceIndexedSearchParamNumber next : numberParams) {
+ myEntityManager.persist(next);
+ }
+ for (ResourceIndexedSearchParamDate next : dateParams) {
+ myEntityManager.persist(next);
+ }
+ for (ResourceLink next : links) {
+ myEntityManager.persist(next);
+ }
+ return entity;
+ }
+ });
+
+ MethodOutcome outcome = toMethodOutcome(entity);
+ return outcome;
+ }
+
+ private List extractResourceLinks(X theEntity, T theResource) {
+ ArrayList retVal = new ArrayList();
+
+ RuntimeResourceDefinition def = myCtx.getResourceDefinition(theResource);
+ FhirTerser t = myCtx.newTerser();
+ for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
+ if (nextSpDef.getParamType() != SearchParamTypeEnum.REFERENCE) {
+ continue;
+ }
+
+ String nextPath = nextSpDef.getPath();
+
+ boolean multiType = false;
+ if (nextPath.endsWith("[x]")) {
+ multiType = true;
+ }
+
+ List