diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
index 1164963c477..e4f0fb505b7 100644
--- a/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
+++ b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
@@ -181,8 +181,8 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java
index e12d5906b79..70f6b4a926f 100644
--- a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java
+++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java
@@ -60,7 +60,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
- LocalContainerEntityManagerFactoryBean retVal = entityManagerFactory();
+ LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
retVal.setPersistenceUnitName("HAPI_PU");
retVal.setDataSource(dataSource());
retVal.setJpaProperties(jpaProperties());
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml b/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
index e4d5ebb0734..7277e817e6a 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhirhapi-fhir3.4.0-SNAPSHOT
- ../pom.xml
+ ../../pom.xmlhapi-fhir-jpaserver-dynamic
diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index 4ec22a88188..587ca67c7d3 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -100,8 +100,8 @@
javax.mail-api
- com.phloc
- phloc-commons
+ com.helger
+ ph-schematroncommons-logging
@@ -140,6 +140,7 @@
changelog.txtjavac.batabout.html
+ changelog.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index 435a965f6ea..7e0641341c3 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -45,27 +45,14 @@
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontrue
-
-
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstrue
-
-
-
- com.google.code.findbugs
- annotations
-
-
- javax.xml.bind
- jaxb-api
- ${jaxb_api_version}
-
-
- com.sun.xml.bind
- jaxb-core
- ${jaxb_core_version}
-
-
- com.sun.xml.bind
- jaxb-impl
- ${jaxb_core_version}
+ org.glassfish.jaxb
+ jaxb-runtime
+ ${jaxb_runtime_version}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index 6ea8c368ea3..4e5d499a599 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -73,7 +73,10 @@ import org.hl7.fhir.r4.model.BaseResource;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.Reference;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
@@ -100,7 +103,7 @@ import static org.apache.commons.lang3.StringUtils.*;
@SuppressWarnings("WeakerAccess")
@Repository
-public abstract class BaseHapiFhirDao implements IDao {
+public abstract class BaseHapiFhirDao implements IDao, ApplicationContextAware {
public static final long INDEX_STATUS_INDEXED = 1L;
public static final long INDEX_STATUS_INDEXING_FAILED = 2L;
@@ -186,9 +189,6 @@ public abstract class BaseHapiFhirDao implements IDao {
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
@Autowired
- private List> myResourceDaos;
- private Map, IFhirResourceDao>> myResourceTypeToDao;
- @Autowired
private ISearchDao mySearchDao;
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
@@ -200,6 +200,8 @@ public abstract class BaseHapiFhirDao implements IDao {
private ISearchResultDao mySearchResultDao;
@Autowired
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ private ApplicationContext myApplicationContext;
+ private Map, IFhirResourceDao>> myResourceTypeToDao;
protected void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
if (theRequestDetails != null) {
@@ -695,6 +697,29 @@ public abstract class BaseHapiFhirDao implements IDao {
return retVal;
}
+
+ @SuppressWarnings("unchecked")
+ public IFhirResourceDao getDao(Class theType) {
+ if (myResourceTypeToDao == null) {
+ Map, IFhirResourceDao>> theResourceTypeToDao = new HashMap<>();
+ Map daos = myApplicationContext.getBeansOfType(IFhirResourceDao.class, false, false);
+ for (IFhirResourceDao> next : daos.values()) {
+ theResourceTypeToDao.put(next.getResourceType(), next);
+ }
+
+ if (this instanceof IFhirResourceDao>) {
+ IFhirResourceDao> thiz = (IFhirResourceDao>) this;
+ theResourceTypeToDao.put(thiz.getResourceType(), thiz);
+ }
+
+ myResourceTypeToDao = theResourceTypeToDao;
+ }
+
+ IFhirResourceDao dao = (IFhirResourceDao) myResourceTypeToDao.get(theType);
+ return dao;
+ }
+
+
protected Set extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
}
@@ -896,6 +921,11 @@ public abstract class BaseHapiFhirDao implements IDao {
return myConfig;
}
+ @Override
+ public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
+ myApplicationContext = theApplicationContext;
+ }
+
public void setConfig(DaoConfig theConfig) {
myConfig = theConfig;
}
@@ -922,26 +952,6 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
- @SuppressWarnings("unchecked")
- public IFhirResourceDao getDao(Class theType) {
- if (myResourceTypeToDao == null) {
- Map, IFhirResourceDao>> theResourceTypeToDao = new HashMap<>();
- for (IFhirResourceDao> next : myResourceDaos) {
- theResourceTypeToDao.put(next.getResourceType(), next);
- }
-
- if (this instanceof IFhirResourceDao>) {
- IFhirResourceDao> thiz = (IFhirResourceDao>) this;
- theResourceTypeToDao.put(thiz.getResourceType(), thiz);
- }
-
- myResourceTypeToDao = theResourceTypeToDao;
- }
-
- IFhirResourceDao dao = (IFhirResourceDao) myResourceTypeToDao.get(theType);
- return dao;
- }
-
public IResourceIndexedCompositeStringUniqueDao getResourceIndexedCompositeStringUniqueDao() {
return myResourceIndexedCompositeStringUniqueDao;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
index 70fe17ac5da..144f62c77e6 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
@@ -159,7 +159,7 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao getResourceCounts() {
Map retVal = new HashMap<>();
@@ -172,7 +172,7 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao getResourceCountsFromCache() {
@@ -265,7 +265,7 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implements ISearchParamRegistry {
+public abstract class BaseSearchParamRegistry implements ISearchParamRegistry, ApplicationContextAware {
private static final int MAX_MANAGED_PARAM_COUNT = 10000;
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
@@ -49,12 +52,12 @@ public abstract class BaseSearchParamRegistry implemen
private volatile Map, List>> myActiveParamNamesToUniqueSearchParams = Collections.emptyMap();
@Autowired
private FhirContext myCtx;
- @Autowired
- private Collection> myDaos;
+ private Collection> myResourceDaos;
private volatile Map> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
+ private ApplicationContext myApplicationContext;
public BaseSearchParamRegistry() {
super();
@@ -212,7 +215,13 @@ public abstract class BaseSearchParamRegistry implemen
public void postConstruct() {
Map> resourceNameToSearchParams = new HashMap<>();
- for (IFhirResourceDao> nextDao : myDaos) {
+ myResourceDaos = new ArrayList<>();
+ Map daos = myApplicationContext.getBeansOfType(IFhirResourceDao.class, false, false);
+ for (IFhirResourceDao next : daos.values()) {
+ myResourceDaos.add(next);
+ }
+
+ for (IFhirResourceDao> nextDao : myResourceDaos) {
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType());
String nextResourceName = nextResDef.getName();
HashMap nameToParam = new HashMap<>();
@@ -321,6 +330,11 @@ public abstract class BaseSearchParamRegistry implemen
refreshCacheIfNecessary();
}
+ @Override
+ public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
+ myApplicationContext = theApplicationContext;
+ }
+
protected abstract RuntimeSearchParam toRuntimeSp(SP theNextSp);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java
index 7b6e38baeea..e9deeca33da 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java
@@ -33,6 +33,8 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,6 +60,7 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2 {
Bundle subRequestBundle = new Bundle();
subRequestBundle.setType(BundleTypeEnum.TRANSACTION);
subRequestBundle.addEntry(nextRequestEntry);
-
- Bundle subResponseBundle = transaction((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
- return subResponseBundle;
+ return transaction((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
}
};
BaseServerResponseException caughtEx;
try {
- Bundle nextResponseBundle = txTemplate.execute(callback);
+ Bundle nextResponseBundle;
+ if (nextRequestEntry.getRequest().getMethodElement().getValueAsEnum() == HTTPVerbEnum.GET) {
+ // Don't process GETs in a transaction because they'll
+ // create their own
+ nextResponseBundle = callback.doInTransaction(null);
+ } else {
+ nextResponseBundle = txTemplate.execute(callback);
+ }
caughtEx = null;
Entry subResponseEntry = nextResponseBundle.getEntry().get(0);
@@ -156,13 +162,17 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
return batch(theRequestDetails, theRequest);
}
- if (transactionType == null) {
- String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleTypeEnum.TRANSACTION.getCode();
+ return doTransaction(theRequestDetails, theRequest, theActionName, transactionType);
+ }
+
+ private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, BundleTypeEnum theTransactionType) {
+ if (theTransactionType == null) {
+ String message = "Transaction Bundle did not specify valid Bundle.type, assuming " + BundleTypeEnum.TRANSACTION.getCode();
ourLog.warn(message);
- transactionType = BundleTypeEnum.TRANSACTION;
+ theTransactionType = BundleTypeEnum.TRANSACTION;
}
- if (transactionType != BundleTypeEnum.TRANSACTION) {
- throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + transactionType.getCode());
+ if (theTransactionType != BundleTypeEnum.TRANSACTION) {
+ throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + theTransactionType.getCode());
}
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
@@ -186,7 +196,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
*/
Bundle response = new Bundle();
List getEntries = new ArrayList();
- IdentityHashMap originalRequestOrder = new IdentityHashMap();
+ IdentityHashMap originalRequestOrder = new IdentityHashMap();
for (int i = 0; i < theRequest.getEntry().size(); i++) {
originalRequestOrder.put(theRequest.getEntry().get(i), i);
response.addEntry();
@@ -203,225 +213,13 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
Set updatedEntities = new HashSet<>();
/*
- * Loop through the request and process any entries of type
- * PUT, POST or DELETE
+ * Handle: GET/PUT/POST
*/
- for (int i = 0; i < theRequest.getEntry().size(); i++) {
-
- if (i % 100 == 0) {
- ourLog.debug("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size());
- }
-
- Entry nextReqEntry = theRequest.getEntry().get(i);
- IResource res = nextReqEntry.getResource();
- IdDt nextResourceId = null;
- if (res != null) {
-
- nextResourceId = res.getId();
-
- if (!nextResourceId.hasIdPart()) {
- if (isNotBlank(nextReqEntry.getFullUrl())) {
- nextResourceId = new IdDt(nextReqEntry.getFullUrl());
- }
- }
-
- if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+\\:.*") && !isPlaceholder(nextResourceId)) {
- throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
- }
-
- if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
- nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
- res.setId(nextResourceId);
- }
-
- /*
- * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
- */
- if (isPlaceholder(nextResourceId)) {
- if (!allIds.add(nextResourceId)) {
- throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
- }
- } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
- IdDt nextId = nextResourceId.toUnqualifiedVersionless();
- if (!allIds.add(nextId)) {
- throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
- }
- }
-
- }
-
- HTTPVerbEnum verb = nextReqEntry.getRequest().getMethodElement().getValueAsEnum();
- if (verb == null) {
- throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextReqEntry.getRequest().getMethod()));
- }
-
- String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
- Entry nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry));
-
- switch (verb) {
- case POST: {
- // CREATE
- @SuppressWarnings("rawtypes")
- IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
- res.setId((String) null);
- DaoMethodOutcome outcome;
- outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails);
- handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
- entriesToProcess.put(nextRespEntry, outcome.getEntity());
- if (outcome.getCreated() == false) {
- nonUpdatedEntities.add(outcome.getEntity());
- }
- break;
- }
- case DELETE: {
- // DELETE
- String url = extractTransactionUrlOrThrowException(nextReqEntry, verb);
- UrlParts parts = UrlUtil.parseUrl(url);
- ca.uhn.fhir.jpa.dao.IFhirResourceDao extends IBaseResource> dao = toDao(parts, verb.getCode(), url);
- int status = Constants.STATUS_HTTP_204_NO_CONTENT;
- if (parts.getResourceId() != null) {
- DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), deleteConflicts, theRequestDetails);
- if (outcome.getEntity() != null) {
- deletedResources.add(outcome.getId().toUnqualifiedVersionless());
- entriesToProcess.put(nextRespEntry, outcome.getEntity());
- }
- } else {
- DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams(), deleteConflicts, theRequestDetails);
- List allDeleted = deleteOutcome.getDeletedEntities();
- for (ResourceTable deleted : allDeleted) {
- deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless());
- }
- if (allDeleted.isEmpty()) {
- status = Constants.STATUS_HTTP_404_NOT_FOUND;
- }
- }
-
- nextRespEntry.getResponse().setStatus(toStatusString(status));
- break;
- }
- case PUT: {
- // UPDATE
- @SuppressWarnings("rawtypes")
- IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
-
- DaoMethodOutcome outcome;
-
- String url = extractTransactionUrlOrThrowException(nextReqEntry, verb);
-
- UrlParts parts = UrlUtil.parseUrl(url);
- if (isNotBlank(parts.getResourceId())) {
- res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
- outcome = resourceDao.update(res, null, false, theRequestDetails);
- } else {
- res.setId((String) null);
- outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false, theRequestDetails);
- }
-
- if (outcome.getCreated() == Boolean.FALSE) {
- updatedEntities.add(outcome.getEntity());
- }
-
- handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
- entriesToProcess.put(nextRespEntry, outcome.getEntity());
- break;
- }
- case GET:
- break;
- }
- }
-
- /*
- * Make sure that there are no conflicts from deletions. E.g. we can't delete something
- * if something else has a reference to it.. Unless the thing that has a reference to it
- * was also deleted as a part of this transaction, which is why we check this now at the
- * end.
- */
-
- deleteConflicts.removeIf(next -> deletedResources.contains(next.getTargetId().toVersionless()));
- validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
-
- /*
- * Perform ID substitutions and then index each resource we have saved
- */
-
- FhirTerser terser = getContext().newTerser();
- for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
- IResource nextResource = (IResource) nextOutcome.getResource();
- if (nextResource == null) {
- continue;
- }
-
- // References
- List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
- for (BaseResourceReferenceDt nextRef : allRefs) {
- IdDt nextId = nextRef.getReference();
- if (!nextId.hasIdPart()) {
- continue;
- }
- if (idSubstitutions.containsKey(nextId)) {
- IdDt newId = idSubstitutions.get(nextId);
- ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
- nextRef.setReference(newId);
- } else {
- ourLog.debug(" * Reference [{}] does not exist in bundle", nextId);
- }
- }
-
- // URIs
- List allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriDt.class);
- for (UriDt nextRef : allUris) {
- if (nextRef instanceof IIdType) {
- continue; // No substitution on the resource ID itself!
- }
- IdDt nextUriString = new IdDt(nextRef.getValueAsString());
- if (idSubstitutions.containsKey(nextUriString)) {
- IdDt newId = idSubstitutions.get(nextUriString);
- ourLog.debug(" * Replacing resource ref {} with {}", nextUriString, newId);
- nextRef.setValue(newId.getValue());
- } else {
- ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString);
- }
- }
-
-
- InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
- Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
- if (updatedEntities.contains(nextOutcome.getEntity())) {
- updateInternal(theRequestDetails, nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
- } else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) {
- updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime, false, true);
- }
- }
-
- myEntityManager.flush();
-
- /*
- * Double check we didn't allow any duplicates we shouldn't have
- */
- for (Entry nextEntry : theRequest.getEntry()) {
- if (nextEntry.getRequest().getMethodElement().getValueAsEnum() == HTTPVerbEnum.POST) {
- String matchUrl = nextEntry.getRequest().getIfNoneExist();
- if (isNotBlank(matchUrl)) {
- IFhirResourceDao> resourceDao = getDao(nextEntry.getResource().getClass());
- Set val = resourceDao.processMatchUrl(matchUrl);
- if (val.size() > 1) {
- throw new InvalidRequestException(
- "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
- }
- }
- }
- }
-
- for (IdDt next : allIds) {
- IdDt replacement = idSubstitutions.get(next);
- if (replacement == null) {
- continue;
- }
- if (replacement.equals(next)) {
- continue;
- }
- ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
- }
+ TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
+ txTemplate.execute(t->{
+ handleTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, deletedResources, deleteConflicts, entriesToProcess, nonUpdatedEntities, updatedEntities);
+ return null;
+ });
/*
* Loop through the request and process any entries of type GET
@@ -447,7 +245,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
- for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) {
+ for (Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.addParameter(nextParamEntry.getKey(), nextValue);
}
@@ -490,10 +288,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
}
- ourLog.info("Flushing context after {}", theActionName);
- myEntityManager.flush();
-
- for (java.util.Map.Entry nextEntry : entriesToProcess.entrySet()) {
+ for (Map.Entry nextEntry : entriesToProcess.entrySet()) {
nextEntry.getKey().getResponse().setLocation(nextEntry.getValue().getIdDt().toUnqualified().getValue());
nextEntry.getKey().getResponse().setEtag(nextEntry.getValue().getIdDt().getVersionIdPart());
}
@@ -507,6 +302,229 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
return response;
}
+ private void handleTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set theAllIds, Map theIdSubstitutions, Map theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap theOriginalRequestOrder, List theDeletedResources, List theDeleteConflicts, Map theEntriesToProcess, Set theNonUpdatedEntities, Set theUpdatedEntities) {
+ /*
+ * Loop through the request and process any entries of type
+ * PUT, POST or DELETE
+ */
+ for (int i = 0; i < theRequest.getEntry().size(); i++) {
+
+ if (i % 100 == 0) {
+ ourLog.debug("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size());
+ }
+
+ Entry nextReqEntry = theRequest.getEntry().get(i);
+ IResource res = nextReqEntry.getResource();
+ IdDt nextResourceId = null;
+ if (res != null) {
+
+ nextResourceId = res.getId();
+
+ if (!nextResourceId.hasIdPart()) {
+ if (isNotBlank(nextReqEntry.getFullUrl())) {
+ nextResourceId = new IdDt(nextReqEntry.getFullUrl());
+ }
+ }
+
+ if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+\\:.*") && !isPlaceholder(nextResourceId)) {
+ throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
+ }
+
+ if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
+ nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
+ res.setId(nextResourceId);
+ }
+
+ /*
+ * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
+ */
+ if (isPlaceholder(nextResourceId)) {
+ if (!theAllIds.add(nextResourceId)) {
+ throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
+ }
+ } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
+ IdDt nextId = nextResourceId.toUnqualifiedVersionless();
+ if (!theAllIds.add(nextId)) {
+ throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
+ }
+ }
+
+ }
+
+ HTTPVerbEnum verb = nextReqEntry.getRequest().getMethodElement().getValueAsEnum();
+ if (verb == null) {
+ throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextReqEntry.getRequest().getMethod()));
+ }
+
+ String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
+ Entry nextRespEntry = theResponse.getEntry().get(theOriginalRequestOrder.get(nextReqEntry));
+
+ switch (verb) {
+ case POST: {
+ // CREATE
+ @SuppressWarnings("rawtypes")
+ IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
+ res.setId((String) null);
+ DaoMethodOutcome outcome;
+ outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails);
+ handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
+ theEntriesToProcess.put(nextRespEntry, outcome.getEntity());
+ if (outcome.getCreated() == false) {
+ theNonUpdatedEntities.add(outcome.getEntity());
+ }
+ break;
+ }
+ case DELETE: {
+ // DELETE
+ String url = extractTransactionUrlOrThrowException(nextReqEntry, verb);
+ UrlParts parts = UrlUtil.parseUrl(url);
+ IFhirResourceDao extends IBaseResource> dao = toDao(parts, verb.getCode(), url);
+ int status = Constants.STATUS_HTTP_204_NO_CONTENT;
+ if (parts.getResourceId() != null) {
+ DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), theDeleteConflicts, theRequestDetails);
+ if (outcome.getEntity() != null) {
+ theDeletedResources.add(outcome.getId().toUnqualifiedVersionless());
+ theEntriesToProcess.put(nextRespEntry, outcome.getEntity());
+ }
+ } else {
+ DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams(), theDeleteConflicts, theRequestDetails);
+ List allDeleted = deleteOutcome.getDeletedEntities();
+ for (ResourceTable deleted : allDeleted) {
+ theDeletedResources.add(deleted.getIdDt().toUnqualifiedVersionless());
+ }
+ if (allDeleted.isEmpty()) {
+ status = Constants.STATUS_HTTP_404_NOT_FOUND;
+ }
+ }
+
+ nextRespEntry.getResponse().setStatus(toStatusString(status));
+ break;
+ }
+ case PUT: {
+ // UPDATE
+ @SuppressWarnings("rawtypes")
+ IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
+
+ DaoMethodOutcome outcome;
+
+ String url = extractTransactionUrlOrThrowException(nextReqEntry, verb);
+
+ UrlParts parts = UrlUtil.parseUrl(url);
+ if (isNotBlank(parts.getResourceId())) {
+ res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
+ outcome = resourceDao.update(res, null, false, theRequestDetails);
+ } else {
+ res.setId((String) null);
+ outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false, theRequestDetails);
+ }
+
+ if (outcome.getCreated() == Boolean.FALSE) {
+ theUpdatedEntities.add(outcome.getEntity());
+ }
+
+ handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
+ theEntriesToProcess.put(nextRespEntry, outcome.getEntity());
+ break;
+ }
+ case GET:
+ break;
+ }
+ }
+
+ /*
+ * Make sure that there are no conflicts from deletions. E.g. we can't delete something
+ * if something else has a reference to it.. Unless the thing that has a reference to it
+ * was also deleted as a part of this transaction, which is why we check this now at the
+ * end.
+ */
+
+ theDeleteConflicts.removeIf(next -> theDeletedResources.contains(next.getTargetId().toVersionless()));
+ validateDeleteConflictsEmptyOrThrowException(theDeleteConflicts);
+
+ /*
+ * Perform ID substitutions and then index each resource we have saved
+ */
+
+ FhirTerser terser = getContext().newTerser();
+ for (DaoMethodOutcome nextOutcome : theIdToPersistedOutcome.values()) {
+ IResource nextResource = (IResource) nextOutcome.getResource();
+ if (nextResource == null) {
+ continue;
+ }
+
+ // References
+ List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
+ for (BaseResourceReferenceDt nextRef : allRefs) {
+ IdDt nextId = nextRef.getReference();
+ if (!nextId.hasIdPart()) {
+ continue;
+ }
+ if (theIdSubstitutions.containsKey(nextId)) {
+ IdDt newId = theIdSubstitutions.get(nextId);
+ ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
+ nextRef.setReference(newId);
+ } else {
+ ourLog.debug(" * Reference [{}] does not exist in bundle", nextId);
+ }
+ }
+
+ // URIs
+ List allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriDt.class);
+ for (UriDt nextRef : allUris) {
+ if (nextRef instanceof IIdType) {
+ continue; // No substitution on the resource ID itself!
+ }
+ IdDt nextUriString = new IdDt(nextRef.getValueAsString());
+ if (theIdSubstitutions.containsKey(nextUriString)) {
+ IdDt newId = theIdSubstitutions.get(nextUriString);
+ ourLog.debug(" * Replacing resource ref {} with {}", nextUriString, newId);
+ nextRef.setValue(newId.getValue());
+ } else {
+ ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString);
+ }
+ }
+
+
+ InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
+ Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
+ if (theUpdatedEntities.contains(nextOutcome.getEntity())) {
+ updateInternal(theRequestDetails, nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource());
+ } else if (!theNonUpdatedEntities.contains(nextOutcome.getEntity())) {
+ updateEntity(theRequestDetails, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true);
+ }
+ }
+
+ myEntityManager.flush();
+
+ /*
+ * Double check we didn't allow any duplicates we shouldn't have
+ */
+ for (Entry nextEntry : theRequest.getEntry()) {
+ if (nextEntry.getRequest().getMethodElement().getValueAsEnum() == HTTPVerbEnum.POST) {
+ String matchUrl = nextEntry.getRequest().getIfNoneExist();
+ if (isNotBlank(matchUrl)) {
+ IFhirResourceDao> resourceDao = getDao(nextEntry.getResource().getClass());
+ Set val = resourceDao.processMatchUrl(matchUrl);
+ if (val.size() > 1) {
+ throw new InvalidRequestException(
+ "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
+ }
+ }
+ }
+ }
+
+ for (IdDt next : theAllIds) {
+ IdDt replacement = theIdSubstitutions.get(next);
+ if (replacement == null) {
+ continue;
+ }
+ if (replacement.equals(next)) {
+ continue;
+ }
+ ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
+ }
+ }
+
private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) {
String url = nextEntry.getRequest().getUrl();
if (isBlank(url)) {
@@ -595,7 +613,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
return retVal;
}
- @Transactional(propagation = Propagation.REQUIRED)
+ @Transactional(propagation = Propagation.NEVER)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
if (theRequestDetails != null) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
index a24fa216865..0b8fe0975a8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
+import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants;
@@ -41,6 +42,7 @@ import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
@@ -50,12 +52,15 @@ import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
-public class FulltextSearchSvcImpl extends BaseHapiFhirDao implements IFulltextSearchSvc {
+public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FulltextSearchSvcImpl.class);
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager myEntityManager;
+ @Autowired
+ protected IForcedIdDao myForcedIdDao;
+
/**
* Constructor
*/
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
index 599f96e4476..51d7ac83894 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
@@ -112,8 +112,6 @@ public interface IFhirResourceDao extends IDao {
TagList getAllResourceTags(RequestDetails theRequestDetails);
- IFhirResourceDao getDao(Class theType);
-
Class getResourceType();
TagList getTags(IIdType theResourceId, RequestDetails theRequestDetails);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
index 2a1e88a81af..8d75646bb01 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java
@@ -25,8 +25,6 @@ import ca.uhn.fhir.jpa.util.ExpungeOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nullable;
import java.util.Date;
@@ -40,12 +38,13 @@ public interface IFhirSystemDao extends IDao {
ExpungeOutcome expunge(ExpungeOptions theExpungeOptions);
+ @SuppressWarnings("unchecked")
IFhirResourceDao getDao(Class theType);
Map getResourceCounts();
/**
- *Returns a cached count of resources using a cache that regularly
+ * Returns a cached count of resources using a cache that regularly
* refreshes in the background. This method will never
*/
@Nullable
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java
index dd41bc48524..86bd6fa3f9f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java
@@ -14,6 +14,8 @@ import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.dstu3.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@@ -63,6 +65,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 myStructureDefinitionDao;
-
- @Autowired
- @Qualifier("myValueSetDaoDstu3")
private IFhirResourceDao myValueSetDao;
-
- @Autowired
- @Qualifier("myQuestionnaireDaoDstu3")
private IFhirResourceDao myQuestionnaireDao;
-
- @Autowired
- @Qualifier("myCodeSystemDaoDstu3")
private IFhirResourceDao myCodeSystemDao;
-
@Autowired
private FhirContext myDstu3Ctx;
+ private ApplicationContext myApplicationContext;
+ /**
+ * Constructor
+ */
public JpaValidationSupportDstu3() {
super();
}
-
@Override
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
@@ -168,6 +162,19 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
return false;
}
+ @Override
+ public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
+ myApplicationContext = theApplicationContext;
+ }
+
+ @PostConstruct
+ public void start() {
+ myStructureDefinitionDao = myApplicationContext.getBean("myStructureDefinitionDaoDstu3", IFhirResourceDao.class);
+ myValueSetDao = myApplicationContext.getBean("myValueSetDaoDstu3", IFhirResourceDao.class);
+ myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoDstu3", IFhirResourceDao.class);
+ myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoDstu3", IFhirResourceDao.class);
+ }
+
@Override
@Transactional(value = TxType.SUPPORTS)
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java
index 91861ac567b..9be9b5988c1 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java
@@ -13,6 +13,8 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@@ -57,6 +59,7 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4 myStructureDefinitionDao;
-
- @Autowired
- @Qualifier("myValueSetDaoR4")
private IFhirResourceDao myValueSetDao;
-
- @Autowired
- @Qualifier("myQuestionnaireDaoR4")
private IFhirResourceDao myQuestionnaireDao;
-
- @Autowired
- @Qualifier("myCodeSystemDaoR4")
private IFhirResourceDao myCodeSystemDao;
@Autowired
private FhirContext myR4Ctx;
+ private ApplicationContext myApplicationContext;
+ /**
+ * Constructor
+ */
public JpaValidationSupportR4() {
super();
}
@@ -163,6 +159,19 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
return false;
}
+ @Override
+ public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
+ myApplicationContext = theApplicationContext;
+ }
+
+ @PostConstruct
+ public void start() {
+ myStructureDefinitionDao = myApplicationContext.getBean("myStructureDefinitionDaoR4", IFhirResourceDao.class);
+ myValueSetDao = myApplicationContext.getBean("myValueSetDaoR4", IFhirResourceDao.class);
+ myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoR4", IFhirResourceDao.class);
+ myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoR4", IFhirResourceDao.class);
+ }
+
@Override
@Transactional(value = TxType.SUPPORTS)
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java
index cbf41cf6c42..44640269b60 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java
@@ -77,7 +77,7 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
}
@Override
- @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ @Transactional(propagation = Propagation.NEVER)
public void pollForStaleSearchesAndDeleteThem() {
if (!myDaoConfig.isExpireSearchResults()) {
return;
@@ -127,7 +127,7 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
}
@Scheduled(fixedDelay = DEFAULT_CUTOFF_SLACK)
- @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ @Transactional(propagation = Propagation.NEVER)
@Override
public synchronized void schedulePollForStaleSearches() {
if (!myDaoConfig.isSchedulingDisabled()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java
index 3b3dfcaf3a7..800d88b1b48 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java
@@ -67,7 +67,7 @@ public abstract class BaseSubscriptionDeliverySubscriber extends BaseSubscriptio
// can have placeholder IDs in them.
IIdType payloadId = msg.getPayloadId(getContext());
Class type = getContext().getResourceDefinition(payloadId.getResourceType()).getImplementingClass();
- IFhirResourceDao dao = getSubscriptionDao().getDao(type);
+ IFhirResourceDao dao = getSubscriptionInterceptor().getDao(type);
IBaseResource loadedPayload;
try {
loadedPayload = dao.read(payloadId);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java
index c427ed579a9..d5b76d15448 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java
@@ -28,7 +28,6 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.jpa.util.JpaConstants;
-import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@@ -37,6 +36,7 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
+import ca.uhn.fhir.util.StopWatch;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
@@ -99,6 +99,7 @@ public abstract class BaseSubscriptionInterceptor exten
@Autowired
@Qualifier(BaseConfig.TASK_EXECUTOR_NAME)
private AsyncTaskExecutor myAsyncTaskExecutor;
+ private Map, IFhirResourceDao>> myResourceTypeToDao;
/**
* Constructor
@@ -243,6 +244,26 @@ public abstract class BaseSubscriptionInterceptor exten
public abstract Subscription.SubscriptionChannelType getChannelType();
+ @SuppressWarnings("unchecked")
+ public IFhirResourceDao getDao(Class theType) {
+ if (myResourceTypeToDao == null) {
+ Map, IFhirResourceDao>> theResourceTypeToDao = new HashMap<>();
+ for (IFhirResourceDao> next : myResourceDaos) {
+ theResourceTypeToDao.put(next.getResourceType(), next);
+ }
+
+ if (this instanceof IFhirResourceDao>) {
+ IFhirResourceDao> thiz = (IFhirResourceDao>) this;
+ theResourceTypeToDao.put(thiz.getResourceType(), thiz);
+ }
+
+ myResourceTypeToDao = theResourceTypeToDao;
+ }
+
+ IFhirResourceDao dao = (IFhirResourceDao) myResourceTypeToDao.get(theType);
+ return dao;
+ }
+
public SubscribableChannel getDeliveryChannel() {
return myDeliveryChannel;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionCheckingSubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionCheckingSubscriber.java
index 054da70044b..af5678ce1b0 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionCheckingSubscriber.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionCheckingSubscriber.java
@@ -140,7 +140,7 @@ public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber {
RequestDetails req = new ServletSubRequestDetails();
req.setSubRequest(true);
- IFhirResourceDao extends IBaseResource> responseDao = getSubscriptionDao().getDao(responseResourceDef.getImplementingClass());
+ IFhirResourceDao extends IBaseResource> responseDao = getSubscriptionInterceptor().getDao(responseResourceDef.getImplementingClass());
responseCriteriaUrl.setLoadSynchronousUpTo(1);
IBundleProvider responseResults = responseDao.search(responseCriteriaUrl, req);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java
index 65881b7d2b3..37ed373598c 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java
@@ -123,7 +123,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
IBaseResource payloadResource = theMsg.getPayload(getContext());
if (theSubscription.getRestHookDetails().isDeliverLatestVersion()) {
- IFhirResourceDao dao = getSubscriptionDao().getDao(payloadResource.getClass());
+ IFhirResourceDao dao = getSubscriptionInterceptor().getDao(payloadResource.getClass());
try {
payloadResource = dao.read(payloadResource.getIdElement().toVersionless());
} catch (ResourceGoneException e) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
index 1c96b893a20..b1153bc5975 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java
@@ -31,7 +31,6 @@ import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
-import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.StopWatch;
@@ -56,7 +55,10 @@ import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled;
@@ -80,7 +82,7 @@ import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
-public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc {
+public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, ApplicationContextAware {
public static final int DEFAULT_FETCH_SIZE = 250;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvcImpl.class);
@@ -123,14 +125,11 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
private boolean myProcessDeferred = true;
@Autowired
private PlatformTransactionManager myTransactionMgr;
- @Autowired(required = false)
private IFhirResourceDaoCodeSystem, ?, ?> myCodeSystemResourceDao;
-
private Cache> myTranslationCache;
private Cache> myTranslationWithReverseCache;
-
-
private int myFetchSize = DEFAULT_FETCH_SIZE;
+ private ApplicationContext myApplicationContext;
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set theAddedCodes, TermConcept theConcept) {
if (theAddedCodes.add(theConcept.getCode())) {
@@ -207,6 +206,32 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
.build();
}
+ /**
+ * This method is present only for unit tests, do not call from client code
+ */
+ @VisibleForTesting
+ public void clearDeferred() {
+ myDeferredValueSets.clear();
+ myDeferredConceptMaps.clear();
+ myDeferredConcepts.clear();
+ }
+
+ /**
+ * This method is present only for unit tests, do not call from client code
+ */
+ @VisibleForTesting
+ public void clearTranslationCache() {
+ myTranslationCache.invalidateAll();
+ }
+
+ /**
+ * This method is present only for unit tests, do not call from client code
+ */
+ @VisibleForTesting()
+ public void clearTranslationWithReverseCache() {
+ myTranslationWithReverseCache.invalidateAll();
+ }
+
protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource);
protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap);
@@ -788,11 +813,21 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
}
+ @Override
+ public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
+ myApplicationContext = theApplicationContext;
+ }
+
@Override
public void setProcessDeferred(boolean theProcessDeferred) {
myProcessDeferred = theProcessDeferred;
}
+ @PostConstruct
+ public void start() {
+ myCodeSystemResourceDao = myApplicationContext.getBean(IFhirResourceDaoCodeSystem.class);
+ }
+
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSystemVersion) {
@@ -1258,32 +1293,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
ourLastResultsFromTranslationWithReverseCache = false;
}
- /**
- * This method is present only for unit tests, do not call from client code
- */
- @VisibleForTesting
- public void clearTranslationCache() {
- myTranslationCache.invalidateAll();
- }
-
- /**
- * This method is present only for unit tests, do not call from client code
- */
- @VisibleForTesting
- public void clearDeferred() {
- myDeferredValueSets.clear();
- myDeferredConceptMaps.clear();
- myDeferredConcepts.clear();
- }
-
- /**
- * This method is present only for unit tests, do not call from client code
- */
- @VisibleForTesting()
- public void clearTranslationWithReverseCache() {
- myTranslationWithReverseCache.invalidateAll();
- }
-
/**
* This method is present only for unit tests, do not call from client code
*/
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java
index 09dfe3372ab..97fd4da07b6 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java
@@ -113,6 +113,7 @@ public class LoincHandler implements IRecordHandler {
ourLog.warn("Unable to find part code with TYPE[{}] and NAME[{}]", key.getPartType(), key.getPartName());
}
break;
+ case DECIMAL:
case CODE:
case INTEGER:
case BOOLEAN:
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java
index cd9fc5b444d..bd2f194cd77 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java
@@ -252,6 +252,7 @@ public class ConnectionWrapper implements Connection {
@Override
public void setReadOnly(boolean theReadOnly) throws SQLException {
+ ourLog.info("Setting connection as readonly");
myWrap.setReadOnly(theReadOnly);
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
index 23e48a0b305..3de0c6202fe 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java
@@ -4,8 +4,11 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
+import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
+import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource;
-import org.hibernate.jpa.HibernatePersistenceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@@ -15,11 +18,35 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.*;
@Configuration
@EnableTransactionManagement()
public class TestDstu2Config extends BaseJavaConfigDstu2 {
+ private static final Logger ourLog = LoggerFactory.getLogger(TestDstu2Config.class);
+ private static int ourMaxThreads;
+
+ static {
+ /*
+ * We use a randomized number of maximum threads in order to try
+ * and catch any potential deadlocks caused by database connection
+ * starvation
+ */
+ ourMaxThreads = (int) (Math.random() * 6.0) + 1;
+ }
+
+ private Exception myLastStackTrace;
+ private String myLastStackTraceThreadName;
+
+ @Bean(name="maxDatabaseThreadsForTest")
+ public Integer getMaxThread(){
+ return ourMaxThreads;
+ }
@Bean()
public DaoConfig daoConfig() {
@@ -28,21 +55,73 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
@Bean()
public DataSource dataSource() {
- BasicDataSource retVal = new BasicDataSource();
+ BasicDataSource retVal = new BasicDataSource() {
+
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ ConnectionWrapper retVal;
+ try {
+ retVal = new ConnectionWrapper(super.getConnection());
+ } catch (Exception e) {
+ ourLog.error("Exceeded maximum wait for connection", e);
+ logGetConnectionStackTrace();
+
+// if ("true".equals(System.getStringProperty("ci"))) {
+ fail("Exceeded maximum wait for connection: " + e.toString());
+// }
+// System.exit(1);
+ retVal = null;
+ }
+
+ try {
+ throw new Exception();
+ } catch (Exception e) {
+ myLastStackTrace = e;
+ myLastStackTraceThreadName = Thread.currentThread().getName();
+ }
+
+ return retVal;
+ }
+
+ private void logGetConnectionStackTrace() {
+ StringBuilder b = new StringBuilder();
+ b.append("Last connection request stack trace:");
+ for (StackTraceElement next : myLastStackTrace.getStackTrace()) {
+ b.append("\n ");
+ b.append(next.getClassName());
+ b.append(".");
+ b.append(next.getMethodName());
+ b.append("(");
+ b.append(next.getFileName());
+ b.append(":");
+ b.append(next.getLineNumber());
+ b.append(")");
+ }
+ ourLog.info(b.toString());
+ ourLog.info("Last connection thread: {}", myLastStackTraceThreadName);
+ }
+
+ };
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDBDstu2;create=true");
+ retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");
- return retVal;
- }
-
- @Bean()
- public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
- JpaTransactionManager retVal = new JpaTransactionManager();
- retVal.setEntityManagerFactory(entityManagerFactory);
- return retVal;
+
+ retVal.setMaxTotal(ourMaxThreads);
+
+ DataSource dataSource = ProxyDataSourceBuilder
+ .create(retVal)
+// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
+ .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
+ .countQuery(new ThreadQueryCountHolder())
+ .build();
+
+ return dataSource;
}
+ @Override
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
@@ -60,7 +139,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
extraProperties.put("hibernate.dialect", "ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect");
extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "ram");
- extraProperties.put("hibernate.search.lucene_version","LUCENE_CURRENT");
+ extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
return extraProperties;
}
@@ -79,4 +158,11 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
return requestValidator;
}
+ @Bean()
+ public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+ JpaTransactionManager retVal = new JpaTransactionManager();
+ retVal.setEntityManagerFactory(entityManagerFactory);
+ return retVal;
+ }
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
index 19a554590d2..0528c8d806e 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
@@ -20,6 +20,10 @@ import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.jdbc.Work;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@@ -30,6 +34,8 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mockito;
+import org.springframework.orm.hibernate5.HibernateTransactionManager;
+import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
@@ -38,8 +44,11 @@ import org.springframework.transaction.support.TransactionTemplate;
import java.io.IOException;
import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
import static ca.uhn.fhir.util.TestUtil.randomizeLocale;
import static org.junit.Assert.*;
@@ -64,7 +73,7 @@ public abstract class BaseJpaTest {
protected IRequestOperationCallback myRequestOperationCallback = mock(IRequestOperationCallback.class);
@After
- public final void afterPerformCleanup() {
+ public void afterPerformCleanup() {
BaseHapiFhirResourceDao.setDisableIncrementOnUpdateForUnitTest(false);
}
@@ -78,6 +87,32 @@ public abstract class BaseJpaTest {
when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(new ArrayList<>());
}
+ @After
+ public void afterValidateNoTransaction() {
+ PlatformTransactionManager txManager = getTxManager();
+ if (txManager != null) {
+ JpaTransactionManager hibernateTxManager = (JpaTransactionManager) txManager;
+ SessionFactory sessionFactory = (SessionFactory) hibernateTxManager.getEntityManagerFactory();
+ AtomicBoolean isReadOnly = new AtomicBoolean();
+ Session currentSession;
+ try {
+ currentSession = sessionFactory.getCurrentSession();
+ } catch (HibernateException e) {
+ currentSession = null;
+ }
+ if (currentSession != null) {
+ currentSession.doWork(new Work() {
+
+ public void execute(Connection connection) throws SQLException {
+ isReadOnly.set(connection.isReadOnly());
+ }
+ });
+
+ assertFalse(isReadOnly.get());
+ }
+ }
+ }
+
@Before
public void beforeRandomizeLocale() {
randomizeLocale();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirDaoConcurrencyDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirDaoConcurrencyDstu3Test.java
index fd0624ae78f..867ef18fa72 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirDaoConcurrencyDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirDaoConcurrencyDstu3Test.java
@@ -1,10 +1,9 @@
package ca.uhn.fhir.jpa.dao.dstu3;
-import ca.uhn.fhir.jpa.dao.DaoConfig;
-import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.TestUtil;
-import com.phloc.commons.compare.ReverseComparator;
+import org.apache.commons.collections4.comparators.ReverseComparator;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
index 22ea61b467d..a5e11d536c2 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
@@ -44,11 +44,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.TransactionDefinition;
-import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
import java.io.IOException;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java
index 993a1923503..0fff1fceb19 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/BaseResourceProviderDstu2Test.java
@@ -49,6 +49,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor;
protected static DatabaseBackedPagingProvider ourPagingProvider;
protected static PlatformTransactionManager ourTxManager;
+ protected static Integer ourConnectionPoolSize;
public BaseResourceProviderDstu2Test() {
super();
@@ -84,6 +85,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
+ ourConnectionPoolSize = myAppCtx.getBean("maxDatabaseThreadsForTest", Integer.class);
ourRestServer.setPagingProvider(ourPagingProvider);
Server server = new Server(ourPort);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
index 2d56930f026..8574762e9af 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java
@@ -1,41 +1,58 @@
package ca.uhn.fhir.jpa.provider;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.*;
-import java.net.*;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.methods.*;
-import org.apache.http.entity.*;
-import org.hl7.fhir.instance.model.api.IBaseBundle;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.*;
-import org.junit.Test;
-import org.springframework.test.util.AopTestUtils;
-
-import com.google.common.base.Charsets;
-
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
-import ca.uhn.fhir.model.api.*;
-import ca.uhn.fhir.model.dstu2.composite.*;
+import ca.uhn.fhir.model.api.ExtensionDt;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
+import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
+import ca.uhn.fhir.model.dstu2.composite.CodingDt;
+import ca.uhn.fhir.model.dstu2.composite.MetaDt;
+import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
+import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.valueset.*;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.parser.IParser;
-import ca.uhn.fhir.rest.api.*;
+import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.param.*;
-import ca.uhn.fhir.rest.server.exceptions.*;
-import ca.uhn.fhir.util.*;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
+import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
+import ca.uhn.fhir.util.BundleUtil;
+import ca.uhn.fhir.util.StopWatch;
+import ca.uhn.fhir.util.TestUtil;
+import ca.uhn.fhir.util.UrlUtil;
+import com.google.common.base.Charsets;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.*;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.junit.*;
+import org.springframework.test.util.AopTestUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
@@ -50,7 +67,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
-
+
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false);
@@ -107,7 +124,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
}
-
+
@Test
public void testBundleCreateWithTypeTransaction() throws Exception {
IGenericClient client = ourClient;
@@ -182,22 +199,22 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
Bundle found = ourClient
- .search()
- .forResource(Organization.class)
- .where(Organization.NAME.matches().value("rpdstu2_testCountParam_01"))
- .count(10)
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Organization.class)
+ .where(Organization.NAME.matches().value("rpdstu2_testCountParam_01"))
+ .count(10)
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(100, found.getTotalElement().getValue().intValue());
assertEquals(10, found.getEntry().size());
found = ourClient
- .search()
- .forResource(Organization.class)
- .where(Organization.NAME.matches().value("rpdstu2_testCountParam_01"))
- .count(999)
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Organization.class)
+ .where(Organization.NAME.matches().value("rpdstu2_testCountParam_01"))
+ .count(999)
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(100, found.getTotalElement().getValue().intValue());
assertEquals(50, found.getEntry().size());
@@ -208,15 +225,15 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
*/
@Test
public void testCreateAndUpdateBinary() throws ClientProtocolException, Exception {
- byte[] arr = { 1, 21, 74, 123, -44 };
+ byte[] arr = {1, 21, 74, 123, -44};
Binary binary = new Binary();
binary.setContent(arr);
binary.setContentType("dansk");
binary.addUndeclaredExtension(true, "bobobo", new StringDt("hey there"));
-
+
IIdType resource = ourClient.create().resource(binary).execute().getId();
-
+
Binary fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("1", fromDB.getId().getVersionIdPart());
@@ -231,7 +248,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} finally {
IOUtils.closeQuietly(resp);
}
-
+
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("2", fromDB.getId().getVersionIdPart());
@@ -247,12 +264,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} finally {
IOUtils.closeQuietly(resp);
}
-
+
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getId().getVersionIdPart());
// Now an update with the wrong ID in the body
-
+
arr[0] = 4;
binary.setContent(arr);
binary.setId("");
@@ -265,7 +282,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
} finally {
IOUtils.closeQuietly(resp);
}
-
+
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getId().getVersionIdPart());
@@ -441,7 +458,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)",
- oo.getIssue().get(0).getDiagnostics());
+ oo.getIssue().get(0).getDiagnostics());
} finally {
response.getEntity().getContent().close();
response.close();
@@ -494,7 +511,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals(e1id.toUnqualifiedVersionless(), BundleUtil.toListOfResourcesOfType(myFhirCtx, res, Encounter.class).get(0).getIdElement().toUnqualifiedVersionless());
}
-
+
@Test
public void testDeleteConditionalMultiple() {
String methodName = "testDeleteConditionalMultiple";
@@ -522,7 +539,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
fail();
} catch (PreconditionFailedException e) {
assertEquals("HTTP 412 Precondition Failed: Failed to DELETE resource with match URL \"Patient?identifier=testDeleteConditionalMultiple\" because this search matched 2 resources",
- e.getMessage());
+ e.getMessage());
}
// Not deleted yet..
@@ -682,12 +699,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IGenericClient client = ourClient;
int initialSize = client
- .search()
- .forResource(DiagnosticOrder.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DiagnosticOrder.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
DiagnosticOrder res = new DiagnosticOrder();
res.addIdentifier().setSystem("urn:foo").setValue("123");
@@ -695,12 +712,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
client.create().resource(res).execute();
int newSize = client
- .search()
- .forResource(DiagnosticOrder.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DiagnosticOrder.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
assertEquals(1, newSize - initialSize);
@@ -717,23 +734,23 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IGenericClient client = ourClient;
int initialSize = client
- .search()
- .forResource(DocumentManifest.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DocumentManifest.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
String resBody = IOUtils.toString(ResourceProviderDstu2Test.class.getResource("/documentmanifest.json"), StandardCharsets.UTF_8);
client.create().resource(resBody).execute();
int newSize = client
- .search()
- .forResource(DocumentManifest.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DocumentManifest.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
assertEquals(1, newSize - initialSize);
@@ -747,23 +764,23 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IGenericClient client = ourClient;
int initialSize = client
- .search()
- .forResource(DocumentReference.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DocumentReference.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
String resBody = IOUtils.toString(ResourceProviderDstu2Test.class.getResource("/documentreference.json"), Charsets.UTF_8);
client.create().resource(resBody).execute();
int newSize = client
- .search()
- .forResource(DocumentReference.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(DocumentReference.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
assertEquals(1, newSize - initialSize);
@@ -934,7 +951,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
List actual;
StringAndListParam param;
- ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", new Object[] { ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart() });
+ ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", new Object[] {ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart()});
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
@@ -1203,7 +1220,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantDt(new Date(time2)).getValueAsString() + "&_lastUpdated=%3C"
- + new InstantDt(new Date(time3)).getValueAsString());
+ + new InstantDt(new Date(time3)).getValueAsString());
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
@@ -1264,24 +1281,24 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IdDt o2id = (IdDt) ourClient.create().resource(o2).execute().getId().toUnqualifiedVersionless();
Bundle actual = ourClient.search()
- .forResource(Organization.class)
- .withProfile("http://profile1")
- .withProfile("http://profileX")
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Organization.class)
+ .withProfile("http://profile1")
+ .withProfile("http://profileX")
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals("nothing matches profile x", Collections.emptyList(), actual.getEntry());
actual = ourClient.search()
- .forResource(Organization.class)
- .withProfile("http://profile1")
- .withProfile("http://profile2")
- .returnBundle(Bundle.class)
- .encodedJson()
- .prettyPrint()
- .execute();
+ .forResource(Organization.class)
+ .withProfile("http://profile1")
+ .withProfile("http://profile2")
+ .returnBundle(Bundle.class)
+ .encodedJson()
+ .prettyPrint()
+ .execute();
Set expectedIds = new HashSet();
expectedIds.add(o1id.getIdPart());
@@ -1292,13 +1309,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals("Expects to retrieve the 1 orgination matching on Org1's profiles", expectedIds, actualIds);
actual = ourClient.search()
- .forResource(Organization.class)
- .withProfile("http://profile1")
- .withAnyProfile(Arrays.asList("http://profile3", "http://profile2"))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Organization.class)
+ .withProfile("http://profile1")
+ .withAnyProfile(Arrays.asList("http://profile3", "http://profile2"))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
expectedIds = new HashSet();
expectedIds.add(o1id.getIdPart());
@@ -1346,7 +1363,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourClient.update().resource(patient).execute();
ca.uhn.fhir.model.dstu2.resource.Bundle history = ourClient.history().onInstance(id).andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA)
- .execute();
+ .execute();
assertEquals(3, history.getEntry().size());
assertEquals(id.withVersion("3"), history.getEntry().get(0).getResource().getId());
assertEquals(1, ((Patient) history.getEntry().get(0).getResource()).getName().size());
@@ -1367,23 +1384,23 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IGenericClient client = ourClient;
int initialSize = client
- .search()
- .forResource(ImagingStudy.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(ImagingStudy.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
String resBody = IOUtils.toString(ResourceProviderDstu2Test.class.getResource("/imagingstudy.json"), StandardCharsets.UTF_8);
client.create().resource(resBody).execute();
int newSize = client
- .search()
- .forResource(ImagingStudy.class)
- .returnBundle(Bundle.class)
- .execute()
- .getEntry()
- .size();
+ .search()
+ .forResource(ImagingStudy.class)
+ .returnBundle(Bundle.class)
+ .execute()
+ .getEntry()
+ .size();
assertEquals(1, newSize - initialSize);
@@ -1397,14 +1414,14 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
//@formatter:off
String input = "\n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- "";
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
//@formatter:on
HttpPost post = new HttpPost(ourServerBase + "/Patient/" + id.getIdPart() + "/$meta-add");
@@ -1471,6 +1488,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
}
+
@Test
public void testPagingOverEverythingSet() throws InterruptedException {
Patient p = new Patient();
@@ -1488,6 +1506,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(10);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(true);
+ StopWatch sw = new StopWatch();
ca.uhn.fhir.model.dstu2.resource.Bundle response = ourClient
.operation()
.onInstance(new IdDt(pid))
@@ -1498,7 +1517,9 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
.execute();
assertEquals(10, response.getEntry().size());
- assertEquals(null, response.getTotalElement().getValueAsString());
+ if (ourConnectionPoolSize > 1) {
+ assertEquals("Total should be null but was " + response.getTotalElement().getValueAsString() + " in " + sw.toString(), null, response.getTotalElement().getValueAsString());
+ }
assertThat(response.getLink("next").getUrl(), not(emptyString()));
// Load page 2
@@ -1571,21 +1592,21 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourClient.create().resource(pat).prettyPrint().encodedXml().execute().getId();
{
Bundle returned = ourClient
- .search()
- .forResource(Patient.class)
- .returnBundle(Bundle.class)
- .encodedXml()
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .returnBundle(Bundle.class)
+ .encodedXml()
+ .execute();
assertThat(returned.getEntry().size(), greaterThan(1));
assertEquals(ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum.SEARCH_RESULTS, returned.getTypeElement().getValueAsEnum());
}
{
Bundle returned = ourClient
- .search()
- .forResource(Patient.class)
- .encodedJson()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .encodedJson()
+ .returnBundle(Bundle.class)
+ .execute();
assertThat(returned.getEntry().size(), greaterThan(1));
}
}
@@ -1633,12 +1654,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertThat(actual.getText().getDiv().getValueAsString(), containsString("
Identifier
testSaveAndRetrieveWithContained01
"));
Bundle b = ourClient
- .search()
- .forResource("Patient")
- .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01"))
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource("Patient")
+ .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01"))
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(1, b.getEntry().size());
}
@@ -1708,13 +1729,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
//@formatter:off
ca.uhn.fhir.model.dstu2.resource.Bundle actual = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01"))
- .encodedJson()
- .prettyPrint()
- .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01"))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
+ .execute();
//@formatter:on
assertEquals(1, actual.getEntry().size());
@@ -1731,13 +1752,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01"))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01"))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(1, actual.getEntry().size());
assertEquals(p1Id.getIdPart(), actual.getEntry().get(0).getResource().getId().getIdPart());
@@ -1765,12 +1786,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IdDt p2Id = (IdDt) ourClient.create().resource(p2).execute().getId();
Bundle actual = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.ORGANIZATION.hasAnyOfIds(Arrays.asList(o1id.getIdPart(), o2id.getIdPart())))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.ORGANIZATION.hasAnyOfIds(Arrays.asList(o1id.getIdPart(), o2id.getIdPart())))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
Set expectedIds = new HashSet();
expectedIds.add(p1Id.getIdPart());
@@ -1796,23 +1817,23 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.ORGANIZATION.hasId(o1id.getIdPart()))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.ORGANIZATION.hasId(o1id.getIdPart()))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(1, actual.getEntry().size());
assertEquals(p1Id.getIdPart(), actual.getEntry().get(0).getResource().getId().getIdPart());
actual = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.ORGANIZATION.hasId(o1id.getValue()))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.ORGANIZATION.hasId(o1id.getValue()))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(1, actual.getEntry().size());
assertEquals(p1Id.getIdPart(), actual.getEntry().get(0).getResource().getId().getIdPart());
@@ -1857,44 +1878,44 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourLog.info("Before: {}", beforeAny.getValue());
{
Bundle found = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
- .lastUpdated(new DateRangeParam(beforeAny, null))
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
+ .lastUpdated(new DateRangeParam(beforeAny, null))
+ .returnBundle(Bundle.class)
+ .execute();
List patients = toIdListUnqualifiedVersionless(found);
assertThat(patients, hasItems(id1a, id1b, id2));
}
{
Bundle found = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
+ .returnBundle(Bundle.class)
+ .execute();
List patients = toIdListUnqualifiedVersionless(found);
assertThat(patients, hasItems(id1a, id1b, id2));
}
{
Bundle found = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
- .lastUpdated(new DateRangeParam(beforeR2, null))
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
+ .lastUpdated(new DateRangeParam(beforeR2, null))
+ .returnBundle(Bundle.class)
+ .execute();
List patients = toIdListUnqualifiedVersionless(found);
assertThat(patients, hasItems(id2));
assertThat(patients, not(hasItems(id1a, id1b)));
}
{
Bundle found = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
- .lastUpdated(new DateRangeParam(beforeAny, beforeR2))
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
+ .lastUpdated(new DateRangeParam(beforeAny, beforeR2))
+ .returnBundle(Bundle.class)
+ .execute();
List patients = toIdListUnqualifiedVersionless(found);
assertThat(patients.toString(), patients, not(hasItems(id2)));
@@ -1902,11 +1923,11 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
{
Bundle found = ourClient.search()
- .forResource(Patient.class)
- .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
- .lastUpdated(new DateRangeParam(null, beforeR2))
- .returnBundle(Bundle.class)
- .execute();
+ .forResource(Patient.class)
+ .where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
+ .lastUpdated(new DateRangeParam(null, beforeR2))
+ .returnBundle(Bundle.class)
+ .execute();
List patients = toIdListUnqualifiedVersionless(found);
assertThat(patients, (hasItems(id1a, id1b)));
@@ -1951,7 +1972,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertThat(matches, greaterThan(0));
}
-
+
@Test
public void testSearchReturnsSearchDate() throws Exception {
ourLog.info("Starting testSearchReturnsSearchDate");
@@ -1960,11 +1981,11 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
Thread.sleep(100);
ca.uhn.fhir.model.dstu2.resource.Bundle found = ourClient
- .search()
- .forResource(Patient.class)
- .prettyPrint()
- .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .prettyPrint()
+ .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
+ .execute();
Thread.sleep(100);
Date after = new Date();
@@ -1978,7 +1999,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertTrue(value.after(before));
assertTrue(value.before(after));
}
-
+
@Test
public void testSearchWithInclude() throws Exception {
Organization org = new Organization();
@@ -1991,13 +2012,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
ourClient.create().resource(pat).prettyPrint().encodedXml().execute().getId();
Bundle found = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("urn:system:rpdstu2","testSearchWithInclude02"))
- .include(Patient.INCLUDE_ORGANIZATION)
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("urn:system:rpdstu2", "testSearchWithInclude02"))
+ .include(Patient.INCLUDE_ORGANIZATION)
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(2, found.getEntry().size());
assertEquals(Patient.class, found.getEntry().get(0).getResource().getClass());
@@ -2054,13 +2075,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
{
Bundle found = ourClient
- .search()
- .forResource(Organization.class)
- .where(Organization.NAME.isMissing(false))
- .count(100)
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Organization.class)
+ .where(Organization.NAME.isMissing(false))
+ .count(100)
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
List list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + ": " + list.toString());
@@ -2071,13 +2092,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
Bundle found = ourClient
- .search()
- .forResource(Organization.class)
- .where(Organization.NAME.isMissing(true))
- .count(100)
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Organization.class)
+ .where(Organization.NAME.isMissing(true))
+ .count(100)
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
List list = toIdListUnqualifiedVersionless(found);
ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing);
@@ -2094,7 +2115,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
checkParamMissing(Observation.SP_ENCOUNTER);
checkParamMissing(Observation.SP_DATE);
}
-
+
@Test
public void testSearchWithTextInexactMatch() throws Exception {
Observation obs = new Observation();
@@ -2295,7 +2316,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
assertEquals(200, resp.getStatusLine().getStatusCode());
- String output= IOUtils.toString(resp.getEntity().getContent());
+ String output = IOUtils.toString(resp.getEntity().getContent());
ourLog.info(output);
} finally {
resp.close();
@@ -2387,7 +2408,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
response.close();
}
- pt.addName().addFamily(methodName+"2");
+ pt.addName().addFamily(methodName + "2");
resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut put = new HttpPut(ourServerBase + "/Patient?name=" + methodName);
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
@@ -2493,7 +2514,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
}
-
+
@Test
public void testUpdateWithClientSuppliedIdWhichDoesntExist() {
Patient p1 = new Patient();
@@ -2505,13 +2526,13 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2/_history"));
Bundle actual = ourClient
- .search()
- .forResource(Patient.class)
- .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2"))
- .encodedJson()
- .prettyPrint()
- .returnBundle(Bundle.class)
- .execute();
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExistRpDstu2"))
+ .encodedJson()
+ .prettyPrint()
+ .returnBundle(Bundle.class)
+ .execute();
assertEquals(1, actual.getEntry().size());
assertEquals(p1Id.getIdPart(), actual.getEntry().get(0).getResource().getId().getIdPart());
@@ -2578,7 +2599,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
}
-
+
/**
* From a Skype message from Brian Postlethwaite
*/
@@ -2725,18 +2746,18 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertThat(resp,
stringContainsInOrder("",
"",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
""
- ));
+ ));
//@formatter:on
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
@@ -2755,8 +2776,8 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals(200, response.getStatusLine().getStatusCode());
//@formatter:off
assertThat(resp, stringContainsInOrder(
- "",
- ""));
+ "",
+ ""));
//@formatter:on
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
@@ -2775,9 +2796,9 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
assertEquals(200, response.getStatusLine().getStatusCode());
//@formatter:off
assertThat(resp, stringContainsInOrder(
- "",
- ""
- ));
+ "",
+ ""
+ ));
//@formatter:on
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
@@ -2785,7 +2806,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
}
-
+
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java
index 6489ad95d90..920febf026c 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java
@@ -11,14 +11,12 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.UriType;
-import org.junit.After;
import org.junit.AfterClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@@ -102,16 +100,14 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
.addTarget()
.setCode("34567");
- runInTransaction(() -> {
- try {
- runInTransaction(() -> {
- myConceptMapDao.create(conceptMap);
- });
- fail();
- } catch (UnprocessableEntityException e) {
- assertEquals("ConceptMap has no value for ConceptMap.url", e.getMessage());
- }
- });
+ try {
+ runInTransaction(() -> {
+ myConceptMapDao.create(conceptMap);
+ });
+ fail();
+ } catch (UnprocessableEntityException e) {
+ assertEquals("ConceptMap has no value for ConceptMap.url", e.getMessage());
+ }
}
diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml
index 8ff7d691083..fed6e611868 100644
--- a/hapi-fhir-jpaserver-example/pom.xml
+++ b/hapi-fhir-jpaserver-example/pom.xml
@@ -187,8 +187,8 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
diff --git a/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/pom.xml b/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/pom.xml
index 3cb4741f06c..06610ca6feb 100644
--- a/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/pom.xml
+++ b/hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/pom.xml
@@ -138,8 +138,8 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
index ac8c9cba079..806737dc495 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
+++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml
@@ -55,8 +55,8 @@
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
@@ -65,8 +65,8 @@
- com.phloc
- phloc-commons
+ com.helger
+ ph-commons
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java
index 240c22eaad7..daee8943c41 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java
@@ -87,8 +87,8 @@ public class FhirTesterConfig {
.addServer()
.withId("spark2")
.withFhirVersion(FhirVersionEnum.DSTU2)
- .withBaseUrl("http://spark-dstu2.furore.com/fhir")
- .withName("Spark - Furore (DSTU2 FHIR)");
+ .withBaseUrl("http://vonk.furore.com/")
+ .withName("Vonk - Furore (STU3 FHIR)");
return retVal;
}
diff --git a/hapi-fhir-osgi-core/pom.xml b/hapi-fhir-osgi-core/pom.xml
index 8500e137de7..35d30c0c7df 100644
--- a/hapi-fhir-osgi-core/pom.xml
+++ b/hapi-fhir-osgi-core/pom.xml
@@ -72,8 +72,8 @@
thymeleaf
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
@@ -82,8 +82,8 @@
- com.phloc
- phloc-commons
+ com.helger
+ ph-commons
@@ -197,8 +197,8 @@
com.ctc.wstx.api;version="4.4";resolution:=optional,
com.ctc.wstx.*;version="4.4";resolution:=optional,
com.google.*;resolution:=optional;-remove-attribute:=version,
- com.phloc.commons;resolution:=optional;-remove-attribute:=version,
- com.phloc.*;resolution:=optional;-remove-attribute:=version,
+ com.helger.commons;resolution:=optional;-remove-attribute:=version,
+ com.helger.*;resolution:=optional;-remove-attribute:=version,
javassist;-remove-attribute:=version,
javax.*;-remove-attribute:=version,
net.sf.saxon;resolution:=optional,
diff --git a/hapi-fhir-structures-dstu/pom.xml b/hapi-fhir-structures-dstu/pom.xml
index cca4934514d..b14c2385fa7 100644
--- a/hapi-fhir-structures-dstu/pom.xml
+++ b/hapi-fhir-structures-dstu/pom.xml
@@ -68,13 +68,13 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml
index f32d3461ae3..80be1513566 100644
--- a/hapi-fhir-structures-dstu2.1/pom.xml
+++ b/hapi-fhir-structures-dstu2.1/pom.xml
@@ -130,22 +130,6 @@
thymeleaftest
-
- com.phloc
- phloc-schematron
- test
-
-
-
-
- com.phloc
- phloc-commons
- test
-
@@ -208,6 +192,16 @@
spring-webtest
+
+ com.helger
+ ph-schematron
+ test
+
+
+ com.helger
+ ph-commons
+ test
+
diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2_1Test.java
index 7a7a7c81413..103de11e628 100644
--- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2_1Test.java
+++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2_1Test.java
@@ -11,6 +11,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
+import com.helger.commons.io.stream.StringInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.*;
@@ -29,7 +30,6 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.base.Charsets;
-import com.phloc.commons.io.streams.StringInputStream;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
@@ -83,7 +83,7 @@ public class GenericClientDstu2_1Test {
return body;
}
- private ArgumentCaptor prepareClientForSearchResponse() throws IOException, ClientProtocolException {
+ private ArgumentCaptor prepareClientForSearchResponse() throws IOException {
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@@ -92,7 +92,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -111,7 +111,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -149,7 +149,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -187,7 +187,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -225,7 +225,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -264,7 +264,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -303,7 +303,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -330,7 +330,7 @@ public class GenericClientDstu2_1Test {
}
@Test
- public void testPatchInvalid() throws Exception {
+ public void testPatchInvalid() {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
try {
@@ -354,7 +354,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -398,7 +398,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -441,7 +441,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -505,7 +505,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -537,14 +537,14 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
@@ -586,14 +586,14 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
@@ -625,7 +625,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -652,7 +652,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -697,7 +697,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -737,7 +737,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -773,7 +773,7 @@ public class GenericClientDstu2_1Test {
private int myCount = 0;
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
final String respString;
if (myCount == 1 || myCount == 2) {
ourLog.info("Encoding patient");
@@ -822,7 +822,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
final String respString;
if (myAnswerCount >= 1) {
ourLog.info("Encoding patient");
@@ -872,7 +872,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
final String respString;
if (myAnswerCount >= 1) {
ourLog.info("Encoding patient");
@@ -912,7 +912,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public StringInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public StringInputStream answer(InvocationOnMock theInvocation) {
return new StringInputStream("HELLO", Charsets.UTF_8);
}
});
@@ -937,7 +937,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public StringInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public StringInputStream answer(InvocationOnMock theInvocation) {
return new StringInputStream("not implemented", Charsets.UTF_8);
}
});
@@ -968,7 +968,7 @@ public class GenericClientDstu2_1Test {
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value((String) null))
.and(Patient.BIRTHDATE.exactly().day((Date) null))
- .and(Patient.GENDER.exactly().code((String) null))
+ .and(Patient.GENDER.exactly().code(null))
.and(Patient.ORGANIZATION.hasId((String) null))
.returnBundle(Bundle.class)
.execute();
@@ -1019,7 +1019,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
}
});
@@ -1064,7 +1064,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
}
});
@@ -1121,7 +1121,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1152,7 +1152,7 @@ public class GenericClientDstu2_1Test {
// when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1179,7 +1179,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1286,7 +1286,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1405,7 +1405,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1497,7 +1497,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1564,7 +1564,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1601,7 +1601,7 @@ public class GenericClientDstu2_1Test {
}
@Test
- public void testTransactionWithInvalidBody() throws Exception {
+ public void testTransactionWithInvalidBody() {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
// Transaction
@@ -1652,7 +1652,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1691,14 +1691,14 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
@@ -1739,14 +1739,14 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
@@ -1784,7 +1784,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1821,7 +1821,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1842,7 +1842,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1870,14 +1870,14 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] {};
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
}
});
@@ -1910,7 +1910,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1948,7 +1948,7 @@ public class GenericClientDstu2_1Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml
index ffbbe2469a5..e10b307f786 100644
--- a/hapi-fhir-structures-dstu2/pom.xml
+++ b/hapi-fhir-structures-dstu2/pom.xml
@@ -103,13 +103,13 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java
index 2bea5344959..8888c4180d7 100644
--- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlightingInterceptorTest.java
@@ -28,7 +28,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
-import com.phloc.commons.collections.iterate.ArrayEnumeration;
+import com.helger.collection.iterate.ArrayEnumeration;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
@@ -130,7 +130,7 @@ public class ResponseHighlightingInterceptorTest {
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer>() {
@Override
public Enumeration answer(InvocationOnMock theInvocation) throws Throwable {
- return new ArrayEnumeration("text/html,application/xhtml+xml,application/xml;q=0.9");
+ return new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9");
}
});
when(req.getHeader(Constants.HEADER_ORIGIN)).thenAnswer(new Answer() {
diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml
index 499350205a9..3a562cb840d 100644
--- a/hapi-fhir-structures-dstu3/pom.xml
+++ b/hapi-fhir-structures-dstu3/pom.xml
@@ -217,13 +217,13 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java
index 7316f1bd6f4..55501fbf9d1 100644
--- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java
+++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/utils/StructureMapUtilities.java
@@ -1,2746 +1,2752 @@
-package org.hl7.fhir.dstu3.utils;
-
-// remember group resolution
-// trace - account for which wasn't transformed in the source
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
-import org.hl7.fhir.dstu3.conformance.ProfileUtilities.ProfileKnowledgeProvider;
-import org.hl7.fhir.dstu3.context.IWorkerContext;
-import org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult;
-import org.hl7.fhir.dstu3.elementmodel.Element;
-import org.hl7.fhir.dstu3.elementmodel.Property;
-import org.hl7.fhir.dstu3.model.Base;
-import org.hl7.fhir.dstu3.model.BooleanType;
-import org.hl7.fhir.dstu3.model.CodeType;
-import org.hl7.fhir.dstu3.model.CodeableConcept;
-import org.hl7.fhir.dstu3.model.Coding;
-import org.hl7.fhir.dstu3.model.ConceptMap;
-import org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupComponent;
-import org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupUnmappedMode;
-import org.hl7.fhir.dstu3.model.ConceptMap.SourceElementComponent;
-import org.hl7.fhir.dstu3.model.ConceptMap.TargetElementComponent;
-import org.hl7.fhir.dstu3.model.Constants;
-import org.hl7.fhir.dstu3.model.ContactDetail;
-import org.hl7.fhir.dstu3.model.ContactPoint;
-import org.hl7.fhir.dstu3.model.DecimalType;
-import org.hl7.fhir.dstu3.model.ElementDefinition;
-import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionMappingComponent;
-import org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent;
-import org.hl7.fhir.dstu3.model.Enumeration;
-import org.hl7.fhir.dstu3.model.Enumerations.ConceptMapEquivalence;
-import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
-import org.hl7.fhir.dstu3.model.ExpressionNode;
-import org.hl7.fhir.dstu3.model.ExpressionNode.CollectionStatus;
-import org.hl7.fhir.dstu3.model.IdType;
-import org.hl7.fhir.dstu3.model.IntegerType;
-import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
-import org.hl7.fhir.dstu3.model.PrimitiveType;
-import org.hl7.fhir.dstu3.model.Reference;
-import org.hl7.fhir.dstu3.model.Resource;
-import org.hl7.fhir.dstu3.model.ResourceFactory;
-import org.hl7.fhir.dstu3.model.StringType;
-import org.hl7.fhir.dstu3.model.StructureDefinition;
-import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionMappingComponent;
-import org.hl7.fhir.dstu3.model.StructureDefinition.TypeDerivationRule;
-import org.hl7.fhir.dstu3.model.StructureMap;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapContextType;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupInputComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleDependentComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleSourceComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleTargetComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleTargetParameterComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupTypeMode;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapInputMode;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapSourceListMode;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapTargetListMode;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapModelMode;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapStructureComponent;
-import org.hl7.fhir.dstu3.model.StructureMap.StructureMapTransform;
-import org.hl7.fhir.dstu3.model.Type;
-import org.hl7.fhir.dstu3.model.TypeDetails;
-import org.hl7.fhir.dstu3.model.TypeDetails.ProfiledType;
-import org.hl7.fhir.dstu3.model.UriType;
-import org.hl7.fhir.dstu3.model.ValueSet;
-import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
-import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
-import org.hl7.fhir.dstu3.utils.FHIRLexer.FHIRLexerException;
-import org.hl7.fhir.dstu3.utils.FHIRPathEngine.IEvaluationContext;
-import org.hl7.fhir.exceptions.DefinitionException;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.exceptions.FHIRFormatError;
-import org.hl7.fhir.exceptions.PathEngineException;
-import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
-import org.hl7.fhir.utilities.TextFile;
-import org.hl7.fhir.utilities.Utilities;
-import org.hl7.fhir.utilities.xhtml.NodeType;
-import org.hl7.fhir.utilities.xhtml.XhtmlNode;
-
-/**
- * Services in this class:
- *
- * string render(map) - take a structure and convert it to text
- * map parse(text) - take a text representation and parse it
- * getTargetType(map) - return the definition for the type to create to hand in
- * transform(appInfo, source, map, target) - transform from source to target following the map
- * analyse(appInfo, map) - generate profiles and other analysis artifacts for the targets of the transform
- * map generateMapFromMappings(StructureDefinition) - build a mapping from a structure definition with loigcal mappings
- *
- * @author Grahame Grieve
- *
- */
-public class StructureMapUtilities {
-
- public class ResolvedGroup {
- public StructureMapGroupComponent target;
- public StructureMap targetMap;
- }
- public static final String MAP_WHERE_CHECK = "map.where.check";
- public static final String MAP_WHERE_EXPRESSION = "map.where.expression";
- public static final String MAP_SEARCH_EXPRESSION = "map.search.expression";
- public static final String MAP_EXPRESSION = "map.transform.expression";
- private static final boolean RENDER_MULTIPLE_TARGETS_ONELINE = true;
- private static final String AUTO_VAR_NAME = "vvv";
-
- public interface ITransformerServices {
- // public boolean validateByValueSet(Coding code, String valuesetId);
- public void log(String message); // log internal progress
- public Base createType(Object appInfo, String name) throws FHIRException;
- public Base createResource(Object appInfo, Base res); // an already created resource is provided; this is to identify/store it
- public Coding translate(Object appInfo, Coding source, String conceptMapUrl) throws FHIRException;
- // public Coding translate(Coding code)
- // ValueSet validation operation
- // Translation operation
- // Lookup another tree of data
- // Create an instance tree
- // Return the correct string format to refer to a tree (input or output)
- public Base resolveReference(Object appContext, String url);
- public List performSearch(Object appContext, String url);
- }
-
- private class FFHIRPathHostServices implements IEvaluationContext{
-
- public Base resolveConstant(Object appContext, String name) throws PathEngineException {
- Variables vars = (Variables) appContext;
- Base res = vars.get(VariableMode.INPUT, name);
- if (res == null)
- res = vars.get(VariableMode.OUTPUT, name);
- return res;
- }
-
- @Override
- public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
- if (!(appContext instanceof VariablesForProfiling))
- throw new Error("Internal Logic Error (wrong type '"+appContext.getClass().getName()+"' in resolveConstantType)");
- VariablesForProfiling vars = (VariablesForProfiling) appContext;
- VariableForProfiling v = vars.get(null, name);
- if (v == null)
- throw new PathEngineException("Unknown variable '"+name+"' from variables "+vars.summary());
- return v.property.types;
- }
-
- @Override
- public boolean log(String argument, List focus) {
- throw new Error("Not Implemented Yet");
- }
-
- @Override
- public FunctionDetails resolveFunction(String functionName) {
- return null; // throw new Error("Not Implemented Yet");
- }
-
- @Override
- public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException {
- throw new Error("Not Implemented Yet");
- }
-
- @Override
- public List executeFunction(Object appContext, String functionName, List> parameters) {
- throw new Error("Not Implemented Yet");
- }
-
- @Override
- public Base resolveReference(Object appContext, String url) {
- if (services == null)
- return null;
- return services.resolveReference(appContext, url);
- }
-
- }
- private IWorkerContext worker;
- private FHIRPathEngine fpe;
- private Map library;
- private ITransformerServices services;
- private ProfileKnowledgeProvider pkp;
- private Map ids = new HashMap();
-
- public StructureMapUtilities(IWorkerContext worker, Map library, ITransformerServices services, ProfileKnowledgeProvider pkp) {
- super();
- this.worker = worker;
- this.library = library;
- this.services = services;
- this.pkp = pkp;
- fpe = new FHIRPathEngine(worker);
- fpe.setHostServices(new FFHIRPathHostServices());
- }
-
- public StructureMapUtilities(IWorkerContext worker, Map library, ITransformerServices services) {
- super();
- this.worker = worker;
- this.library = library;
- this.services = services;
- fpe = new FHIRPathEngine(worker);
- fpe.setHostServices(new FFHIRPathHostServices());
- }
-
- public StructureMapUtilities(IWorkerContext worker, Map library) {
- super();
- this.worker = worker;
- this.library = library;
- fpe = new FHIRPathEngine(worker);
- fpe.setHostServices(new FFHIRPathHostServices());
- }
-
- public StructureMapUtilities(IWorkerContext worker) {
- super();
- this.worker = worker;
- fpe = new FHIRPathEngine(worker);
- fpe.setHostServices(new FFHIRPathHostServices());
- }
-
- public StructureMapUtilities(IWorkerContext worker, ITransformerServices services) {
- super();
- this.worker = worker;
- this.library = new HashMap();
- for (org.hl7.fhir.dstu3.model.MetadataResource bc : worker.allConformanceResources()) {
- if (bc instanceof StructureMap)
- library.put(bc.getUrl(), (StructureMap) bc);
- }
- this.services = services;
- fpe = new FHIRPathEngine(worker);
- fpe.setHostServices(new FFHIRPathHostServices());
- }
-
- public static String render(StructureMap map) {
- StringBuilder b = new StringBuilder();
- b.append("map \"");
- b.append(map.getUrl());
- b.append("\" = \"");
- b.append(Utilities.escapeJava(map.getName()));
- b.append("\"\r\n\r\n");
-
- renderConceptMaps(b, map);
- renderUses(b, map);
- renderImports(b, map);
- for (StructureMapGroupComponent g : map.getGroup())
- renderGroup(b, g);
- return b.toString();
- }
-
- private static void renderConceptMaps(StringBuilder b, StructureMap map) {
- for (Resource r : map.getContained()) {
- if (r instanceof ConceptMap) {
- produceConceptMap(b, (ConceptMap) r);
- }
- }
- }
-
- private static void produceConceptMap(StringBuilder b, ConceptMap cm) {
- b.append("conceptmap \"");
- b.append(cm.getId());
- b.append("\" {\r\n");
- Map prefixesSrc = new HashMap();
- Map prefixesTgt = new HashMap();
- char prefix = 's';
- for (ConceptMapGroupComponent cg : cm.getGroup()) {
- if (!prefixesSrc.containsKey(cg.getSource())) {
- prefixesSrc.put(cg.getSource(), String.valueOf(prefix));
- b.append(" prefix ");
- b.append(prefix);
- b.append(" = \"");
- b.append(cg.getSource());
- b.append("\"\r\n");
- prefix++;
- }
- if (!prefixesTgt.containsKey(cg.getTarget())) {
- prefixesTgt.put(cg.getTarget(), String.valueOf(prefix));
- b.append(" prefix ");
- b.append(prefix);
- b.append(" = \"");
- b.append(cg.getTarget());
- b.append("\"\r\n");
- prefix++;
- }
- }
- b.append("\r\n");
- for (ConceptMapGroupComponent cg : cm.getGroup()) {
- if (cg.hasUnmapped()) {
- b.append(" unmapped for ");
- b.append(prefix);
- b.append(" = ");
- b.append(cg.getUnmapped().getMode());
- b.append("\r\n");
- }
- }
-
- for (ConceptMapGroupComponent cg : cm.getGroup()) {
- for (SourceElementComponent ce : cg.getElement()) {
- b.append(" ");
- b.append(prefixesSrc.get(cg.getSource()));
- b.append(":");
- b.append(ce.getCode());
- b.append(" ");
- b.append(getChar(ce.getTargetFirstRep().getEquivalence()));
- b.append(" ");
- b.append(prefixesTgt.get(cg.getTarget()));
- b.append(":");
- b.append(ce.getTargetFirstRep().getCode());
- b.append("\r\n");
- }
- }
- b.append("}\r\n\r\n");
- }
-
- private static Object getChar(ConceptMapEquivalence equivalence) {
- switch (equivalence) {
- case RELATEDTO: return "-";
- case EQUAL: return "=";
- case EQUIVALENT: return "==";
- case DISJOINT: return "!=";
- case UNMATCHED: return "--";
- case WIDER: return "<=";
- case SUBSUMES: return "<-";
- case NARROWER: return ">=";
- case SPECIALIZES: return ">-";
- case INEXACT: return "~";
- default: return "??";
- }
- }
-
- private static void renderUses(StringBuilder b, StructureMap map) {
- for (StructureMapStructureComponent s : map.getStructure()) {
- b.append("uses \"");
- b.append(s.getUrl());
- b.append("\" ");
- if (s.hasAlias()) {
- b.append("alias ");
- b.append(s.getAlias());
- b.append(" ");
- }
- b.append("as ");
- b.append(s.getMode().toCode());
- b.append("\r\n");
- renderDoco(b, s.getDocumentation());
- }
- if (map.hasStructure())
- b.append("\r\n");
- }
-
- private static void renderImports(StringBuilder b, StructureMap map) {
- for (UriType s : map.getImport()) {
- b.append("imports \"");
- b.append(s.getValue());
- b.append("\"\r\n");
- }
- if (map.hasImport())
- b.append("\r\n");
- }
-
- public static String groupToString(StructureMapGroupComponent g) {
- StringBuilder b = new StringBuilder();
- renderGroup(b, g);
- return b.toString();
- }
-
- private static void renderGroup(StringBuilder b, StructureMapGroupComponent g) {
- b.append("group ");
- switch (g.getTypeMode()) {
- case TYPES: b.append("for types");
- case TYPEANDTYPES: b.append("for type+types ");
- default: // NONE, NULL
- }
- b.append("for types ");
- b.append(g.getName());
- if (g.hasExtends()) {
- b.append(" extends ");
- b.append(g.getExtends());
- }
- if (g.hasDocumentation())
- renderDoco(b, g.getDocumentation());
- b.append("\r\n");
- for (StructureMapGroupInputComponent gi : g.getInput()) {
- b.append(" input ");
- b.append(gi.getName());
- if (gi.hasType()) {
- b.append(" : ");
- b.append(gi.getType());
- }
- b.append(" as ");
- b.append(gi.getMode().toCode());
- b.append("\r\n");
- }
- if (g.hasInput())
- b.append("\r\n");
- for (StructureMapGroupRuleComponent r : g.getRule()) {
- renderRule(b, r, 2);
- }
- b.append("\r\nendgroup\r\n");
- }
-
- public static String ruleToString(StructureMapGroupRuleComponent r) {
- StringBuilder b = new StringBuilder();
- renderRule(b, r, 0);
- return b.toString();
- }
-
- private static void renderRule(StringBuilder b, StructureMapGroupRuleComponent r, int indent) {
- for (int i = 0; i < indent; i++)
- b.append(' ');
- b.append(r.getName());
- b.append(" : for ");
- boolean canBeAbbreviated = checkisSimple(r);
-
- boolean first = true;
- for (StructureMapGroupRuleSourceComponent rs : r.getSource()) {
- if (first)
- first = false;
- else
- b.append(", ");
- renderSource(b, rs, canBeAbbreviated);
- }
- if (r.getTarget().size() > 1) {
- b.append(" make ");
- first = true;
- for (StructureMapGroupRuleTargetComponent rt : r.getTarget()) {
- if (first)
- first = false;
- else
- b.append(", ");
- if (RENDER_MULTIPLE_TARGETS_ONELINE)
- b.append(' ');
- else {
- b.append("\r\n");
- for (int i = 0; i < indent+4; i++)
- b.append(' ');
- }
- renderTarget(b, rt, false);
- }
- } else if (r.hasTarget()) {
- b.append(" make ");
- renderTarget(b, r.getTarget().get(0), canBeAbbreviated);
- }
- if (!canBeAbbreviated) {
- if (r.hasRule()) {
- b.append(" then {\r\n");
- renderDoco(b, r.getDocumentation());
- for (StructureMapGroupRuleComponent ir : r.getRule()) {
- renderRule(b, ir, indent+2);
- }
- for (int i = 0; i < indent; i++)
- b.append(' ');
- b.append("}\r\n");
- } else {
- if (r.hasDependent()) {
- b.append(" then ");
- first = true;
- for (StructureMapGroupRuleDependentComponent rd : r.getDependent()) {
- if (first)
- first = false;
- else
- b.append(", ");
- b.append(rd.getName());
- b.append("(");
- boolean ifirst = true;
- for (StringType rdp : rd.getVariable()) {
- if (ifirst)
- ifirst = false;
- else
- b.append(", ");
- b.append(rdp.asStringValue());
- }
- b.append(")");
- }
- }
- }
- }
- renderDoco(b, r.getDocumentation());
- b.append("\r\n");
- }
-
- private static boolean checkisSimple(StructureMapGroupRuleComponent r) {
- return
- (r.getSource().size() == 1 && r.getSourceFirstRep().hasElement() && r.getSourceFirstRep().hasVariable()) &&
- (r.getTarget().size() == 1 && r.getTargetFirstRep().hasVariable() && (r.getTargetFirstRep().getTransform() == null || r.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE) && r.getTargetFirstRep().getParameter().size() == 0) &&
- (r.getDependent().size() == 0);
- }
-
- public static String sourceToString(StructureMapGroupRuleSourceComponent r) {
- StringBuilder b = new StringBuilder();
- renderSource(b, r, false);
- return b.toString();
- }
-
- private static void renderSource(StringBuilder b, StructureMapGroupRuleSourceComponent rs, boolean abbreviate) {
- b.append(rs.getContext());
- if (rs.getContext().equals("@search")) {
- b.append('(');
- b.append(rs.getElement());
- b.append(')');
- } else if (rs.hasElement()) {
- b.append('.');
- b.append(rs.getElement());
- }
- if (rs.hasType()) {
- b.append(" : ");
- b.append(rs.getType());
- if (rs.hasMin()) {
- b.append(" ");
- b.append(rs.getMin());
- b.append("..");
- b.append(rs.getMax());
- }
- }
-
- if (rs.hasListMode()) {
- b.append(" ");
- b.append(rs.getListMode().toCode());
- }
- if (rs.hasDefaultValue()) {
- b.append(" default ");
- assert rs.getDefaultValue() instanceof StringType;
- b.append("\""+Utilities.escapeJson(((StringType) rs.getDefaultValue()).asStringValue())+"\"");
- }
- if (!abbreviate && rs.hasVariable()) {
- b.append(" as ");
- b.append(rs.getVariable());
- }
- if (rs.hasCondition()) {
- b.append(" where ");
- b.append(rs.getCondition());
- }
- if (rs.hasCheck()) {
- b.append(" check ");
- b.append(rs.getCheck());
- }
- }
-
- public static String targetToString(StructureMapGroupRuleTargetComponent rt) {
- StringBuilder b = new StringBuilder();
- renderTarget(b, rt, false);
- return b.toString();
- }
-
- private static void renderTarget(StringBuilder b, StructureMapGroupRuleTargetComponent rt, boolean abbreviate) {
- if (rt.hasContext()) {
- if (rt.getContextType() == StructureMapContextType.TYPE)
- b.append("@");
- b.append(rt.getContext());
- if (rt.hasElement()) {
- b.append('.');
- b.append(rt.getElement());
- }
- }
- if (!abbreviate && rt.hasTransform()) {
- if (rt.hasContext())
- b.append(" = ");
- if (rt.getTransform() == StructureMapTransform.COPY && rt.getParameter().size() == 1) {
- renderTransformParam(b, rt.getParameter().get(0));
- } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 1) {
- b.append("(");
- b.append("\""+((StringType) rt.getParameter().get(0).getValue()).asStringValue()+"\"");
- b.append(")");
- } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 2) {
- b.append(rt.getTransform().toCode());
- b.append("(");
- b.append(((IdType) rt.getParameter().get(0).getValue()).asStringValue());
- b.append("\""+((StringType) rt.getParameter().get(1).getValue()).asStringValue()+"\"");
- b.append(")");
- } else {
- b.append(rt.getTransform().toCode());
- b.append("(");
- boolean first = true;
- for (StructureMapGroupRuleTargetParameterComponent rtp : rt.getParameter()) {
- if (first)
- first = false;
- else
- b.append(", ");
- renderTransformParam(b, rtp);
- }
- b.append(")");
- }
- }
- if (!abbreviate && rt.hasVariable()) {
- b.append(" as ");
- b.append(rt.getVariable());
- }
- for (Enumeration lm : rt.getListMode()) {
- b.append(" ");
- b.append(lm.getValue().toCode());
- if (lm.getValue() == StructureMapTargetListMode.SHARE) {
- b.append(" ");
- b.append(rt.getListRuleId());
- }
- }
- }
-
- public static String paramToString(StructureMapGroupRuleTargetParameterComponent rtp) {
- StringBuilder b = new StringBuilder();
- renderTransformParam(b, rtp);
- return b.toString();
- }
-
- private static void renderTransformParam(StringBuilder b, StructureMapGroupRuleTargetParameterComponent rtp) {
- try {
- if (rtp.hasValueBooleanType())
- b.append(rtp.getValueBooleanType().asStringValue());
- else if (rtp.hasValueDecimalType())
- b.append(rtp.getValueDecimalType().asStringValue());
- else if (rtp.hasValueIdType())
- b.append(rtp.getValueIdType().asStringValue());
- else if (rtp.hasValueDecimalType())
- b.append(rtp.getValueDecimalType().asStringValue());
- else if (rtp.hasValueIntegerType())
- b.append(rtp.getValueIntegerType().asStringValue());
- else
- b.append("\""+Utilities.escapeJava(rtp.getValueStringType().asStringValue())+"\"");
- } catch (FHIRException e) {
- e.printStackTrace();
- b.append("error!");
- }
- }
-
- private static void renderDoco(StringBuilder b, String doco) {
- if (Utilities.noString(doco))
- return;
- b.append(" // ");
- b.append(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " "));
- }
-
- public StructureMap parse(String text) throws FHIRException {
- FHIRLexer lexer = new FHIRLexer(text);
- if (lexer.done())
- throw lexer.error("Map Input cannot be empty");
- lexer.skipComments();
- lexer.token("map");
- StructureMap result = new StructureMap();
- result.setUrl(lexer.readConstant("url"));
- lexer.token("=");
- result.setName(lexer.readConstant("name"));
- lexer.skipComments();
-
- while (lexer.hasToken("conceptmap"))
- parseConceptMap(result, lexer);
-
- while (lexer.hasToken("uses"))
- parseUses(result, lexer);
- while (lexer.hasToken("imports"))
- parseImports(result, lexer);
-
- parseGroup(result, lexer);
-
- while (!lexer.done()) {
- parseGroup(result, lexer);
- }
-
- return result;
- }
-
- private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException {
- lexer.token("conceptmap");
- ConceptMap map = new ConceptMap();
- String id = lexer.readConstant("map id");
- if (!id.startsWith("#"))
- lexer.error("Concept Map identifier must start with #");
- map.setId(id);
- map.setStatus(PublicationStatus.DRAFT); // todo: how to add this to the text format
- result.getContained().add(map);
- lexer.token("{");
- lexer.skipComments();
- // lexer.token("source");
- // map.setSource(new UriType(lexer.readConstant("source")));
- // lexer.token("target");
- // map.setSource(new UriType(lexer.readConstant("target")));
- Map prefixes = new HashMap();
- while (lexer.hasToken("prefix")) {
- lexer.token("prefix");
- String n = lexer.take();
- lexer.token("=");
- String v = lexer.readConstant("prefix url");
- prefixes.put(n, v);
- }
- while (lexer.hasToken("unmapped")) {
- lexer.token("unmapped");
- lexer.token("for");
- String n = readPrefix(prefixes, lexer);
- ConceptMapGroupComponent g = getGroup(map, n, null);
- lexer.token("=");
- String v = lexer.take();
- if (v.equals("provided")) {
- g.getUnmapped().setMode(ConceptMapGroupUnmappedMode.PROVIDED);
- } else
- lexer.error("Only unmapped mode PROVIDED is supported at this time");
- }
- while (!lexer.hasToken("}")) {
- String srcs = readPrefix(prefixes, lexer);
- lexer.token(":");
- String sc = lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take();
- ConceptMapEquivalence eq = readEquivalence(lexer);
- String tgts = (eq != ConceptMapEquivalence.UNMATCHED) ? readPrefix(prefixes, lexer) : "";
- ConceptMapGroupComponent g = getGroup(map, srcs, tgts);
- SourceElementComponent e = g.addElement();
- e.setCode(sc);
- if (e.getCode().startsWith("\""))
- e.setCode(lexer.processConstant(e.getCode()));
- TargetElementComponent tgt = e.addTarget();
- if (eq != ConceptMapEquivalence.EQUIVALENT)
- tgt.setEquivalence(eq);
- if (tgt.getEquivalence() != ConceptMapEquivalence.UNMATCHED) {
- lexer.token(":");
- tgt.setCode(lexer.take());
- if (tgt.getCode().startsWith("\""))
- tgt.setCode(lexer.processConstant(tgt.getCode()));
- }
- if (lexer.hasComment())
- tgt.setComment(lexer.take().substring(2).trim());
- }
- lexer.token("}");
- }
-
-
- private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) {
- for (ConceptMapGroupComponent grp : map.getGroup()) {
- if (grp.getSource().equals(srcs))
- if ((tgts == null && !grp.hasTarget()) || (tgts != null && tgts.equals(grp.getTarget())))
- return grp;
- }
- ConceptMapGroupComponent grp = map.addGroup();
- grp.setSource(srcs);
- grp.setTarget(tgts);
- return grp;
- }
-
-
- private String readPrefix(Map prefixes, FHIRLexer lexer) throws FHIRLexerException {
- String prefix = lexer.take();
- if (!prefixes.containsKey(prefix))
- throw lexer.error("Unknown prefix '"+prefix+"'");
- return prefixes.get(prefix);
- }
-
-
- private ConceptMapEquivalence readEquivalence(FHIRLexer lexer) throws FHIRLexerException {
- String token = lexer.take();
- if (token.equals("-"))
- return ConceptMapEquivalence.RELATEDTO;
- if (token.equals("="))
- return ConceptMapEquivalence.EQUAL;
- if (token.equals("=="))
- return ConceptMapEquivalence.EQUIVALENT;
- if (token.equals("!="))
- return ConceptMapEquivalence.DISJOINT;
- if (token.equals("--"))
- return ConceptMapEquivalence.UNMATCHED;
- if (token.equals("<="))
- return ConceptMapEquivalence.WIDER;
- if (token.equals("<-"))
- return ConceptMapEquivalence.SUBSUMES;
- if (token.equals(">="))
- return ConceptMapEquivalence.NARROWER;
- if (token.equals(">-"))
- return ConceptMapEquivalence.SPECIALIZES;
- if (token.equals("~"))
- return ConceptMapEquivalence.INEXACT;
- throw lexer.error("Unknown equivalence token '"+token+"'");
- }
-
-
- private void parseUses(StructureMap result, FHIRLexer lexer) throws FHIRException {
- lexer.token("uses");
- StructureMapStructureComponent st = result.addStructure();
- st.setUrl(lexer.readConstant("url"));
- if (lexer.hasToken("alias")) {
- lexer.token("alias");
- st.setAlias(lexer.take());
- }
- lexer.token("as");
- st.setMode(StructureMapModelMode.fromCode(lexer.take()));
- lexer.skipToken(";");
- if (lexer.hasComment()) {
- st.setDocumentation(lexer.take().substring(2).trim());
- }
- lexer.skipComments();
- }
-
- private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException {
- lexer.token("imports");
- result.addImport(lexer.readConstant("url"));
- lexer.skipToken(";");
- if (lexer.hasComment()) {
- lexer.next();
- }
- lexer.skipComments();
- }
-
- private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException {
- lexer.token("group");
- StructureMapGroupComponent group = result.addGroup();
- if (lexer.hasToken("for")) {
- lexer.token("for");
- if ("type".equals(lexer.getCurrent())) {
- lexer.token("type");
- lexer.token("+");
- lexer.token("types");
- group.setTypeMode(StructureMapGroupTypeMode.TYPEANDTYPES);
- } else {
- lexer.token("types");
- group.setTypeMode(StructureMapGroupTypeMode.TYPES);
- }
- } else
- group.setTypeMode(StructureMapGroupTypeMode.NONE);
- group.setName(lexer.take());
- if (lexer.hasToken("extends")) {
- lexer.next();
- group.setExtends(lexer.take());
- }
- lexer.skipComments();
- while (lexer.hasToken("input"))
- parseInput(group, lexer);
- while (!lexer.hasToken("endgroup")) {
- if (lexer.done())
- throw lexer.error("premature termination expecting 'endgroup'");
- parseRule(result, group.getRule(), lexer);
- }
- lexer.next();
- lexer.skipComments();
- }
-
- private void parseInput(StructureMapGroupComponent group, FHIRLexer lexer) throws FHIRException {
- lexer.token("input");
- StructureMapGroupInputComponent input = group.addInput();
- input.setName(lexer.take());
- if (lexer.hasToken(":")) {
- lexer.token(":");
- input.setType(lexer.take());
- }
- lexer.token("as");
- input.setMode(StructureMapInputMode.fromCode(lexer.take()));
- if (lexer.hasComment()) {
- input.setDocumentation(lexer.take().substring(2).trim());
- }
- lexer.skipToken(";");
- lexer.skipComments();
- }
-
- private void parseRule(StructureMap map, List list, FHIRLexer lexer) throws FHIRException {
- StructureMapGroupRuleComponent rule = new StructureMapGroupRuleComponent();
- list.add(rule);
- rule.setName(lexer.takeDottedToken());
- lexer.token(":");
- lexer.token("for");
- boolean done = false;
- while (!done) {
- parseSource(rule, lexer);
- done = !lexer.hasToken(",");
- if (!done)
- lexer.next();
- }
- if (lexer.hasToken("make")) {
- lexer.token("make");
- done = false;
- while (!done) {
- parseTarget(rule, lexer);
- done = !lexer.hasToken(",");
- if (!done)
- lexer.next();
- }
- }
- if (lexer.hasToken("then")) {
- lexer.token("then");
- if (lexer.hasToken("{")) {
- lexer.token("{");
- if (lexer.hasComment()) {
- rule.setDocumentation(lexer.take().substring(2).trim());
- }
- lexer.skipComments();
- while (!lexer.hasToken("}")) {
- if (lexer.done())
- throw lexer.error("premature termination expecting '}' in nested group");
- parseRule(map, rule.getRule(), lexer);
- }
- lexer.token("}");
- } else {
- done = false;
- while (!done) {
- parseRuleReference(rule, lexer);
- done = !lexer.hasToken(",");
- if (!done)
- lexer.next();
- }
- }
- } else if (lexer.hasComment()) {
- rule.setDocumentation(lexer.take().substring(2).trim());
- }
- if (isSimpleSyntax(rule)) {
- rule.getSourceFirstRep().setVariable(AUTO_VAR_NAME);
- rule.getTargetFirstRep().setVariable(AUTO_VAR_NAME);
- rule.getTargetFirstRep().setTransform(StructureMapTransform.CREATE); // with no parameter - e.g. imply what is to be created
- // no dependencies - imply what is to be done based on types
- }
- lexer.skipComments();
- }
-
- private boolean isSimpleSyntax(StructureMapGroupRuleComponent rule) {
- return
- (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasContext() && rule.getSourceFirstRep().hasElement() && !rule.getSourceFirstRep().hasVariable()) &&
- (rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasContext() && rule.getTargetFirstRep().hasElement() && !rule.getTargetFirstRep().hasVariable() && !rule.getTargetFirstRep().hasParameter()) &&
- (rule.getDependent().size() == 0 && rule.getRule().size() == 0);
- }
-
- private void parseRuleReference(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRLexerException {
- StructureMapGroupRuleDependentComponent ref = rule.addDependent();
- ref.setName(lexer.take());
- lexer.token("(");
- boolean done = false;
- while (!done) {
- ref.addVariable(lexer.take());
- done = !lexer.hasToken(",");
- if (!done)
- lexer.next();
- }
- lexer.token(")");
- }
-
- private void parseSource(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
- StructureMapGroupRuleSourceComponent source = rule.addSource();
- source.setContext(lexer.take());
- if (source.getContext().equals("search") && lexer.hasToken("(")) {
- source.setContext("@search");
- lexer.take();
- ExpressionNode node = fpe.parse(lexer);
- source.setUserData(MAP_SEARCH_EXPRESSION, node);
- source.setElement(node.toString());
- lexer.token(")");
- } else if (lexer.hasToken(".")) {
- lexer.token(".");
- source.setElement(lexer.take());
- }
- if (lexer.hasToken(":")) {
- // type and cardinality
- lexer.token(":");
- source.setType(lexer.takeDottedToken());
- if (!lexer.hasToken("as", "first", "last", "not_first", "not_last", "only_one", "default")) {
- source.setMin(lexer.takeInt());
- lexer.token("..");
- source.setMax(lexer.take());
- }
- }
- if (lexer.hasToken("default")) {
- lexer.token("default");
- source.setDefaultValue(new StringType(lexer.readConstant("default value")));
- }
- if (Utilities.existsInList(lexer.getCurrent(), "first", "last", "not_first", "not_last", "only_one"))
- source.setListMode(StructureMapSourceListMode.fromCode(lexer.take()));
-
- if (lexer.hasToken("as")) {
- lexer.take();
- source.setVariable(lexer.take());
- }
- if (lexer.hasToken("where")) {
- lexer.take();
- ExpressionNode node = fpe.parse(lexer);
- source.setUserData(MAP_WHERE_EXPRESSION, node);
- source.setCondition(node.toString());
- }
- if (lexer.hasToken("check")) {
- lexer.take();
- ExpressionNode node = fpe.parse(lexer);
- source.setUserData(MAP_WHERE_CHECK, node);
- source.setCheck(node.toString());
- }
- }
-
- private void parseTarget(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
- StructureMapGroupRuleTargetComponent target = rule.addTarget();
- String start = lexer.take();
- if (lexer.hasToken(".")) {
- target.setContext(start);
- target.setContextType(StructureMapContextType.VARIABLE);
- start = null;
- lexer.token(".");
- target.setElement(lexer.take());
- }
- String name;
- boolean isConstant = false;
- if (lexer.hasToken("=")) {
- if (start != null)
- target.setContext(start);
- lexer.token("=");
- isConstant = lexer.isConstant(true);
- name = lexer.take();
- } else
- name = start;
-
- if ("(".equals(name)) {
- // inline fluentpath expression
- target.setTransform(StructureMapTransform.EVALUATE);
- ExpressionNode node = fpe.parse(lexer);
- target.setUserData(MAP_EXPRESSION, node);
- target.addParameter().setValue(new StringType(node.toString()));
- lexer.token(")");
- } else if (lexer.hasToken("(")) {
- target.setTransform(StructureMapTransform.fromCode(name));
- lexer.token("(");
- if (target.getTransform() == StructureMapTransform.EVALUATE) {
- parseParameter(target, lexer);
- lexer.token(",");
- ExpressionNode node = fpe.parse(lexer);
- target.setUserData(MAP_EXPRESSION, node);
- target.addParameter().setValue(new StringType(node.toString()));
- } else {
- while (!lexer.hasToken(")")) {
- parseParameter(target, lexer);
- if (!lexer.hasToken(")"))
- lexer.token(",");
- }
- }
- lexer.token(")");
- } else if (name != null) {
- target.setTransform(StructureMapTransform.COPY);
- if (!isConstant) {
- String id = name;
- while (lexer.hasToken(".")) {
- id = id + lexer.take() + lexer.take();
- }
- target.addParameter().setValue(new IdType(id));
- }
- else
- target.addParameter().setValue(readConstant(name, lexer));
- }
- if (lexer.hasToken("as")) {
- lexer.take();
- target.setVariable(lexer.take());
- }
- while (Utilities.existsInList(lexer.getCurrent(), "first", "last", "share", "collate")) {
- if (lexer.getCurrent().equals("share")) {
- target.addListMode(StructureMapTargetListMode.SHARE);
- lexer.next();
- target.setListRuleId(lexer.take());
- } else if (lexer.getCurrent().equals("first"))
- target.addListMode(StructureMapTargetListMode.FIRST);
- else
- target.addListMode(StructureMapTargetListMode.LAST);
- lexer.next();
- }
- }
-
-
- private void parseParameter(StructureMapGroupRuleTargetComponent target, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError {
- if (!lexer.isConstant(true)) {
- target.addParameter().setValue(new IdType(lexer.take()));
- } else if (lexer.isStringConstant())
- target.addParameter().setValue(new StringType(lexer.readConstant("??")));
- else {
- target.addParameter().setValue(readConstant(lexer.take(), lexer));
- }
- }
-
- private Type readConstant(String s, FHIRLexer lexer) throws FHIRLexerException {
- if (Utilities.isInteger(s))
- return new IntegerType(s);
- else if (Utilities.isDecimal(s))
- return new DecimalType(s);
- else if (Utilities.existsInList(s, "true", "false"))
- return new BooleanType(s.equals("true"));
- else
- return new StringType(lexer.processConstant(s));
- }
-
- public StructureDefinition getTargetType(StructureMap map) throws FHIRException {
- boolean found = false;
- StructureDefinition res = null;
- for (StructureMapStructureComponent uses : map.getStructure()) {
- if (uses.getMode() == StructureMapModelMode.TARGET) {
- if (found)
- throw new FHIRException("Multiple targets found in map "+map.getUrl());
- found = true;
- res = worker.fetchResource(StructureDefinition.class, uses.getUrl());
- if (res == null)
- throw new FHIRException("Unable to find "+uses.getUrl()+" referenced from map "+map.getUrl());
- }
- }
- if (res == null)
- throw new FHIRException("No targets found in map "+map.getUrl());
- return res;
- }
-
- public enum VariableMode {
- INPUT, OUTPUT
- }
-
- public class Variable {
- private VariableMode mode;
- private String name;
- private Base object;
- public Variable(VariableMode mode, String name, Base object) {
- super();
- this.mode = mode;
- this.name = name;
- this.object = object;
- }
- public VariableMode getMode() {
- return mode;
- }
- public String getName() {
- return name;
- }
- public Base getObject() {
- return object;
- }
- public String summary() {
- return name+": "+object.fhirType();
- }
- }
-
- public class Variables {
- private List list = new ArrayList();
-
- public void add(VariableMode mode, String name, Base object) {
- Variable vv = null;
- for (Variable v : list)
- if ((v.mode == mode) && v.getName().equals(name))
- vv = v;
- if (vv != null)
- list.remove(vv);
- list.add(new Variable(mode, name, object));
- }
-
- public Variables copy() {
- Variables result = new Variables();
- result.list.addAll(list);
- return result;
- }
-
- public Base get(VariableMode mode, String name) {
- for (Variable v : list)
- if ((v.mode == mode) && v.getName().equals(name))
- return v.getObject();
- return null;
- }
-
- public String summary() {
- CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder();
- CommaSeparatedStringBuilder t = new CommaSeparatedStringBuilder();
- for (Variable v : list)
- if (v.mode == VariableMode.INPUT)
- s.append(v.summary());
- else
- t.append(v.summary());
- return "source variables ["+s.toString()+"], target variables ["+t.toString()+"]";
- }
- }
-
- public class TransformContext {
- private Object appInfo;
-
- public TransformContext(Object appInfo) {
- super();
- this.appInfo = appInfo;
- }
-
- public Object getAppInfo() {
- return appInfo;
- }
-
- }
-
- private void log(String cnt) {
- if (services != null)
- services.log(cnt);
- }
-
- /**
- * Given an item, return all the children that conform to the pattern described in name
- *
- * Possible patterns:
- * - a simple name (which may be the base of a name with [] e.g. value[x])
- * - a name with a type replacement e.g. valueCodeableConcept
- * - * which means all children
- * - ** which means all descendents
- *
- * @param item
- * @param name
- * @param result
- * @throws FHIRException
- */
- protected void getChildrenByName(Base item, String name, List result) throws FHIRException {
- for (Base v : item.listChildrenByName(name, true))
- if (v != null)
- result.add(v);
- }
-
- public void transform(Object appInfo, Base source, StructureMap map, Base target) throws FHIRException {
- TransformContext context = new TransformContext(appInfo);
- log("Start Transform "+map.getUrl());
- StructureMapGroupComponent g = map.getGroup().get(0);
-
- Variables vars = new Variables();
- vars.add(VariableMode.INPUT, getInputName(g, StructureMapInputMode.SOURCE, "source"), source);
- vars.add(VariableMode.OUTPUT, getInputName(g, StructureMapInputMode.TARGET, "target"), target);
-
- executeGroup("", context, map, vars, g);
- if (target instanceof Element)
- ((Element) target).sort();
- }
-
- private String getInputName(StructureMapGroupComponent g, StructureMapInputMode mode, String def) throws DefinitionException {
- String name = null;
- for (StructureMapGroupInputComponent inp : g.getInput()) {
- if (inp.getMode() == mode)
- if (name != null)
- throw new DefinitionException("This engine does not support multiple source inputs");
- else
- name = inp.getName();
- }
- return name == null ? def : name;
- }
-
- private void executeGroup(String indent, TransformContext context, StructureMap map, Variables vars, StructureMapGroupComponent group) throws FHIRException {
- log(indent+"Group : "+group.getName());
- // todo: check inputs
- if (group.hasExtends()) {
- ResolvedGroup rg = resolveGroupReference(map, group, group.getExtends());
- executeGroup(indent+" ", context, rg.targetMap, vars, rg.target);
- }
-
- for (StructureMapGroupRuleComponent r : group.getRule()) {
- executeRule(indent+" ", context, map, vars, group, r);
- }
- }
-
- private void executeRule(String indent, TransformContext context, StructureMap map, Variables vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule) throws FHIRException {
- log(indent+"rule : "+rule.getName());
- if (rule.getName().contains("CarePlan.participant-unlink"))
- System.out.println("debug");
- Variables srcVars = vars.copy();
- if (rule.getSource().size() != 1)
- throw new FHIRException("Rule \""+rule.getName()+"\": not handled yet");
- List source = processSource(rule.getName(), context, srcVars, rule.getSource().get(0));
- if (source != null) {
- for (Variables v : source) {
- for (StructureMapGroupRuleTargetComponent t : rule.getTarget()) {
- processTarget(rule.getName(), context, v, map, group, t, rule.getSource().size() == 1 ? rule.getSourceFirstRep().getVariable() : null);
- }
- if (rule.hasRule()) {
- for (StructureMapGroupRuleComponent childrule : rule.getRule()) {
- executeRule(indent +" ", context, map, v, group, childrule);
- }
- } else if (rule.hasDependent()) {
- for (StructureMapGroupRuleDependentComponent dependent : rule.getDependent()) {
- executeDependency(indent+" ", context, map, v, group, dependent);
- }
- } else if (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasVariable() && rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasVariable() && rule.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE && !rule.getTargetFirstRep().hasParameter()) {
- // simple inferred, map by type
- Base src = v.get(VariableMode.INPUT, rule.getSourceFirstRep().getVariable());
- Base tgt = v.get(VariableMode.OUTPUT, rule.getTargetFirstRep().getVariable());
- String srcType = src.fhirType();
- String tgtType = tgt.fhirType();
- ResolvedGroup defGroup = resolveGroupByTypes(map, rule.getName(), group, srcType, tgtType);
- Variables vdef = new Variables();
- vdef.add(VariableMode.INPUT, defGroup.target.getInput().get(0).getName(), src);
- vdef.add(VariableMode.OUTPUT, defGroup.target.getInput().get(1).getName(), tgt);
- executeGroup(indent+" ", context, defGroup.targetMap, vdef, defGroup.target);
- }
- }
- }
- }
-
- private void executeDependency(String indent, TransformContext context, StructureMap map, Variables vin, StructureMapGroupComponent group, StructureMapGroupRuleDependentComponent dependent) throws FHIRException {
- ResolvedGroup rg = resolveGroupReference(map, group, dependent.getName());
-
- if (rg.target.getInput().size() != dependent.getVariable().size()) {
- throw new FHIRException("Rule '"+dependent.getName()+"' has "+Integer.toString(rg.target.getInput().size())+" but the invocation has "+Integer.toString(dependent.getVariable().size())+" variables");
- }
- Variables v = new Variables();
- for (int i = 0; i < rg.target.getInput().size(); i++) {
- StructureMapGroupInputComponent input = rg.target.getInput().get(i);
- StringType rdp = dependent.getVariable().get(i);
- String var = rdp.asStringValue();
- VariableMode mode = input.getMode() == StructureMapInputMode.SOURCE ? VariableMode.INPUT : VariableMode.OUTPUT;
- Base vv = vin.get(mode, var);
- if (vv == null && mode == VariableMode.INPUT) //* once source, always source. but target can be treated as source at user convenient
- vv = vin.get(VariableMode.OUTPUT, var);
- if (vv == null)
- throw new FHIRException("Rule '"+dependent.getName()+"' "+mode.toString()+" variable '"+input.getName()+"' named as '"+var+"' has no value");
- v.add(mode, input.getName(), vv);
- }
- executeGroup(indent+" ", context, rg.targetMap, v, rg.target);
- }
-
- private String determineTypeFromSourceType(StructureMap map, StructureMapGroupComponent source, Base base, String[] types) throws FHIRException {
- String type = base.fhirType();
- String kn = "type^"+type;
- if (source.hasUserData(kn))
- return source.getUserString(kn);
-
- ResolvedGroup res = new ResolvedGroup();
- res.targetMap = null;
- res.target = null;
- for (StructureMapGroupComponent grp : map.getGroup()) {
- if (matchesByType(map, grp, type)) {
- if (res.targetMap == null) {
- res.targetMap = map;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches looking for default rule for '"+type+"'");
- }
- }
- if (res.targetMap != null) {
- String result = getActualType(res.targetMap, res.target.getInput().get(1).getType());
- source.setUserData(kn, result);
- return result;
- }
-
- for (UriType imp : map.getImport()) {
- List impMapList = findMatchingMaps(imp.getValue());
- if (impMapList.size() == 0)
- throw new FHIRException("Unable to find map(s) for "+imp.getValue());
- for (StructureMap impMap : impMapList) {
- if (!impMap.getUrl().equals(map.getUrl())) {
- for (StructureMapGroupComponent grp : impMap.getGroup()) {
- if (matchesByType(impMap, grp, type)) {
- if (res.targetMap == null) {
- res.targetMap = impMap;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches for default rule for '"+type+"' in "+res.targetMap.getUrl()+" ("+res.target.getName()+") and "+impMap.getUrl()+" ("+grp.getName()+")");
- }
- }
- }
- }
- }
- if (res.target == null)
- throw new FHIRException("No matches found for default rule for '"+type+"' from "+map.getUrl());
- String result = getActualType(res.targetMap, res.target.getInput().get(1).getType()); // should be .getType, but R2...
- source.setUserData(kn, result);
- return result;
- }
-
- private List findMatchingMaps(String value) {
- List res = new ArrayList();
- if (value.contains("*")) {
- for (StructureMap sm : library.values()) {
- if (urlMatches(value, sm.getUrl())) {
- res.add(sm);
- }
- }
- } else {
- StructureMap sm = library.get(value);
- if (sm != null)
- res.add(sm);
- }
- Set check = new HashSet();
- for (StructureMap sm : res) {
- if (check.contains(sm.getUrl()))
- throw new Error("duplicate");
- else
- check.add(sm.getUrl());
- }
- return res;
- }
-
- private boolean urlMatches(String mask, String url) {
- return url.length() > mask.length() && url.startsWith(mask.substring(0, mask.indexOf("*"))) && url.endsWith(mask.substring(mask.indexOf("*")+1)) ;
- }
-
- private ResolvedGroup resolveGroupByTypes(StructureMap map, String ruleid, StructureMapGroupComponent source, String srcType, String tgtType) throws FHIRException {
- String kn = "types^"+srcType+":"+tgtType;
- if (source.hasUserData(kn))
- return (ResolvedGroup) source.getUserData(kn);
-
- ResolvedGroup res = new ResolvedGroup();
- res.targetMap = null;
- res.target = null;
- for (StructureMapGroupComponent grp : map.getGroup()) {
- if (matchesByType(map, grp, srcType, tgtType)) {
- if (res.targetMap == null) {
- res.targetMap = map;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches looking for rule for '"+srcType+"/"+tgtType+"', from rule '"+ruleid+"'");
- }
- }
- if (res.targetMap != null) {
- source.setUserData(kn, res);
- return res;
- }
-
- for (UriType imp : map.getImport()) {
- List impMapList = findMatchingMaps(imp.getValue());
- if (impMapList.size() == 0)
- throw new FHIRException("Unable to find map(s) for "+imp.getValue());
- for (StructureMap impMap : impMapList) {
- if (!impMap.getUrl().equals(map.getUrl())) {
- for (StructureMapGroupComponent grp : impMap.getGroup()) {
- if (matchesByType(impMap, grp, srcType, tgtType)) {
- if (res.targetMap == null) {
- res.targetMap = impMap;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches for rule for '"+srcType+"/"+tgtType+"' in "+res.targetMap.getUrl()+" and "+impMap.getUrl()+", from rule '"+ruleid+"'");
- }
- }
- }
- }
- }
- if (res.target == null)
- throw new FHIRException("No matches found for rule for '"+srcType+"/"+tgtType+"' from "+map.getUrl()+", from rule '"+ruleid+"'");
- source.setUserData(kn, res);
- return res;
- }
-
-
- private boolean matchesByType(StructureMap map, StructureMapGroupComponent grp, String type) throws FHIRException {
- if (grp.getTypeMode() != StructureMapGroupTypeMode.TYPEANDTYPES)
- return false;
- if (grp.getInput().size() != 2 || grp.getInput().get(0).getMode() != StructureMapInputMode.SOURCE || grp.getInput().get(1).getMode() != StructureMapInputMode.TARGET)
- return false;
- return matchesType(map, type, grp.getInput().get(0).getType());
- }
-
- private boolean matchesByType(StructureMap map, StructureMapGroupComponent grp, String srcType, String tgtType) throws FHIRException {
- if (grp.getTypeMode() == StructureMapGroupTypeMode.NONE)
- return false;
- if (grp.getInput().size() != 2 || grp.getInput().get(0).getMode() != StructureMapInputMode.SOURCE || grp.getInput().get(1).getMode() != StructureMapInputMode.TARGET)
- return false;
- if (!grp.getInput().get(0).hasType() || !grp.getInput().get(1).hasType())
- return false;
- return matchesType(map, srcType, grp.getInput().get(0).getType()) && matchesType(map, tgtType, grp.getInput().get(1).getType());
- }
-
- private boolean matchesType(StructureMap map, String actualType, String statedType) throws FHIRException {
- // check the aliases
- for (StructureMapStructureComponent imp : map.getStructure()) {
- if (imp.hasAlias() && statedType.equals(imp.getAlias())) {
- StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
- if (sd != null)
- statedType = sd.getType();
- break;
- }
- }
-
- return actualType.equals(statedType);
- }
-
- private String getActualType(StructureMap map, String statedType) throws FHIRException {
- // check the aliases
- for (StructureMapStructureComponent imp : map.getStructure()) {
- if (imp.hasAlias() && statedType.equals(imp.getAlias())) {
- StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
- if (sd == null)
- throw new FHIRException("Unable to resolve structure "+imp.getUrl());
- return sd.getId(); // should be sd.getType(), but R2...
- }
- }
- return statedType;
- }
-
-
- private ResolvedGroup resolveGroupReference(StructureMap map, StructureMapGroupComponent source, String name) throws FHIRException {
- String kn = "ref^"+name;
- if (source.hasUserData(kn))
- return (ResolvedGroup) source.getUserData(kn);
-
- ResolvedGroup res = new ResolvedGroup();
- res.targetMap = null;
- res.target = null;
- for (StructureMapGroupComponent grp : map.getGroup()) {
- if (grp.getName().equals(name)) {
- if (res.targetMap == null) {
- res.targetMap = map;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches for rule '"+name+"'");
- }
- }
- if (res.targetMap != null) {
- source.setUserData(kn, res);
- return res;
- }
-
- for (UriType imp : map.getImport()) {
- List impMapList = findMatchingMaps(imp.getValue());
- if (impMapList.size() == 0)
- throw new FHIRException("Unable to find map(s) for "+imp.getValue());
- for (StructureMap impMap : impMapList) {
- if (!impMap.getUrl().equals(map.getUrl())) {
- for (StructureMapGroupComponent grp : impMap.getGroup()) {
- if (grp.getName().equals(name)) {
- if (res.targetMap == null) {
- res.targetMap = impMap;
- res.target = grp;
- } else
- throw new FHIRException("Multiple possible matches for rule '"+name+"' in "+res.targetMap.getUrl()+" and "+impMap.getUrl());
- }
- }
- }
- }
- }
- if (res.target == null)
- throw new FHIRException("No matches found for rule '"+name+"'. Reference found in "+map.getUrl());
- source.setUserData(kn, res);
- return res;
- }
-
- private List processSource(String ruleId, TransformContext context, Variables vars, StructureMapGroupRuleSourceComponent src) throws FHIRException {
- List items;
- if (src.getContext().equals("@search")) {
- ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_SEARCH_EXPRESSION);
- if (expr == null) {
- expr = fpe.parse(src.getElement());
- src.setUserData(MAP_SEARCH_EXPRESSION, expr);
- }
- String search = fpe.evaluateToString(vars, null, new StringType(), expr); // string is a holder of nothing to ensure that variables are processed correctly
- items = services.performSearch(context.appInfo, search);
- } else {
- items = new ArrayList();
- Base b = vars.get(VariableMode.INPUT, src.getContext());
- if (b == null)
- throw new FHIRException("Unknown input variable "+src.getContext());
-
- if (!src.hasElement())
- items.add(b);
- else {
- getChildrenByName(b, src.getElement(), items);
- if (items.size() == 0 && src.hasDefaultValue())
- items.add(src.getDefaultValue());
- }
- }
-
- if (src.hasType()) {
- List remove = new ArrayList();
- for (Base item : items) {
- if (item != null && !isType(item, src.getType())) {
- remove.add(item);
- }
- }
- items.removeAll(remove);
- }
-
- if (src.hasCondition()) {
- ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_WHERE_EXPRESSION);
- if (expr == null) {
- expr = fpe.parse(src.getCondition());
- // fpe.check(context.appInfo, ??, ??, expr)
- src.setUserData(MAP_WHERE_EXPRESSION, expr);
- }
- List remove = new ArrayList();
- for (Base item : items) {
- if (!fpe.evaluateToBoolean(vars, null, item, expr))
- remove.add(item);
- }
- items.removeAll(remove);
- }
-
- if (src.hasCheck()) {
- ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_WHERE_CHECK);
- if (expr == null) {
- expr = fpe.parse(src.getCheck());
- // fpe.check(context.appInfo, ??, ??, expr)
- src.setUserData(MAP_WHERE_CHECK, expr);
- }
- List remove = new ArrayList();
- for (Base item : items) {
- if (!fpe.evaluateToBoolean(vars, null, item, expr))
- throw new FHIRException("Rule \""+ruleId+"\": Check condition failed");
- }
- }
-
-
- if (src.hasListMode() && !items.isEmpty()) {
- switch (src.getListMode()) {
- case FIRST:
- Base bt = items.get(0);
- items.clear();
- items.add(bt);
- break;
- case NOTFIRST:
- if (items.size() > 0)
- items.remove(0);
- break;
- case LAST:
- bt = items.get(items.size()-1);
- items.clear();
- items.add(bt);
- break;
- case NOTLAST:
- if (items.size() > 0)
- items.remove(items.size()-1);
- break;
- case ONLYONE:
- if (items.size() > 1)
- throw new FHIRException("Rule \""+ruleId+"\": Check condition failed: the collection has more than one item");
- break;
- case NULL:
- }
- }
- List result = new ArrayList();
- for (Base r : items) {
- Variables v = vars.copy();
- if (src.hasVariable())
- v.add(VariableMode.INPUT, src.getVariable(), r);
- result.add(v);
- }
- return result;
- }
-
-
- private boolean isType(Base item, String type) {
- if (type.equals(item.fhirType()))
- return true;
- return false;
- }
-
- private void processTarget(String ruleId, TransformContext context, Variables vars, StructureMap map, StructureMapGroupComponent group, StructureMapGroupRuleTargetComponent tgt, String srcVar) throws FHIRException {
- Base dest = null;
- if (tgt.hasContext()) {
- dest = vars.get(VariableMode.OUTPUT, tgt.getContext());
- if (dest == null)
- throw new FHIRException("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
- if (!tgt.hasElement())
- throw new FHIRException("Rule \""+ruleId+"\": Not supported yet");
- }
- Base v = null;
- if (tgt.hasTransform()) {
- v = runTransform(ruleId, context, map, group, tgt, vars, dest, tgt.getElement(), srcVar);
- if (v != null && dest != null)
- v = dest.setProperty(tgt.getElement().hashCode(), tgt.getElement(), v); // reset v because some implementations may have to rewrite v when setting the value
- } else if (dest != null)
- v = dest.makeProperty(tgt.getElement().hashCode(), tgt.getElement());
- if (tgt.hasVariable() && v != null)
- vars.add(VariableMode.OUTPUT, tgt.getVariable(), v);
- }
-
- private Base runTransform(String ruleId, TransformContext context, StructureMap map, StructureMapGroupComponent group, StructureMapGroupRuleTargetComponent tgt, Variables vars, Base dest, String element, String srcVar) throws FHIRException {
- try {
- switch (tgt.getTransform()) {
- case CREATE :
- String tn;
- if (tgt.getParameter().isEmpty()) {
- // we have to work out the type. First, we see if there is a single type for the target. If there is, we use that
- String[] types = dest.getTypesForProperty(element.hashCode(), element);
- if (types.length == 1 && !"*".equals(types[0]) && !types[0].equals("Resource"))
- tn = types[0];
- else if (srcVar != null) {
- tn = determineTypeFromSourceType(map, group, vars.get(VariableMode.INPUT, srcVar), types);
- } else
- throw new Error("Cannot determine type implicitly because there is no single input variable");
- } else
- tn = getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString());
- Base res = services != null ? services.createType(context.getAppInfo(), tn) : ResourceFactory.createResourceOrType(tn);
- if (res.isResource() && !res.fhirType().equals("Parameters")) {
-// res.setIdBase(tgt.getParameter().size() > 1 ? getParamString(vars, tgt.getParameter().get(0)) : UUID.randomUUID().toString().toLowerCase());
- if (services != null)
- res = services.createResource(context.getAppInfo(), res);
- }
- if (tgt.hasUserData("profile"))
- res.setUserData("profile", tgt.getUserData("profile"));
- return res;
- case COPY :
- return getParam(vars, tgt.getParameter().get(0));
- case EVALUATE :
- ExpressionNode expr = (ExpressionNode) tgt.getUserData(MAP_EXPRESSION);
- if (expr == null) {
- expr = fpe.parse(getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString()));
- tgt.setUserData(MAP_WHERE_EXPRESSION, expr);
- }
- List v = fpe.evaluate(vars, null, tgt.getParameter().size() == 2 ? getParam(vars, tgt.getParameter().get(0)) : new BooleanType(false), expr);
- if (v.size() == 0)
- return null;
- else if (v.size() != 1)
- throw new FHIRException("Rule \""+ruleId+"\": Evaluation of "+expr.toString()+" returned "+Integer.toString(v.size())+" objects");
- else
- return v.get(0);
-
- case TRUNCATE :
- String src = getParamString(vars, tgt.getParameter().get(0));
- String len = getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString());
- if (Utilities.isInteger(len)) {
- int l = Integer.parseInt(len);
- if (src.length() > l)
- src = src.substring(0, l);
- }
- return new StringType(src);
- case ESCAPE :
- throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
- case CAST :
- throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
- case APPEND :
- throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
- case TRANSLATE :
- return translate(context, map, vars, tgt.getParameter());
- case REFERENCE :
- Base b = getParam(vars, tgt.getParameter().get(0));
- if (b == null)
- throw new FHIRException("Rule \""+ruleId+"\": Unable to find parameter "+((IdType) tgt.getParameter().get(0).getValue()).asStringValue());
- if (!b.isResource())
- throw new FHIRException("Rule \""+ruleId+"\": Transform engine cannot point at an element of type "+b.fhirType());
- else {
- String id = b.getIdBase();
- if (id == null) {
- id = UUID.randomUUID().toString().toLowerCase();
- b.setIdBase(id);
- }
- return new Reference().setReference(b.fhirType()+"/"+id);
- }
- case DATEOP :
- throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
- case UUID :
- return new IdType(UUID.randomUUID().toString());
- case POINTER :
- b = getParam(vars, tgt.getParameter().get(0));
- if (b instanceof Resource)
- return new UriType("urn:uuid:"+((Resource) b).getId());
- else
- throw new FHIRException("Rule \""+ruleId+"\": Transform engine cannot point at an element of type "+b.fhirType());
- case CC:
- CodeableConcept cc = new CodeableConcept();
- cc.addCoding(buildCoding(getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString()), getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString())));
- return cc;
- case C:
- Coding c = buildCoding(getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString()), getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString()));
- return c;
- default:
- throw new Error("Rule \""+ruleId+"\": Transform Unknown: "+tgt.getTransform().toCode());
- }
- } catch (Exception e) {
- throw new FHIRException("Exception executing transform "+tgt.toString()+" on Rule \""+ruleId+"\": "+e.getMessage(), e);
- }
- }
-
-
- private Coding buildCoding(String uri, String code) throws FHIRException {
- // if we can get this as a valueSet, we will
- String system = null;
- String display = null;
- ValueSet vs = Utilities.noString(uri) ? null : worker.fetchResourceWithException(ValueSet.class, uri);
- if (vs != null) {
- ValueSetExpansionOutcome vse = worker.expandVS(vs, true, false);
- if (vse.getError() != null)
- throw new FHIRException(vse.getError());
- CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
- for (ValueSetExpansionContainsComponent t : vse.getValueset().getExpansion().getContains()) {
- if (t.hasCode())
- b.append(t.getCode());
- if (code.equals(t.getCode()) && t.hasSystem()) {
- system = t.getSystem();
- display = t.getDisplay();
- break;
- }
- if (code.equalsIgnoreCase(t.getDisplay()) && t.hasSystem()) {
- system = t.getSystem();
- display = t.getDisplay();
- break;
- }
- }
- if (system == null)
- throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)");
- } else
- system = uri;
- ValidationResult vr = worker.validateCode(system, code, null);
- if (vr != null && vr.getDisplay() != null)
- display = vr.getDisplay();
- return new Coding().setSystem(system).setCode(code).setDisplay(display);
- }
-
-
- private String getParamStringNoNull(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter, String message) throws FHIRException {
- Base b = getParam(vars, parameter);
- if (b == null)
- throw new FHIRException("Unable to find a value for "+parameter.toString()+". Context: "+message);
- if (!b.hasPrimitiveValue())
- throw new FHIRException("Found a value for "+parameter.toString()+", but it has a type of "+b.fhirType()+" and cannot be treated as a string. Context: "+message);
- return b.primitiveValue();
- }
-
- private String getParamString(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
- Base b = getParam(vars, parameter);
- if (b == null || !b.hasPrimitiveValue())
- return null;
- return b.primitiveValue();
- }
-
-
- private Base getParam(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
- Type p = parameter.getValue();
- if (!(p instanceof IdType))
- return p;
- else {
- String n = ((IdType) p).asStringValue();
- Base b = vars.get(VariableMode.INPUT, n);
- if (b == null)
- b = vars.get(VariableMode.OUTPUT, n);
- if (b == null)
- throw new DefinitionException("Variable "+n+" not found ("+vars.summary()+")");
- return b;
- }
- }
-
-
- private Base translate(TransformContext context, StructureMap map, Variables vars, List parameter) throws FHIRException {
- Base src = getParam(vars, parameter.get(0));
- String id = getParamString(vars, parameter.get(1));
- String fld = parameter.size() > 2 ? getParamString(vars, parameter.get(2)) : null;
- return translate(context, map, src, id, fld);
- }
-
- private class SourceElementComponentWrapper {
- private ConceptMapGroupComponent group;
- private SourceElementComponent comp;
- public SourceElementComponentWrapper(ConceptMapGroupComponent group, SourceElementComponent comp) {
- super();
- this.group = group;
- this.comp = comp;
- }
- }
- public Base translate(TransformContext context, StructureMap map, Base source, String conceptMapUrl, String fieldToReturn) throws FHIRException {
- Coding src = new Coding();
- if (source.isPrimitive()) {
- src.setCode(source.primitiveValue());
- } else if ("Coding".equals(source.fhirType())) {
- Base[] b = source.getProperty("system".hashCode(), "system", true);
- if (b.length == 1)
- src.setSystem(b[0].primitiveValue());
- b = source.getProperty("code".hashCode(), "code", true);
- if (b.length == 1)
- src.setCode(b[0].primitiveValue());
- } else if ("CE".equals(source.fhirType())) {
- Base[] b = source.getProperty("codeSystem".hashCode(), "codeSystem", true);
- if (b.length == 1)
- src.setSystem(b[0].primitiveValue());
- b = source.getProperty("code".hashCode(), "code", true);
- if (b.length == 1)
- src.setCode(b[0].primitiveValue());
- } else
- throw new FHIRException("Unable to translate source "+source.fhirType());
-
- String su = conceptMapUrl;
- if (conceptMapUrl.equals("http://hl7.org/fhir/ConceptMap/special-oid2uri")) {
- String uri = worker.oid2Uri(src.getCode());
- if (uri == null)
- uri = "urn:oid:"+src.getCode();
- if ("uri".equals(fieldToReturn))
- return new UriType(uri);
- else
- throw new FHIRException("Error in return code");
- } else {
- ConceptMap cmap = null;
- if (conceptMapUrl.startsWith("#")) {
- for (Resource r : map.getContained()) {
- if (r instanceof ConceptMap && ((ConceptMap) r).getId().equals(conceptMapUrl.substring(1))) {
- cmap = (ConceptMap) r;
- su = map.getUrl()+conceptMapUrl;
- }
- }
- if (cmap == null)
- throw new FHIRException("Unable to translate - cannot find map "+conceptMapUrl);
- } else
- cmap = worker.fetchResource(ConceptMap.class, conceptMapUrl);
- Coding outcome = null;
- boolean done = false;
- String message = null;
- if (cmap == null) {
- if (services == null)
- message = "No map found for "+conceptMapUrl;
- else {
- outcome = services.translate(context.appInfo, src, conceptMapUrl);
- done = true;
- }
- } else {
- List list = new ArrayList();
- for (ConceptMapGroupComponent g : cmap.getGroup()) {
- for (SourceElementComponent e : g.getElement()) {
- if (!src.hasSystem() && src.getCode().equals(e.getCode()))
- list.add(new SourceElementComponentWrapper(g, e));
- else if (src.hasSystem() && src.getSystem().equals(g.getSource()) && src.getCode().equals(e.getCode()))
- list.add(new SourceElementComponentWrapper(g, e));
- }
- }
- if (list.size() == 0)
- done = true;
- else if (list.get(0).comp.getTarget().size() == 0)
- message = "Concept map "+su+" found no translation for "+src.getCode();
- else {
- for (TargetElementComponent tgt : list.get(0).comp.getTarget()) {
- if (tgt.getEquivalence() == null || EnumSet.of( ConceptMapEquivalence.EQUAL , ConceptMapEquivalence.RELATEDTO , ConceptMapEquivalence.EQUIVALENT, ConceptMapEquivalence.WIDER).contains(tgt.getEquivalence())) {
- if (done) {
- message = "Concept map "+su+" found multiple matches for "+src.getCode();
- done = false;
- } else {
- done = true;
- outcome = new Coding().setCode(tgt.getCode()).setSystem(list.get(0).group.getTarget());
- }
- } else if (tgt.getEquivalence() == ConceptMapEquivalence.UNMATCHED) {
- done = true;
- }
- }
- if (!done)
- message = "Concept map "+su+" found no usable translation for "+src.getCode();
- }
- }
- if (!done)
- throw new FHIRException(message);
- if (outcome == null)
- return null;
- if ("code".equals(fieldToReturn))
- return new CodeType(outcome.getCode());
- else
- return outcome;
- }
- }
-
-
- public Map getLibrary() {
- return library;
- }
-
- public class PropertyWithType {
- private String path;
- private Property baseProperty;
- private Property profileProperty;
- private TypeDetails types;
- public PropertyWithType(String path, Property baseProperty, Property profileProperty, TypeDetails types) {
- super();
- this.baseProperty = baseProperty;
- this.profileProperty = profileProperty;
- this.path = path;
- this.types = types;
- }
-
- public TypeDetails getTypes() {
- return types;
- }
- public String getPath() {
- return path;
- }
-
- public Property getBaseProperty() {
- return baseProperty;
- }
-
- public void setBaseProperty(Property baseProperty) {
- this.baseProperty = baseProperty;
- }
-
- public Property getProfileProperty() {
- return profileProperty;
- }
-
- public void setProfileProperty(Property profileProperty) {
- this.profileProperty = profileProperty;
- }
-
- public String summary() {
- return path;
- }
-
- }
-
- public class VariableForProfiling {
- private VariableMode mode;
- private String name;
- private PropertyWithType property;
-
- public VariableForProfiling(VariableMode mode, String name, PropertyWithType property) {
- super();
- this.mode = mode;
- this.name = name;
- this.property = property;
- }
- public VariableMode getMode() {
- return mode;
- }
- public String getName() {
- return name;
- }
- public PropertyWithType getProperty() {
- return property;
- }
- public String summary() {
- return name+": "+property.summary();
- }
- }
-
- public class VariablesForProfiling {
- private List list = new ArrayList();
- private boolean optional;
- private boolean repeating;
-
- public VariablesForProfiling(boolean optional, boolean repeating) {
- this.optional = optional;
- this.repeating = repeating;
- }
-
- public void add(VariableMode mode, String name, String path, Property property, TypeDetails types) {
- add(mode, name, new PropertyWithType(path, property, null, types));
- }
-
- public void add(VariableMode mode, String name, String path, Property baseProperty, Property profileProperty, TypeDetails types) {
- add(mode, name, new PropertyWithType(path, baseProperty, profileProperty, types));
- }
-
- public void add(VariableMode mode, String name, PropertyWithType property) {
- VariableForProfiling vv = null;
- for (VariableForProfiling v : list)
- if ((v.mode == mode) && v.getName().equals(name))
- vv = v;
- if (vv != null)
- list.remove(vv);
- list.add(new VariableForProfiling(mode, name, property));
- }
-
- public VariablesForProfiling copy(boolean optional, boolean repeating) {
- VariablesForProfiling result = new VariablesForProfiling(optional, repeating);
- result.list.addAll(list);
- return result;
- }
-
- public VariablesForProfiling copy() {
- VariablesForProfiling result = new VariablesForProfiling(optional, repeating);
- result.list.addAll(list);
- return result;
- }
-
- public VariableForProfiling get(VariableMode mode, String name) {
- if (mode == null) {
- for (VariableForProfiling v : list)
- if ((v.mode == VariableMode.OUTPUT) && v.getName().equals(name))
- return v;
- for (VariableForProfiling v : list)
- if ((v.mode == VariableMode.INPUT) && v.getName().equals(name))
- return v;
- }
- for (VariableForProfiling v : list)
- if ((v.mode == mode) && v.getName().equals(name))
- return v;
- return null;
- }
-
- public String summary() {
- CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder();
- CommaSeparatedStringBuilder t = new CommaSeparatedStringBuilder();
- for (VariableForProfiling v : list)
- if (v.mode == VariableMode.INPUT)
- s.append(v.summary());
- else
- t.append(v.summary());
- return "source variables ["+s.toString()+"], target variables ["+t.toString()+"]";
- }
- }
-
- public class StructureMapAnalysis {
- private List profiles = new ArrayList();
- private XhtmlNode summary;
- public List getProfiles() {
- return profiles;
- }
- public XhtmlNode getSummary() {
- return summary;
- }
-
- }
-
- /**
- * Given a structure map, return a set of analyses on it.
- *
- * Returned:
- * - a list or profiles for what it will create. First profile is the target
- * - a table with a summary (in xhtml) for easy human undertanding of the mapping
- *
- *
- * @param appInfo
- * @param map
- * @return
- * @throws Exception
- */
- public StructureMapAnalysis analyse(Object appInfo, StructureMap map) throws Exception {
- ids.clear();
- StructureMapAnalysis result = new StructureMapAnalysis();
- TransformContext context = new TransformContext(appInfo);
- VariablesForProfiling vars = new VariablesForProfiling(false, false);
- StructureMapGroupComponent start = map.getGroup().get(0);
- for (StructureMapGroupInputComponent t : start.getInput()) {
- PropertyWithType ti = resolveType(map, t.getType(), t.getMode());
- if (t.getMode() == StructureMapInputMode.SOURCE)
- vars.add(VariableMode.INPUT, t.getName(), ti);
- else
- vars.add(VariableMode.OUTPUT, t.getName(), createProfile(map, result.profiles, ti, start.getName(), start));
- }
-
- result.summary = new XhtmlNode(NodeType.Element, "table").setAttribute("class", "grid");
- XhtmlNode tr = result.summary.addTag("tr");
- tr.addTag("td").addTag("b").addText("Source");
- tr.addTag("td").addTag("b").addText("Target");
-
- log("Start Profiling Transform "+map.getUrl());
- analyseGroup("", context, map, vars, start, result);
- ProfileUtilities pu = new ProfileUtilities(worker, null, pkp);
- for (StructureDefinition sd : result.getProfiles())
- pu.cleanUpDifferential(sd);
- return result;
- }
-
-
- private void analyseGroup(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapAnalysis result) throws Exception {
- log(indent+"Analyse Group : "+group.getName());
- // todo: extends
- // todo: check inputs
- XhtmlNode tr = result.summary.addTag("tr").setAttribute("class", "diff-title");
- XhtmlNode xs = tr.addTag("td");
- XhtmlNode xt = tr.addTag("td");
- for (StructureMapGroupInputComponent inp : group.getInput()) {
- if (inp.getMode() == StructureMapInputMode.SOURCE)
- noteInput(vars, inp, VariableMode.INPUT, xs);
- if (inp.getMode() == StructureMapInputMode.TARGET)
- noteInput(vars, inp, VariableMode.OUTPUT, xt);
- }
- for (StructureMapGroupRuleComponent r : group.getRule()) {
- analyseRule(indent+" ", context, map, vars, group, r, result);
- }
- }
-
-
- private void noteInput(VariablesForProfiling vars, StructureMapGroupInputComponent inp, VariableMode mode, XhtmlNode xs) {
- VariableForProfiling v = vars.get(mode, inp.getName());
- if (v != null)
- xs.addText("Input: "+v.property.getPath());
- }
-
- private void analyseRule(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule, StructureMapAnalysis result) throws Exception {
- log(indent+"Analyse rule : "+rule.getName());
- XhtmlNode tr = result.summary.addTag("tr");
- XhtmlNode xs = tr.addTag("td");
- XhtmlNode xt = tr.addTag("td");
-
- VariablesForProfiling srcVars = vars.copy();
- if (rule.getSource().size() != 1)
- throw new Exception("Rule \""+rule.getName()+"\": not handled yet");
- VariablesForProfiling source = analyseSource(rule.getName(), context, srcVars, rule.getSourceFirstRep(), xs);
-
- TargetWriter tw = new TargetWriter();
- for (StructureMapGroupRuleTargetComponent t : rule.getTarget()) {
- analyseTarget(rule.getName(), context, source, map, t, rule.getSourceFirstRep().getVariable(), tw, result.profiles, rule.getName());
- }
- tw.commit(xt);
-
- for (StructureMapGroupRuleComponent childrule : rule.getRule()) {
- analyseRule(indent+" ", context, map, source, group, childrule, result);
- }
-// for (StructureMapGroupRuleDependentComponent dependent : rule.getDependent()) {
-// executeDependency(indent+" ", context, map, v, group, dependent); // do we need group here?
-// }
- }
-
- public class StringPair {
- private String var;
- private String desc;
- public StringPair(String var, String desc) {
- super();
- this.var = var;
- this.desc = desc;
- }
- public String getVar() {
- return var;
- }
- public String getDesc() {
- return desc;
- }
- }
- public class TargetWriter {
- private Map newResources = new HashMap();
- private List assignments = new ArrayList();
- private List keyProps = new ArrayList();
- private CommaSeparatedStringBuilder txt = new CommaSeparatedStringBuilder();
-
- public void newResource(String var, String name) {
- newResources.put(var, name);
- txt.append("new "+name);
- }
-
- public void valueAssignment(String context, String desc) {
- assignments.add(new StringPair(context, desc));
- txt.append(desc);
- }
-
- public void keyAssignment(String context, String desc) {
- keyProps.add(new StringPair(context, desc));
- txt.append(desc);
- }
- public void commit(XhtmlNode xt) {
- if (newResources.size() == 1 && assignments.size() == 1 && newResources.containsKey(assignments.get(0).getVar()) && keyProps.size() == 1 && newResources.containsKey(keyProps.get(0).getVar()) ) {
- xt.addText("new "+assignments.get(0).desc+" ("+keyProps.get(0).desc.substring(keyProps.get(0).desc.indexOf(".")+1)+")");
- } else if (newResources.size() == 1 && assignments.size() == 1 && newResources.containsKey(assignments.get(0).getVar()) && keyProps.size() == 0) {
- xt.addText("new "+assignments.get(0).desc);
- } else {
- xt.addText(txt.toString());
- }
- }
- }
-
- private VariablesForProfiling analyseSource(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMapGroupRuleSourceComponent src, XhtmlNode td) throws Exception {
- VariableForProfiling var = vars.get(VariableMode.INPUT, src.getContext());
- if (var == null)
- throw new FHIRException("Rule \""+ruleId+"\": Unknown input variable "+src.getContext());
- PropertyWithType prop = var.getProperty();
-
- boolean optional = false;
- boolean repeating = false;
-
- if (src.hasCondition()) {
- optional = true;
- }
-
- if (src.hasElement()) {
- Property element = prop.getBaseProperty().getChild(prop.types.getType(), src.getElement());
- if (element == null)
- throw new Exception("Rule \""+ruleId+"\": Unknown element name "+src.getElement());
- if (element.getDefinition().getMin() == 0)
- optional = true;
- if (element.getDefinition().getMax().equals("*"))
- repeating = true;
- VariablesForProfiling result = vars.copy(optional, repeating);
- TypeDetails type = new TypeDetails(CollectionStatus.SINGLETON);
- for (TypeRefComponent tr : element.getDefinition().getType()) {
- if (!tr.hasCode())
- throw new Error("Rule \""+ruleId+"\": Element has no type");
- ProfiledType pt = new ProfiledType(tr.getCode());
- if (tr.hasProfile())
- pt.addProfile(tr.getProfile());
- if (element.getDefinition().hasBinding())
- pt.addBinding(element.getDefinition().getBinding());
- type.addType(pt);
- }
- td.addText(prop.getPath()+"."+src.getElement());
- if (src.hasVariable())
- result.add(VariableMode.INPUT, src.getVariable(), new PropertyWithType(prop.getPath()+"."+src.getElement(), element, null, type));
- return result;
- } else {
- td.addText(prop.getPath()); // ditto!
- return vars.copy(optional, repeating);
- }
- }
-
-
- private void analyseTarget(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMap map, StructureMapGroupRuleTargetComponent tgt, String tv, TargetWriter tw, List profiles, String sliceName) throws Exception {
- VariableForProfiling var = null;
- if (tgt.hasContext()) {
- var = vars.get(VariableMode.OUTPUT, tgt.getContext());
- if (var == null)
- throw new Exception("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
- if (!tgt.hasElement())
- throw new Exception("Rule \""+ruleId+"\": Not supported yet");
- }
-
-
- TypeDetails type = null;
- if (tgt.hasTransform()) {
- type = analyseTransform(context, map, tgt, var, vars);
- // profiling: dest.setProperty(tgt.getElement().hashCode(), tgt.getElement(), v);
- } else {
- Property vp = var.property.baseProperty.getChild(tgt.getElement(), tgt.getElement());
- if (vp == null)
- throw new Exception("Unknown Property "+tgt.getElement()+" on "+var.property.path);
-
- type = new TypeDetails(CollectionStatus.SINGLETON, vp.getType(tgt.getElement()));
- }
-
- if (tgt.getTransform() == StructureMapTransform.CREATE) {
- String s = getParamString(vars, tgt.getParameter().get(0));
- if (worker.getResourceNames().contains(s))
- tw.newResource(tgt.getVariable(), s);
- } else {
- boolean mapsSrc = false;
- for (StructureMapGroupRuleTargetParameterComponent p : tgt.getParameter()) {
- Type pr = p.getValue();
- if (pr instanceof IdType && ((IdType) pr).asStringValue().equals(tv))
- mapsSrc = true;
- }
- if (mapsSrc) {
- if (var == null)
- throw new Error("Rule \""+ruleId+"\": Attempt to assign with no context");
- tw.valueAssignment(tgt.getContext(), var.property.getPath()+"."+tgt.getElement()+getTransformSuffix(tgt.getTransform()));
- } else if (tgt.hasContext()) {
- if (isSignificantElement(var.property, tgt.getElement())) {
- String td = describeTransform(tgt);
- if (td != null)
- tw.keyAssignment(tgt.getContext(), var.property.getPath()+"."+tgt.getElement()+" = "+td);
- }
- }
- }
- Type fixed = generateFixedValue(tgt);
-
- PropertyWithType prop = updateProfile(var, tgt.getElement(), type, map, profiles, sliceName, fixed, tgt);
- if (tgt.hasVariable())
- if (tgt.hasElement())
- vars.add(VariableMode.OUTPUT, tgt.getVariable(), prop);
- else
- vars.add(VariableMode.OUTPUT, tgt.getVariable(), prop);
- }
-
- private Type generateFixedValue(StructureMapGroupRuleTargetComponent tgt) {
- if (!allParametersFixed(tgt))
- return null;
- if (!tgt.hasTransform())
- return null;
- switch (tgt.getTransform()) {
- case COPY: return tgt.getParameter().get(0).getValue();
- case TRUNCATE: return null;
- //case ESCAPE:
- //case CAST:
- //case APPEND:
- case TRANSLATE: return null;
- //case DATEOP,
- //case UUID,
- //case POINTER,
- //case EVALUATE,
- case CC:
- CodeableConcept cc = new CodeableConcept();
- cc.addCoding(buildCoding(tgt.getParameter().get(0).getValue(), tgt.getParameter().get(1).getValue()));
- return cc;
- case C:
- return buildCoding(tgt.getParameter().get(0).getValue(), tgt.getParameter().get(1).getValue());
- case QTY: return null;
- //case ID,
- //case CP,
- default:
- return null;
- }
- }
-
- @SuppressWarnings("rawtypes")
- private Coding buildCoding(Type value1, Type value2) {
- return new Coding().setSystem(((PrimitiveType) value1).asStringValue()).setCode(((PrimitiveType) value2).asStringValue()) ;
- }
-
- private boolean allParametersFixed(StructureMapGroupRuleTargetComponent tgt) {
- for (StructureMapGroupRuleTargetParameterComponent p : tgt.getParameter()) {
- Type pr = p.getValue();
- if (pr instanceof IdType)
- return false;
- }
- return true;
- }
-
- private String describeTransform(StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
- switch (tgt.getTransform()) {
- case COPY: return null;
- case TRUNCATE: return null;
- //case ESCAPE:
- //case CAST:
- //case APPEND:
- case TRANSLATE: return null;
- //case DATEOP,
- //case UUID,
- //case POINTER,
- //case EVALUATE,
- case CC: return describeTransformCCorC(tgt);
- case C: return describeTransformCCorC(tgt);
- case QTY: return null;
- //case ID,
- //case CP,
- default:
- return null;
- }
- }
-
- @SuppressWarnings("rawtypes")
- private String describeTransformCCorC(StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
- if (tgt.getParameter().size() < 2)
- return null;
- Type p1 = tgt.getParameter().get(0).getValue();
- Type p2 = tgt.getParameter().get(1).getValue();
- if (p1 instanceof IdType || p2 instanceof IdType)
- return null;
- if (!(p1 instanceof PrimitiveType) || !(p2 instanceof PrimitiveType))
- return null;
- String uri = ((PrimitiveType) p1).asStringValue();
- String code = ((PrimitiveType) p2).asStringValue();
- if (Utilities.noString(uri))
- throw new FHIRException("Describe Transform, but the uri is blank");
- if (Utilities.noString(code))
- throw new FHIRException("Describe Transform, but the code is blank");
- Coding c = buildCoding(uri, code);
- return NarrativeGenerator.describeSystem(c.getSystem())+"#"+c.getCode()+(c.hasDisplay() ? "("+c.getDisplay()+")" : "");
- }
-
-
- private boolean isSignificantElement(PropertyWithType property, String element) {
- if ("Observation".equals(property.getPath()))
- return "code".equals(element);
- else if ("Bundle".equals(property.getPath()))
- return "type".equals(element);
- else
- return false;
- }
-
- private String getTransformSuffix(StructureMapTransform transform) {
- switch (transform) {
- case COPY: return "";
- case TRUNCATE: return " (truncated)";
- //case ESCAPE:
- //case CAST:
- //case APPEND:
- case TRANSLATE: return " (translated)";
- //case DATEOP,
- //case UUID,
- //case POINTER,
- //case EVALUATE,
- case CC: return " (--> CodeableConcept)";
- case C: return " (--> Coding)";
- case QTY: return " (--> Quantity)";
- //case ID,
- //case CP,
- default:
- return " {??)";
- }
- }
-
- private PropertyWithType updateProfile(VariableForProfiling var, String element, TypeDetails type, StructureMap map, List profiles, String sliceName, Type fixed, StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
- if (var == null) {
- assert (Utilities.noString(element));
- // 1. start the new structure definition
- StructureDefinition sdn = worker.fetchResource(StructureDefinition.class, type.getType());
- if (sdn == null)
- throw new FHIRException("Unable to find definition for "+type.getType());
- ElementDefinition edn = sdn.getSnapshot().getElementFirstRep();
- PropertyWithType pn = createProfile(map, profiles, new PropertyWithType(sdn.getId(), new Property(worker, edn, sdn), null, type), sliceName, tgt);
-
-// // 2. hook it into the base bundle
-// if (type.getType().startsWith("http://hl7.org/fhir/StructureDefinition/") && worker.getResourceNames().contains(type.getType().substring(40))) {
-// StructureDefinition sd = var.getProperty().profileProperty.getStructure();
-// ElementDefinition ed = sd.getDifferential().addElement();
-// ed.setPath("Bundle.entry");
-// ed.setName(sliceName);
-// ed.setMax("1"); // well, it is for now...
-// ed = sd.getDifferential().addElement();
-// ed.setPath("Bundle.entry.fullUrl");
-// ed.setMin(1);
-// ed = sd.getDifferential().addElement();
-// ed.setPath("Bundle.entry.resource");
-// ed.setMin(1);
-// ed.addType().setCode(pn.getProfileProperty().getStructure().getType()).setProfile(pn.getProfileProperty().getStructure().getUrl());
-// }
- return pn;
- } else {
- assert (!Utilities.noString(element));
- Property pvb = var.getProperty().getBaseProperty();
- Property pvd = var.getProperty().getProfileProperty();
- Property pc = pvb.getChild(element, var.property.types);
- if (pc == null)
- throw new DefinitionException("Unable to find a definition for "+pvb.getDefinition().getPath()+"."+element);
-
- // the profile structure definition (derived)
- StructureDefinition sd = var.getProperty().profileProperty.getStructure();
- ElementDefinition ednew = sd.getDifferential().addElement();
- ednew.setPath(var.getProperty().profileProperty.getDefinition().getPath()+"."+pc.getName());
- ednew.setUserData("slice-name", sliceName);
- ednew.setFixed(fixed);
- for (ProfiledType pt : type.getProfiledTypes()) {
- if (pt.hasBindings())
- ednew.setBinding(pt.getBindings().get(0));
- if (pt.getUri().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
- String t = pt.getUri().substring(40);
- t = checkType(t, pc, pt.getProfiles());
- if (t != null) {
- if (pt.hasProfiles()) {
- for (String p : pt.getProfiles())
- if (t.equals("Reference"))
- ednew.addType().setCode(t).setTargetProfile(p);
- else
- ednew.addType().setCode(t).setProfile(p);
- } else
- ednew.addType().setCode(t);
- }
- }
- }
-
- return new PropertyWithType(var.property.path+"."+element, pc, new Property(worker, ednew, sd), type);
- }
- }
-
-
-
- private String checkType(String t, Property pvb, List profiles) throws FHIRException {
- if (pvb.getDefinition().getType().size() == 1 && isCompatibleType(t, pvb.getDefinition().getType().get(0).getCode()) && profilesMatch(profiles, pvb.getDefinition().getType().get(0).getProfile()))
- return null;
- for (TypeRefComponent tr : pvb.getDefinition().getType()) {
- if (isCompatibleType(t, tr.getCode()))
- return tr.getCode(); // note what is returned - the base type, not the inferred mapping type
- }
- throw new FHIRException("The type "+t+" is not compatible with the allowed types for "+pvb.getDefinition().getPath()+" ("+pvb.getDefinition().typeSummary()+")");
- }
-
- private boolean profilesMatch(List profiles, String profile) {
- return profiles == null || profiles.size() == 0 || (profiles.size() == 1 && profiles.get(0).equals(profile));
- }
-
- private boolean isCompatibleType(String t, String code) {
- if (t.equals(code))
- return true;
- if (t.equals("string")) {
- StructureDefinition sd = worker.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code);
- if (sd != null && sd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/string"))
- return true;
- }
- return false;
- }
-
- private TypeDetails analyseTransform(TransformContext context, StructureMap map, StructureMapGroupRuleTargetComponent tgt, VariableForProfiling var, VariablesForProfiling vars) throws FHIRException {
- switch (tgt.getTransform()) {
- case CREATE :
- String p = getParamString(vars, tgt.getParameter().get(0));
- return new TypeDetails(CollectionStatus.SINGLETON, p);
- case COPY :
- return getParam(vars, tgt.getParameter().get(0));
- case EVALUATE :
- ExpressionNode expr = (ExpressionNode) tgt.getUserData(MAP_EXPRESSION);
- if (expr == null) {
- expr = fpe.parse(getParamString(vars, tgt.getParameter().get(tgt.getParameter().size()-1)));
- tgt.setUserData(MAP_WHERE_EXPRESSION, expr);
- }
- return fpe.check(vars, null, expr);
-
-////case TRUNCATE :
-//// String src = getParamString(vars, tgt.getParameter().get(0));
-//// String len = getParamString(vars, tgt.getParameter().get(1));
-//// if (Utilities.isInteger(len)) {
-//// int l = Integer.parseInt(len);
-//// if (src.length() > l)
-//// src = src.substring(0, l);
-//// }
-//// return new StringType(src);
-////case ESCAPE :
-//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
-////case CAST :
-//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
-////case APPEND :
-//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
- case TRANSLATE :
- return new TypeDetails(CollectionStatus.SINGLETON, "CodeableConcept");
- case CC:
- ProfiledType res = new ProfiledType("CodeableConcept");
- if (tgt.getParameter().size() >= 2 && isParamId(vars, tgt.getParameter().get(1))) {
- TypeDetails td = vars.get(null, getParamId(vars, tgt.getParameter().get(1))).property.types;
- if (td != null && td.hasBinding())
- // todo: do we need to check that there's no implicit translation her? I don't think we do...
- res.addBinding(td.getBinding());
- }
- return new TypeDetails(CollectionStatus.SINGLETON, res);
- case C:
- return new TypeDetails(CollectionStatus.SINGLETON, "Coding");
- case QTY:
- return new TypeDetails(CollectionStatus.SINGLETON, "Quantity");
- case REFERENCE :
- VariableForProfiling vrs = vars.get(VariableMode.OUTPUT, getParamId(vars, tgt.getParameterFirstRep()));
- if (vrs == null)
- throw new FHIRException("Unable to resolve variable \""+getParamId(vars, tgt.getParameterFirstRep())+"\"");
- String profile = vrs.property.getProfileProperty().getStructure().getUrl();
- TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON);
- td.addType("Reference", profile);
- return td;
-////case DATEOP :
-//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
-////case UUID :
-//// return new IdType(UUID.randomUUID().toString());
-////case POINTER :
-//// Base b = getParam(vars, tgt.getParameter().get(0));
-//// if (b instanceof Resource)
-//// return new UriType("urn:uuid:"+((Resource) b).getId());
-//// else
-//// throw new FHIRException("Transform engine cannot point at an element of type "+b.fhirType());
- default:
- throw new Error("Transform Unknown or not handled yet: "+tgt.getTransform().toCode());
- }
- }
- private String getParamString(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
- Type p = parameter.getValue();
- if (p == null || p instanceof IdType)
- return null;
- if (!p.hasPrimitiveValue())
- return null;
- return p.primitiveValue();
- }
-
- private String getParamId(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
- Type p = parameter.getValue();
- if (p == null || !(p instanceof IdType))
- return null;
- return p.primitiveValue();
- }
-
- private boolean isParamId(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
- Type p = parameter.getValue();
- if (p == null || !(p instanceof IdType))
- return false;
- return vars.get(null, p.primitiveValue()) != null;
- }
-
- private TypeDetails getParam(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
- Type p = parameter.getValue();
- if (!(p instanceof IdType))
- return new TypeDetails(CollectionStatus.SINGLETON, "http://hl7.org/fhir/StructureDefinition/"+p.fhirType());
- else {
- String n = ((IdType) p).asStringValue();
- VariableForProfiling b = vars.get(VariableMode.INPUT, n);
- if (b == null)
- b = vars.get(VariableMode.OUTPUT, n);
- if (b == null)
- throw new DefinitionException("Variable "+n+" not found ("+vars.summary()+")");
- return b.getProperty().getTypes();
- }
- }
-
- private PropertyWithType createProfile(StructureMap map, List profiles, PropertyWithType prop, String sliceName, Base ctxt) throws DefinitionException {
- if (prop.getBaseProperty().getDefinition().getPath().contains("."))
- throw new DefinitionException("Unable to process entry point");
-
- String type = prop.getBaseProperty().getDefinition().getPath();
- String suffix = "";
- if (ids.containsKey(type)) {
- int id = ids.get(type);
- id++;
- ids.put(type, id);
- suffix = "-"+Integer.toString(id);
- } else
- ids.put(type, 0);
-
- StructureDefinition profile = new StructureDefinition();
- profiles.add(profile);
- profile.setDerivation(TypeDerivationRule.CONSTRAINT);
- profile.setType(type);
- profile.setBaseDefinition(prop.getBaseProperty().getStructure().getUrl());
- profile.setName("Profile for "+profile.getType()+" for "+sliceName);
- profile.setUrl(map.getUrl().replace("StructureMap", "StructureDefinition")+"-"+profile.getType()+suffix);
- ctxt.setUserData("profile", profile.getUrl()); // then we can easily assign this profile url for validation later when we actually transform
- profile.setId(map.getId()+"-"+profile.getType()+suffix);
- profile.setStatus(map.getStatus());
- profile.setExperimental(map.getExperimental());
- profile.setDescription("Generated automatically from the mapping by the Java Reference Implementation");
- for (ContactDetail c : map.getContact()) {
- ContactDetail p = profile.addContact();
- p.setName(c.getName());
- for (ContactPoint cc : c.getTelecom())
- p.addTelecom(cc);
- }
- profile.setDate(map.getDate());
- profile.setCopyright(map.getCopyright());
- profile.setFhirVersion(Constants.VERSION);
- profile.setKind(prop.getBaseProperty().getStructure().getKind());
- profile.setAbstract(false);
- ElementDefinition ed = profile.getDifferential().addElement();
- ed.setPath(profile.getType());
- prop.profileProperty = new Property(worker, ed, profile);
- return prop;
- }
-
- private PropertyWithType resolveType(StructureMap map, String type, StructureMapInputMode mode) throws Exception {
- for (StructureMapStructureComponent imp : map.getStructure()) {
- if ((imp.getMode() == StructureMapModelMode.SOURCE && mode == StructureMapInputMode.SOURCE) ||
- (imp.getMode() == StructureMapModelMode.TARGET && mode == StructureMapInputMode.TARGET)) {
- StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
- if (sd == null)
- throw new Exception("Import "+imp.getUrl()+" cannot be resolved");
- if (sd.getId().equals(type)) {
- return new PropertyWithType(sd.getType(), new Property(worker, sd.getSnapshot().getElement().get(0), sd), null, new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()));
- }
- }
- }
- throw new Exception("Unable to find structure definition for "+type+" in imports");
- }
-
-
- public StructureMap generateMapFromMappings(StructureDefinition sd) throws IOException, FHIRException {
- String id = getLogicalMappingId(sd);
- if (id == null)
- return null;
- String prefix = ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_MAPPING_PREFIX);
- String suffix = ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_MAPPING_SUFFIX);
- if (prefix == null || suffix == null)
- return null;
- // we build this by text. Any element that has a mapping, we put it's mappings inside it....
- StringBuilder b = new StringBuilder();
- b.append(prefix);
-
- ElementDefinition root = sd.getSnapshot().getElementFirstRep();
- String m = getMapping(root, id);
- if (m != null)
- b.append(m+"\r\n");
- addChildMappings(b, id, "", sd, root, false);
- b.append("\r\n");
- b.append(suffix);
- b.append("\r\n");
- StructureMap map = parse(b.toString());
- map.setId(tail(map.getUrl()));
- if (!map.hasStatus())
- map.setStatus(PublicationStatus.DRAFT);
- map.getText().setStatus(NarrativeStatus.GENERATED);
- map.getText().setDiv(new XhtmlNode(NodeType.Element, "div"));
- map.getText().getDiv().addTag("pre").addText(render(map));
- return map;
- }
-
-
- private String tail(String url) {
- return url.substring(url.lastIndexOf("/")+1);
- }
-
-
- private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException {
- boolean first = true;
- List children = ProfileUtilities.getChildMap(sd, ed);
- for (ElementDefinition child : children) {
- if (first && inner) {
- b.append(" then {\r\n");
- first = false;
- }
- String map = getMapping(child, id);
- if (map != null) {
- b.append(indent+" "+child.getPath()+": "+map);
- addChildMappings(b, id, indent+" ", sd, child, true);
- b.append("\r\n");
- }
- }
- if (!first && inner)
- b.append(indent+"}");
-
- }
-
-
- private String getMapping(ElementDefinition ed, String id) {
- for (ElementDefinitionMappingComponent map : ed.getMapping())
- if (id.equals(map.getIdentity()))
- return map.getMap();
- return null;
- }
-
-
- private String getLogicalMappingId(StructureDefinition sd) {
- String id = null;
- for (StructureDefinitionMappingComponent map : sd.getMapping()) {
- if ("http://hl7.org/fhir/logical".equals(map.getUri()))
- return map.getIdentity();
- }
- return null;
- }
-
-}
+package org.hl7.fhir.dstu3.utils;
+
+// remember group resolution
+// trace - account for which wasn't transformed in the source
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
+import org.hl7.fhir.dstu3.conformance.ProfileUtilities.ProfileKnowledgeProvider;
+import org.hl7.fhir.dstu3.context.IWorkerContext;
+import org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult;
+import org.hl7.fhir.dstu3.elementmodel.Element;
+import org.hl7.fhir.dstu3.elementmodel.Property;
+import org.hl7.fhir.dstu3.model.Base;
+import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.CodeType;
+import org.hl7.fhir.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.ConceptMap;
+import org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupComponent;
+import org.hl7.fhir.dstu3.model.ConceptMap.ConceptMapGroupUnmappedMode;
+import org.hl7.fhir.dstu3.model.ConceptMap.SourceElementComponent;
+import org.hl7.fhir.dstu3.model.ConceptMap.TargetElementComponent;
+import org.hl7.fhir.dstu3.model.Constants;
+import org.hl7.fhir.dstu3.model.ContactDetail;
+import org.hl7.fhir.dstu3.model.ContactPoint;
+import org.hl7.fhir.dstu3.model.DecimalType;
+import org.hl7.fhir.dstu3.model.ElementDefinition;
+import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionMappingComponent;
+import org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent;
+import org.hl7.fhir.dstu3.model.Enumeration;
+import org.hl7.fhir.dstu3.model.Enumerations.ConceptMapEquivalence;
+import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
+import org.hl7.fhir.dstu3.model.ExpressionNode;
+import org.hl7.fhir.dstu3.model.ExpressionNode.CollectionStatus;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.IntegerType;
+import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
+import org.hl7.fhir.dstu3.model.PrimitiveType;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.Resource;
+import org.hl7.fhir.dstu3.model.ResourceFactory;
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.dstu3.model.StructureDefinition;
+import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionMappingComponent;
+import org.hl7.fhir.dstu3.model.StructureDefinition.TypeDerivationRule;
+import org.hl7.fhir.dstu3.model.StructureMap;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapContextType;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupInputComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleDependentComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleSourceComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleTargetComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupRuleTargetParameterComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapGroupTypeMode;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapInputMode;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapSourceListMode;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapTargetListMode;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapModelMode;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapStructureComponent;
+import org.hl7.fhir.dstu3.model.StructureMap.StructureMapTransform;
+import org.hl7.fhir.dstu3.model.Type;
+import org.hl7.fhir.dstu3.model.TypeDetails;
+import org.hl7.fhir.dstu3.model.TypeDetails.ProfiledType;
+import org.hl7.fhir.dstu3.model.UriType;
+import org.hl7.fhir.dstu3.model.ValueSet;
+import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
+import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
+import org.hl7.fhir.dstu3.utils.FHIRLexer.FHIRLexerException;
+import org.hl7.fhir.dstu3.utils.FHIRPathEngine.IEvaluationContext;
+import org.hl7.fhir.exceptions.DefinitionException;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.exceptions.FHIRFormatError;
+import org.hl7.fhir.exceptions.PathEngineException;
+import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
+import org.hl7.fhir.utilities.TextFile;
+import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.xhtml.NodeType;
+import org.hl7.fhir.utilities.xhtml.XhtmlNode;
+
+/**
+ * Services in this class:
+ *
+ * string render(map) - take a structure and convert it to text
+ * map parse(text) - take a text representation and parse it
+ * getTargetType(map) - return the definition for the type to create to hand in
+ * transform(appInfo, source, map, target) - transform from source to target following the map
+ * analyse(appInfo, map) - generate profiles and other analysis artifacts for the targets of the transform
+ * map generateMapFromMappings(StructureDefinition) - build a mapping from a structure definition with loigcal mappings
+ *
+ * @author Grahame Grieve
+ *
+ */
+public class StructureMapUtilities {
+
+ public class ResolvedGroup {
+ public StructureMapGroupComponent target;
+ public StructureMap targetMap;
+ }
+ public static final String MAP_WHERE_CHECK = "map.where.check";
+ public static final String MAP_WHERE_EXPRESSION = "map.where.expression";
+ public static final String MAP_SEARCH_EXPRESSION = "map.search.expression";
+ public static final String MAP_EXPRESSION = "map.transform.expression";
+ private static final boolean RENDER_MULTIPLE_TARGETS_ONELINE = true;
+ private static final String AUTO_VAR_NAME = "vvv";
+
+ public interface ITransformerServices {
+ // public boolean validateByValueSet(Coding code, String valuesetId);
+ public void log(String message); // log internal progress
+ public Base createType(Object appInfo, String name) throws FHIRException;
+ public Base createResource(Object appInfo, Base res); // an already created resource is provided; this is to identify/store it
+ public Coding translate(Object appInfo, Coding source, String conceptMapUrl) throws FHIRException;
+ // public Coding translate(Coding code)
+ // ValueSet validation operation
+ // Translation operation
+ // Lookup another tree of data
+ // Create an instance tree
+ // Return the correct string format to refer to a tree (input or output)
+ public Base resolveReference(Object appContext, String url);
+ public List performSearch(Object appContext, String url);
+ }
+
+ private class FFHIRPathHostServices implements IEvaluationContext{
+
+ public Base resolveConstant(Object appContext, String name) throws PathEngineException {
+ Variables vars = (Variables) appContext;
+ Base res = vars.get(VariableMode.INPUT, name);
+ if (res == null)
+ res = vars.get(VariableMode.OUTPUT, name);
+ return res;
+ }
+
+ @Override
+ public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
+ if (!(appContext instanceof VariablesForProfiling))
+ throw new Error("Internal Logic Error (wrong type '"+appContext.getClass().getName()+"' in resolveConstantType)");
+ VariablesForProfiling vars = (VariablesForProfiling) appContext;
+ VariableForProfiling v = vars.get(null, name);
+ if (v == null)
+ throw new PathEngineException("Unknown variable '"+name+"' from variables "+vars.summary());
+ return v.property.types;
+ }
+
+ @Override
+ public boolean log(String argument, List focus) {
+ throw new Error("Not Implemented Yet");
+ }
+
+ @Override
+ public FunctionDetails resolveFunction(String functionName) {
+ return null; // throw new Error("Not Implemented Yet");
+ }
+
+ @Override
+ public TypeDetails checkFunction(Object appContext, String functionName, List parameters) throws PathEngineException {
+ throw new Error("Not Implemented Yet");
+ }
+
+ @Override
+ public List executeFunction(Object appContext, String functionName, List> parameters) {
+ throw new Error("Not Implemented Yet");
+ }
+
+ @Override
+ public Base resolveReference(Object appContext, String url) {
+ if (services == null)
+ return null;
+ return services.resolveReference(appContext, url);
+ }
+
+ }
+ private IWorkerContext worker;
+ private FHIRPathEngine fpe;
+ private Map library;
+ private ITransformerServices services;
+ private ProfileKnowledgeProvider pkp;
+ private Map ids = new HashMap();
+
+ public StructureMapUtilities(IWorkerContext worker, Map library, ITransformerServices services, ProfileKnowledgeProvider pkp) {
+ super();
+ this.worker = worker;
+ this.library = library;
+ this.services = services;
+ this.pkp = pkp;
+ fpe = new FHIRPathEngine(worker);
+ fpe.setHostServices(new FFHIRPathHostServices());
+ }
+
+ public StructureMapUtilities(IWorkerContext worker, Map library, ITransformerServices services) {
+ super();
+ this.worker = worker;
+ this.library = library;
+ this.services = services;
+ fpe = new FHIRPathEngine(worker);
+ fpe.setHostServices(new FFHIRPathHostServices());
+ }
+
+ public StructureMapUtilities(IWorkerContext worker, Map library) {
+ super();
+ this.worker = worker;
+ this.library = library;
+ fpe = new FHIRPathEngine(worker);
+ fpe.setHostServices(new FFHIRPathHostServices());
+ }
+
+ public StructureMapUtilities(IWorkerContext worker) {
+ super();
+ this.worker = worker;
+ fpe = new FHIRPathEngine(worker);
+ fpe.setHostServices(new FFHIRPathHostServices());
+ }
+
+ public StructureMapUtilities(IWorkerContext worker, ITransformerServices services) {
+ super();
+ this.worker = worker;
+ this.library = new HashMap();
+ for (org.hl7.fhir.dstu3.model.MetadataResource bc : worker.allConformanceResources()) {
+ if (bc instanceof StructureMap)
+ library.put(bc.getUrl(), (StructureMap) bc);
+ }
+ this.services = services;
+ fpe = new FHIRPathEngine(worker);
+ fpe.setHostServices(new FFHIRPathHostServices());
+ }
+
+ public static String render(StructureMap map) {
+ StringBuilder b = new StringBuilder();
+ b.append("map \"");
+ b.append(map.getUrl());
+ b.append("\" = \"");
+ b.append(Utilities.escapeJava(map.getName()));
+ b.append("\"\r\n\r\n");
+
+ renderConceptMaps(b, map);
+ renderUses(b, map);
+ renderImports(b, map);
+ for (StructureMapGroupComponent g : map.getGroup())
+ renderGroup(b, g);
+ return b.toString();
+ }
+
+ private static void renderConceptMaps(StringBuilder b, StructureMap map) {
+ for (Resource r : map.getContained()) {
+ if (r instanceof ConceptMap) {
+ produceConceptMap(b, (ConceptMap) r);
+ }
+ }
+ }
+
+ private static void produceConceptMap(StringBuilder b, ConceptMap cm) {
+ b.append("conceptmap \"");
+ b.append(cm.getId());
+ b.append("\" {\r\n");
+ Map prefixesSrc = new HashMap();
+ Map prefixesTgt = new HashMap();
+ char prefix = 's';
+ for (ConceptMapGroupComponent cg : cm.getGroup()) {
+ if (!prefixesSrc.containsKey(cg.getSource())) {
+ prefixesSrc.put(cg.getSource(), String.valueOf(prefix));
+ b.append(" prefix ");
+ b.append(prefix);
+ b.append(" = \"");
+ b.append(cg.getSource());
+ b.append("\"\r\n");
+ prefix++;
+ }
+ if (!prefixesTgt.containsKey(cg.getTarget())) {
+ prefixesTgt.put(cg.getTarget(), String.valueOf(prefix));
+ b.append(" prefix ");
+ b.append(prefix);
+ b.append(" = \"");
+ b.append(cg.getTarget());
+ b.append("\"\r\n");
+ prefix++;
+ }
+ }
+ b.append("\r\n");
+ for (ConceptMapGroupComponent cg : cm.getGroup()) {
+ if (cg.hasUnmapped()) {
+ b.append(" unmapped for ");
+ b.append(prefix);
+ b.append(" = ");
+ b.append(cg.getUnmapped().getMode());
+ b.append("\r\n");
+ }
+ }
+
+ for (ConceptMapGroupComponent cg : cm.getGroup()) {
+ for (SourceElementComponent ce : cg.getElement()) {
+ b.append(" ");
+ b.append(prefixesSrc.get(cg.getSource()));
+ b.append(":");
+ b.append(ce.getCode());
+ b.append(" ");
+ b.append(getChar(ce.getTargetFirstRep().getEquivalence()));
+ b.append(" ");
+ b.append(prefixesTgt.get(cg.getTarget()));
+ b.append(":");
+ b.append(ce.getTargetFirstRep().getCode());
+ b.append("\r\n");
+ }
+ }
+ b.append("}\r\n\r\n");
+ }
+
+ private static Object getChar(ConceptMapEquivalence equivalence) {
+ switch (equivalence) {
+ case RELATEDTO: return "-";
+ case EQUAL: return "=";
+ case EQUIVALENT: return "==";
+ case DISJOINT: return "!=";
+ case UNMATCHED: return "--";
+ case WIDER: return "<=";
+ case SUBSUMES: return "<-";
+ case NARROWER: return ">=";
+ case SPECIALIZES: return ">-";
+ case INEXACT: return "~";
+ default: return "??";
+ }
+ }
+
+ private static void renderUses(StringBuilder b, StructureMap map) {
+ for (StructureMapStructureComponent s : map.getStructure()) {
+ b.append("uses \"");
+ b.append(s.getUrl());
+ b.append("\" ");
+ if (s.hasAlias()) {
+ b.append("alias ");
+ b.append(s.getAlias());
+ b.append(" ");
+ }
+ b.append("as ");
+ b.append(s.getMode().toCode());
+ b.append("\r\n");
+ renderDoco(b, s.getDocumentation());
+ }
+ if (map.hasStructure())
+ b.append("\r\n");
+ }
+
+ private static void renderImports(StringBuilder b, StructureMap map) {
+ for (UriType s : map.getImport()) {
+ b.append("imports \"");
+ b.append(s.getValue());
+ b.append("\"\r\n");
+ }
+ if (map.hasImport())
+ b.append("\r\n");
+ }
+
+ public static String groupToString(StructureMapGroupComponent g) {
+ StringBuilder b = new StringBuilder();
+ renderGroup(b, g);
+ return b.toString();
+ }
+
+ private static void renderGroup(StringBuilder b, StructureMapGroupComponent g) {
+ b.append("group ");
+ switch (g.getTypeMode()) {
+ case TYPES:
+ b.append("for types");
+ break;
+ case TYPEANDTYPES:
+ b.append("for type+types ");
+ break;
+ case NONE:
+ case NULL:
+ break;
+ }
+ b.append("for types ");
+ b.append(g.getName());
+ if (g.hasExtends()) {
+ b.append(" extends ");
+ b.append(g.getExtends());
+ }
+ if (g.hasDocumentation())
+ renderDoco(b, g.getDocumentation());
+ b.append("\r\n");
+ for (StructureMapGroupInputComponent gi : g.getInput()) {
+ b.append(" input ");
+ b.append(gi.getName());
+ if (gi.hasType()) {
+ b.append(" : ");
+ b.append(gi.getType());
+ }
+ b.append(" as ");
+ b.append(gi.getMode().toCode());
+ b.append("\r\n");
+ }
+ if (g.hasInput())
+ b.append("\r\n");
+ for (StructureMapGroupRuleComponent r : g.getRule()) {
+ renderRule(b, r, 2);
+ }
+ b.append("\r\nendgroup\r\n");
+ }
+
+ public static String ruleToString(StructureMapGroupRuleComponent r) {
+ StringBuilder b = new StringBuilder();
+ renderRule(b, r, 0);
+ return b.toString();
+ }
+
+ private static void renderRule(StringBuilder b, StructureMapGroupRuleComponent r, int indent) {
+ for (int i = 0; i < indent; i++)
+ b.append(' ');
+ b.append(r.getName());
+ b.append(" : for ");
+ boolean canBeAbbreviated = checkisSimple(r);
+
+ boolean first = true;
+ for (StructureMapGroupRuleSourceComponent rs : r.getSource()) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+ renderSource(b, rs, canBeAbbreviated);
+ }
+ if (r.getTarget().size() > 1) {
+ b.append(" make ");
+ first = true;
+ for (StructureMapGroupRuleTargetComponent rt : r.getTarget()) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+ if (RENDER_MULTIPLE_TARGETS_ONELINE)
+ b.append(' ');
+ else {
+ b.append("\r\n");
+ for (int i = 0; i < indent+4; i++)
+ b.append(' ');
+ }
+ renderTarget(b, rt, false);
+ }
+ } else if (r.hasTarget()) {
+ b.append(" make ");
+ renderTarget(b, r.getTarget().get(0), canBeAbbreviated);
+ }
+ if (!canBeAbbreviated) {
+ if (r.hasRule()) {
+ b.append(" then {\r\n");
+ renderDoco(b, r.getDocumentation());
+ for (StructureMapGroupRuleComponent ir : r.getRule()) {
+ renderRule(b, ir, indent+2);
+ }
+ for (int i = 0; i < indent; i++)
+ b.append(' ');
+ b.append("}\r\n");
+ } else {
+ if (r.hasDependent()) {
+ b.append(" then ");
+ first = true;
+ for (StructureMapGroupRuleDependentComponent rd : r.getDependent()) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+ b.append(rd.getName());
+ b.append("(");
+ boolean ifirst = true;
+ for (StringType rdp : rd.getVariable()) {
+ if (ifirst)
+ ifirst = false;
+ else
+ b.append(", ");
+ b.append(rdp.asStringValue());
+ }
+ b.append(")");
+ }
+ }
+ }
+ }
+ renderDoco(b, r.getDocumentation());
+ b.append("\r\n");
+ }
+
+ private static boolean checkisSimple(StructureMapGroupRuleComponent r) {
+ return
+ (r.getSource().size() == 1 && r.getSourceFirstRep().hasElement() && r.getSourceFirstRep().hasVariable()) &&
+ (r.getTarget().size() == 1 && r.getTargetFirstRep().hasVariable() && (r.getTargetFirstRep().getTransform() == null || r.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE) && r.getTargetFirstRep().getParameter().size() == 0) &&
+ (r.getDependent().size() == 0);
+ }
+
+ public static String sourceToString(StructureMapGroupRuleSourceComponent r) {
+ StringBuilder b = new StringBuilder();
+ renderSource(b, r, false);
+ return b.toString();
+ }
+
+ private static void renderSource(StringBuilder b, StructureMapGroupRuleSourceComponent rs, boolean abbreviate) {
+ b.append(rs.getContext());
+ if (rs.getContext().equals("@search")) {
+ b.append('(');
+ b.append(rs.getElement());
+ b.append(')');
+ } else if (rs.hasElement()) {
+ b.append('.');
+ b.append(rs.getElement());
+ }
+ if (rs.hasType()) {
+ b.append(" : ");
+ b.append(rs.getType());
+ if (rs.hasMin()) {
+ b.append(" ");
+ b.append(rs.getMin());
+ b.append("..");
+ b.append(rs.getMax());
+ }
+ }
+
+ if (rs.hasListMode()) {
+ b.append(" ");
+ b.append(rs.getListMode().toCode());
+ }
+ if (rs.hasDefaultValue()) {
+ b.append(" default ");
+ assert rs.getDefaultValue() instanceof StringType;
+ b.append("\""+Utilities.escapeJson(((StringType) rs.getDefaultValue()).asStringValue())+"\"");
+ }
+ if (!abbreviate && rs.hasVariable()) {
+ b.append(" as ");
+ b.append(rs.getVariable());
+ }
+ if (rs.hasCondition()) {
+ b.append(" where ");
+ b.append(rs.getCondition());
+ }
+ if (rs.hasCheck()) {
+ b.append(" check ");
+ b.append(rs.getCheck());
+ }
+ }
+
+ public static String targetToString(StructureMapGroupRuleTargetComponent rt) {
+ StringBuilder b = new StringBuilder();
+ renderTarget(b, rt, false);
+ return b.toString();
+ }
+
+ private static void renderTarget(StringBuilder b, StructureMapGroupRuleTargetComponent rt, boolean abbreviate) {
+ if (rt.hasContext()) {
+ if (rt.getContextType() == StructureMapContextType.TYPE)
+ b.append("@");
+ b.append(rt.getContext());
+ if (rt.hasElement()) {
+ b.append('.');
+ b.append(rt.getElement());
+ }
+ }
+ if (!abbreviate && rt.hasTransform()) {
+ if (rt.hasContext())
+ b.append(" = ");
+ if (rt.getTransform() == StructureMapTransform.COPY && rt.getParameter().size() == 1) {
+ renderTransformParam(b, rt.getParameter().get(0));
+ } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 1) {
+ b.append("(");
+ b.append("\""+((StringType) rt.getParameter().get(0).getValue()).asStringValue()+"\"");
+ b.append(")");
+ } else if (rt.getTransform() == StructureMapTransform.EVALUATE && rt.getParameter().size() == 2) {
+ b.append(rt.getTransform().toCode());
+ b.append("(");
+ b.append(((IdType) rt.getParameter().get(0).getValue()).asStringValue());
+ b.append("\""+((StringType) rt.getParameter().get(1).getValue()).asStringValue()+"\"");
+ b.append(")");
+ } else {
+ b.append(rt.getTransform().toCode());
+ b.append("(");
+ boolean first = true;
+ for (StructureMapGroupRuleTargetParameterComponent rtp : rt.getParameter()) {
+ if (first)
+ first = false;
+ else
+ b.append(", ");
+ renderTransformParam(b, rtp);
+ }
+ b.append(")");
+ }
+ }
+ if (!abbreviate && rt.hasVariable()) {
+ b.append(" as ");
+ b.append(rt.getVariable());
+ }
+ for (Enumeration lm : rt.getListMode()) {
+ b.append(" ");
+ b.append(lm.getValue().toCode());
+ if (lm.getValue() == StructureMapTargetListMode.SHARE) {
+ b.append(" ");
+ b.append(rt.getListRuleId());
+ }
+ }
+ }
+
+ public static String paramToString(StructureMapGroupRuleTargetParameterComponent rtp) {
+ StringBuilder b = new StringBuilder();
+ renderTransformParam(b, rtp);
+ return b.toString();
+ }
+
+ private static void renderTransformParam(StringBuilder b, StructureMapGroupRuleTargetParameterComponent rtp) {
+ try {
+ if (rtp.hasValueBooleanType())
+ b.append(rtp.getValueBooleanType().asStringValue());
+ else if (rtp.hasValueDecimalType())
+ b.append(rtp.getValueDecimalType().asStringValue());
+ else if (rtp.hasValueIdType())
+ b.append(rtp.getValueIdType().asStringValue());
+ else if (rtp.hasValueDecimalType())
+ b.append(rtp.getValueDecimalType().asStringValue());
+ else if (rtp.hasValueIntegerType())
+ b.append(rtp.getValueIntegerType().asStringValue());
+ else
+ b.append("\""+Utilities.escapeJava(rtp.getValueStringType().asStringValue())+"\"");
+ } catch (FHIRException e) {
+ e.printStackTrace();
+ b.append("error!");
+ }
+ }
+
+ private static void renderDoco(StringBuilder b, String doco) {
+ if (Utilities.noString(doco))
+ return;
+ b.append(" // ");
+ b.append(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " "));
+ }
+
+ public StructureMap parse(String text) throws FHIRException {
+ FHIRLexer lexer = new FHIRLexer(text);
+ if (lexer.done())
+ throw lexer.error("Map Input cannot be empty");
+ lexer.skipComments();
+ lexer.token("map");
+ StructureMap result = new StructureMap();
+ result.setUrl(lexer.readConstant("url"));
+ lexer.token("=");
+ result.setName(lexer.readConstant("name"));
+ lexer.skipComments();
+
+ while (lexer.hasToken("conceptmap"))
+ parseConceptMap(result, lexer);
+
+ while (lexer.hasToken("uses"))
+ parseUses(result, lexer);
+ while (lexer.hasToken("imports"))
+ parseImports(result, lexer);
+
+ parseGroup(result, lexer);
+
+ while (!lexer.done()) {
+ parseGroup(result, lexer);
+ }
+
+ return result;
+ }
+
+ private void parseConceptMap(StructureMap result, FHIRLexer lexer) throws FHIRLexerException {
+ lexer.token("conceptmap");
+ ConceptMap map = new ConceptMap();
+ String id = lexer.readConstant("map id");
+ if (!id.startsWith("#"))
+ lexer.error("Concept Map identifier must start with #");
+ map.setId(id);
+ map.setStatus(PublicationStatus.DRAFT); // todo: how to add this to the text format
+ result.getContained().add(map);
+ lexer.token("{");
+ lexer.skipComments();
+ // lexer.token("source");
+ // map.setSource(new UriType(lexer.readConstant("source")));
+ // lexer.token("target");
+ // map.setSource(new UriType(lexer.readConstant("target")));
+ Map prefixes = new HashMap();
+ while (lexer.hasToken("prefix")) {
+ lexer.token("prefix");
+ String n = lexer.take();
+ lexer.token("=");
+ String v = lexer.readConstant("prefix url");
+ prefixes.put(n, v);
+ }
+ while (lexer.hasToken("unmapped")) {
+ lexer.token("unmapped");
+ lexer.token("for");
+ String n = readPrefix(prefixes, lexer);
+ ConceptMapGroupComponent g = getGroup(map, n, null);
+ lexer.token("=");
+ String v = lexer.take();
+ if (v.equals("provided")) {
+ g.getUnmapped().setMode(ConceptMapGroupUnmappedMode.PROVIDED);
+ } else
+ lexer.error("Only unmapped mode PROVIDED is supported at this time");
+ }
+ while (!lexer.hasToken("}")) {
+ String srcs = readPrefix(prefixes, lexer);
+ lexer.token(":");
+ String sc = lexer.getCurrent().startsWith("\"") ? lexer.readConstant("code") : lexer.take();
+ ConceptMapEquivalence eq = readEquivalence(lexer);
+ String tgts = (eq != ConceptMapEquivalence.UNMATCHED) ? readPrefix(prefixes, lexer) : "";
+ ConceptMapGroupComponent g = getGroup(map, srcs, tgts);
+ SourceElementComponent e = g.addElement();
+ e.setCode(sc);
+ if (e.getCode().startsWith("\""))
+ e.setCode(lexer.processConstant(e.getCode()));
+ TargetElementComponent tgt = e.addTarget();
+ if (eq != ConceptMapEquivalence.EQUIVALENT)
+ tgt.setEquivalence(eq);
+ if (tgt.getEquivalence() != ConceptMapEquivalence.UNMATCHED) {
+ lexer.token(":");
+ tgt.setCode(lexer.take());
+ if (tgt.getCode().startsWith("\""))
+ tgt.setCode(lexer.processConstant(tgt.getCode()));
+ }
+ if (lexer.hasComment())
+ tgt.setComment(lexer.take().substring(2).trim());
+ }
+ lexer.token("}");
+ }
+
+
+ private ConceptMapGroupComponent getGroup(ConceptMap map, String srcs, String tgts) {
+ for (ConceptMapGroupComponent grp : map.getGroup()) {
+ if (grp.getSource().equals(srcs))
+ if ((tgts == null && !grp.hasTarget()) || (tgts != null && tgts.equals(grp.getTarget())))
+ return grp;
+ }
+ ConceptMapGroupComponent grp = map.addGroup();
+ grp.setSource(srcs);
+ grp.setTarget(tgts);
+ return grp;
+ }
+
+
+ private String readPrefix(Map prefixes, FHIRLexer lexer) throws FHIRLexerException {
+ String prefix = lexer.take();
+ if (!prefixes.containsKey(prefix))
+ throw lexer.error("Unknown prefix '"+prefix+"'");
+ return prefixes.get(prefix);
+ }
+
+
+ private ConceptMapEquivalence readEquivalence(FHIRLexer lexer) throws FHIRLexerException {
+ String token = lexer.take();
+ if (token.equals("-"))
+ return ConceptMapEquivalence.RELATEDTO;
+ if (token.equals("="))
+ return ConceptMapEquivalence.EQUAL;
+ if (token.equals("=="))
+ return ConceptMapEquivalence.EQUIVALENT;
+ if (token.equals("!="))
+ return ConceptMapEquivalence.DISJOINT;
+ if (token.equals("--"))
+ return ConceptMapEquivalence.UNMATCHED;
+ if (token.equals("<="))
+ return ConceptMapEquivalence.WIDER;
+ if (token.equals("<-"))
+ return ConceptMapEquivalence.SUBSUMES;
+ if (token.equals(">="))
+ return ConceptMapEquivalence.NARROWER;
+ if (token.equals(">-"))
+ return ConceptMapEquivalence.SPECIALIZES;
+ if (token.equals("~"))
+ return ConceptMapEquivalence.INEXACT;
+ throw lexer.error("Unknown equivalence token '"+token+"'");
+ }
+
+
+ private void parseUses(StructureMap result, FHIRLexer lexer) throws FHIRException {
+ lexer.token("uses");
+ StructureMapStructureComponent st = result.addStructure();
+ st.setUrl(lexer.readConstant("url"));
+ if (lexer.hasToken("alias")) {
+ lexer.token("alias");
+ st.setAlias(lexer.take());
+ }
+ lexer.token("as");
+ st.setMode(StructureMapModelMode.fromCode(lexer.take()));
+ lexer.skipToken(";");
+ if (lexer.hasComment()) {
+ st.setDocumentation(lexer.take().substring(2).trim());
+ }
+ lexer.skipComments();
+ }
+
+ private void parseImports(StructureMap result, FHIRLexer lexer) throws FHIRException {
+ lexer.token("imports");
+ result.addImport(lexer.readConstant("url"));
+ lexer.skipToken(";");
+ if (lexer.hasComment()) {
+ lexer.next();
+ }
+ lexer.skipComments();
+ }
+
+ private void parseGroup(StructureMap result, FHIRLexer lexer) throws FHIRException {
+ lexer.token("group");
+ StructureMapGroupComponent group = result.addGroup();
+ if (lexer.hasToken("for")) {
+ lexer.token("for");
+ if ("type".equals(lexer.getCurrent())) {
+ lexer.token("type");
+ lexer.token("+");
+ lexer.token("types");
+ group.setTypeMode(StructureMapGroupTypeMode.TYPEANDTYPES);
+ } else {
+ lexer.token("types");
+ group.setTypeMode(StructureMapGroupTypeMode.TYPES);
+ }
+ } else
+ group.setTypeMode(StructureMapGroupTypeMode.NONE);
+ group.setName(lexer.take());
+ if (lexer.hasToken("extends")) {
+ lexer.next();
+ group.setExtends(lexer.take());
+ }
+ lexer.skipComments();
+ while (lexer.hasToken("input"))
+ parseInput(group, lexer);
+ while (!lexer.hasToken("endgroup")) {
+ if (lexer.done())
+ throw lexer.error("premature termination expecting 'endgroup'");
+ parseRule(result, group.getRule(), lexer);
+ }
+ lexer.next();
+ lexer.skipComments();
+ }
+
+ private void parseInput(StructureMapGroupComponent group, FHIRLexer lexer) throws FHIRException {
+ lexer.token("input");
+ StructureMapGroupInputComponent input = group.addInput();
+ input.setName(lexer.take());
+ if (lexer.hasToken(":")) {
+ lexer.token(":");
+ input.setType(lexer.take());
+ }
+ lexer.token("as");
+ input.setMode(StructureMapInputMode.fromCode(lexer.take()));
+ if (lexer.hasComment()) {
+ input.setDocumentation(lexer.take().substring(2).trim());
+ }
+ lexer.skipToken(";");
+ lexer.skipComments();
+ }
+
+ private void parseRule(StructureMap map, List list, FHIRLexer lexer) throws FHIRException {
+ StructureMapGroupRuleComponent rule = new StructureMapGroupRuleComponent();
+ list.add(rule);
+ rule.setName(lexer.takeDottedToken());
+ lexer.token(":");
+ lexer.token("for");
+ boolean done = false;
+ while (!done) {
+ parseSource(rule, lexer);
+ done = !lexer.hasToken(",");
+ if (!done)
+ lexer.next();
+ }
+ if (lexer.hasToken("make")) {
+ lexer.token("make");
+ done = false;
+ while (!done) {
+ parseTarget(rule, lexer);
+ done = !lexer.hasToken(",");
+ if (!done)
+ lexer.next();
+ }
+ }
+ if (lexer.hasToken("then")) {
+ lexer.token("then");
+ if (lexer.hasToken("{")) {
+ lexer.token("{");
+ if (lexer.hasComment()) {
+ rule.setDocumentation(lexer.take().substring(2).trim());
+ }
+ lexer.skipComments();
+ while (!lexer.hasToken("}")) {
+ if (lexer.done())
+ throw lexer.error("premature termination expecting '}' in nested group");
+ parseRule(map, rule.getRule(), lexer);
+ }
+ lexer.token("}");
+ } else {
+ done = false;
+ while (!done) {
+ parseRuleReference(rule, lexer);
+ done = !lexer.hasToken(",");
+ if (!done)
+ lexer.next();
+ }
+ }
+ } else if (lexer.hasComment()) {
+ rule.setDocumentation(lexer.take().substring(2).trim());
+ }
+ if (isSimpleSyntax(rule)) {
+ rule.getSourceFirstRep().setVariable(AUTO_VAR_NAME);
+ rule.getTargetFirstRep().setVariable(AUTO_VAR_NAME);
+ rule.getTargetFirstRep().setTransform(StructureMapTransform.CREATE); // with no parameter - e.g. imply what is to be created
+ // no dependencies - imply what is to be done based on types
+ }
+ lexer.skipComments();
+ }
+
+ private boolean isSimpleSyntax(StructureMapGroupRuleComponent rule) {
+ return
+ (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasContext() && rule.getSourceFirstRep().hasElement() && !rule.getSourceFirstRep().hasVariable()) &&
+ (rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasContext() && rule.getTargetFirstRep().hasElement() && !rule.getTargetFirstRep().hasVariable() && !rule.getTargetFirstRep().hasParameter()) &&
+ (rule.getDependent().size() == 0 && rule.getRule().size() == 0);
+ }
+
+ private void parseRuleReference(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRLexerException {
+ StructureMapGroupRuleDependentComponent ref = rule.addDependent();
+ ref.setName(lexer.take());
+ lexer.token("(");
+ boolean done = false;
+ while (!done) {
+ ref.addVariable(lexer.take());
+ done = !lexer.hasToken(",");
+ if (!done)
+ lexer.next();
+ }
+ lexer.token(")");
+ }
+
+ private void parseSource(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
+ StructureMapGroupRuleSourceComponent source = rule.addSource();
+ source.setContext(lexer.take());
+ if (source.getContext().equals("search") && lexer.hasToken("(")) {
+ source.setContext("@search");
+ lexer.take();
+ ExpressionNode node = fpe.parse(lexer);
+ source.setUserData(MAP_SEARCH_EXPRESSION, node);
+ source.setElement(node.toString());
+ lexer.token(")");
+ } else if (lexer.hasToken(".")) {
+ lexer.token(".");
+ source.setElement(lexer.take());
+ }
+ if (lexer.hasToken(":")) {
+ // type and cardinality
+ lexer.token(":");
+ source.setType(lexer.takeDottedToken());
+ if (!lexer.hasToken("as", "first", "last", "not_first", "not_last", "only_one", "default")) {
+ source.setMin(lexer.takeInt());
+ lexer.token("..");
+ source.setMax(lexer.take());
+ }
+ }
+ if (lexer.hasToken("default")) {
+ lexer.token("default");
+ source.setDefaultValue(new StringType(lexer.readConstant("default value")));
+ }
+ if (Utilities.existsInList(lexer.getCurrent(), "first", "last", "not_first", "not_last", "only_one"))
+ source.setListMode(StructureMapSourceListMode.fromCode(lexer.take()));
+
+ if (lexer.hasToken("as")) {
+ lexer.take();
+ source.setVariable(lexer.take());
+ }
+ if (lexer.hasToken("where")) {
+ lexer.take();
+ ExpressionNode node = fpe.parse(lexer);
+ source.setUserData(MAP_WHERE_EXPRESSION, node);
+ source.setCondition(node.toString());
+ }
+ if (lexer.hasToken("check")) {
+ lexer.take();
+ ExpressionNode node = fpe.parse(lexer);
+ source.setUserData(MAP_WHERE_CHECK, node);
+ source.setCheck(node.toString());
+ }
+ }
+
+ private void parseTarget(StructureMapGroupRuleComponent rule, FHIRLexer lexer) throws FHIRException {
+ StructureMapGroupRuleTargetComponent target = rule.addTarget();
+ String start = lexer.take();
+ if (lexer.hasToken(".")) {
+ target.setContext(start);
+ target.setContextType(StructureMapContextType.VARIABLE);
+ start = null;
+ lexer.token(".");
+ target.setElement(lexer.take());
+ }
+ String name;
+ boolean isConstant = false;
+ if (lexer.hasToken("=")) {
+ if (start != null)
+ target.setContext(start);
+ lexer.token("=");
+ isConstant = lexer.isConstant(true);
+ name = lexer.take();
+ } else
+ name = start;
+
+ if ("(".equals(name)) {
+ // inline fluentpath expression
+ target.setTransform(StructureMapTransform.EVALUATE);
+ ExpressionNode node = fpe.parse(lexer);
+ target.setUserData(MAP_EXPRESSION, node);
+ target.addParameter().setValue(new StringType(node.toString()));
+ lexer.token(")");
+ } else if (lexer.hasToken("(")) {
+ target.setTransform(StructureMapTransform.fromCode(name));
+ lexer.token("(");
+ if (target.getTransform() == StructureMapTransform.EVALUATE) {
+ parseParameter(target, lexer);
+ lexer.token(",");
+ ExpressionNode node = fpe.parse(lexer);
+ target.setUserData(MAP_EXPRESSION, node);
+ target.addParameter().setValue(new StringType(node.toString()));
+ } else {
+ while (!lexer.hasToken(")")) {
+ parseParameter(target, lexer);
+ if (!lexer.hasToken(")"))
+ lexer.token(",");
+ }
+ }
+ lexer.token(")");
+ } else if (name != null) {
+ target.setTransform(StructureMapTransform.COPY);
+ if (!isConstant) {
+ String id = name;
+ while (lexer.hasToken(".")) {
+ id = id + lexer.take() + lexer.take();
+ }
+ target.addParameter().setValue(new IdType(id));
+ }
+ else
+ target.addParameter().setValue(readConstant(name, lexer));
+ }
+ if (lexer.hasToken("as")) {
+ lexer.take();
+ target.setVariable(lexer.take());
+ }
+ while (Utilities.existsInList(lexer.getCurrent(), "first", "last", "share", "collate")) {
+ if (lexer.getCurrent().equals("share")) {
+ target.addListMode(StructureMapTargetListMode.SHARE);
+ lexer.next();
+ target.setListRuleId(lexer.take());
+ } else if (lexer.getCurrent().equals("first"))
+ target.addListMode(StructureMapTargetListMode.FIRST);
+ else
+ target.addListMode(StructureMapTargetListMode.LAST);
+ lexer.next();
+ }
+ }
+
+
+ private void parseParameter(StructureMapGroupRuleTargetComponent target, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError {
+ if (!lexer.isConstant(true)) {
+ target.addParameter().setValue(new IdType(lexer.take()));
+ } else if (lexer.isStringConstant())
+ target.addParameter().setValue(new StringType(lexer.readConstant("??")));
+ else {
+ target.addParameter().setValue(readConstant(lexer.take(), lexer));
+ }
+ }
+
+ private Type readConstant(String s, FHIRLexer lexer) throws FHIRLexerException {
+ if (Utilities.isInteger(s))
+ return new IntegerType(s);
+ else if (Utilities.isDecimal(s))
+ return new DecimalType(s);
+ else if (Utilities.existsInList(s, "true", "false"))
+ return new BooleanType(s.equals("true"));
+ else
+ return new StringType(lexer.processConstant(s));
+ }
+
+ public StructureDefinition getTargetType(StructureMap map) throws FHIRException {
+ boolean found = false;
+ StructureDefinition res = null;
+ for (StructureMapStructureComponent uses : map.getStructure()) {
+ if (uses.getMode() == StructureMapModelMode.TARGET) {
+ if (found)
+ throw new FHIRException("Multiple targets found in map "+map.getUrl());
+ found = true;
+ res = worker.fetchResource(StructureDefinition.class, uses.getUrl());
+ if (res == null)
+ throw new FHIRException("Unable to find "+uses.getUrl()+" referenced from map "+map.getUrl());
+ }
+ }
+ if (res == null)
+ throw new FHIRException("No targets found in map "+map.getUrl());
+ return res;
+ }
+
+ public enum VariableMode {
+ INPUT, OUTPUT
+ }
+
+ public class Variable {
+ private VariableMode mode;
+ private String name;
+ private Base object;
+ public Variable(VariableMode mode, String name, Base object) {
+ super();
+ this.mode = mode;
+ this.name = name;
+ this.object = object;
+ }
+ public VariableMode getMode() {
+ return mode;
+ }
+ public String getName() {
+ return name;
+ }
+ public Base getObject() {
+ return object;
+ }
+ public String summary() {
+ return name+": "+object.fhirType();
+ }
+ }
+
+ public class Variables {
+ private List list = new ArrayList();
+
+ public void add(VariableMode mode, String name, Base object) {
+ Variable vv = null;
+ for (Variable v : list)
+ if ((v.mode == mode) && v.getName().equals(name))
+ vv = v;
+ if (vv != null)
+ list.remove(vv);
+ list.add(new Variable(mode, name, object));
+ }
+
+ public Variables copy() {
+ Variables result = new Variables();
+ result.list.addAll(list);
+ return result;
+ }
+
+ public Base get(VariableMode mode, String name) {
+ for (Variable v : list)
+ if ((v.mode == mode) && v.getName().equals(name))
+ return v.getObject();
+ return null;
+ }
+
+ public String summary() {
+ CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder();
+ CommaSeparatedStringBuilder t = new CommaSeparatedStringBuilder();
+ for (Variable v : list)
+ if (v.mode == VariableMode.INPUT)
+ s.append(v.summary());
+ else
+ t.append(v.summary());
+ return "source variables ["+s.toString()+"], target variables ["+t.toString()+"]";
+ }
+ }
+
+ public class TransformContext {
+ private Object appInfo;
+
+ public TransformContext(Object appInfo) {
+ super();
+ this.appInfo = appInfo;
+ }
+
+ public Object getAppInfo() {
+ return appInfo;
+ }
+
+ }
+
+ private void log(String cnt) {
+ if (services != null)
+ services.log(cnt);
+ }
+
+ /**
+ * Given an item, return all the children that conform to the pattern described in name
+ *
+ * Possible patterns:
+ * - a simple name (which may be the base of a name with [] e.g. value[x])
+ * - a name with a type replacement e.g. valueCodeableConcept
+ * - * which means all children
+ * - ** which means all descendents
+ *
+ * @param item
+ * @param name
+ * @param result
+ * @throws FHIRException
+ */
+ protected void getChildrenByName(Base item, String name, List result) throws FHIRException {
+ for (Base v : item.listChildrenByName(name, true))
+ if (v != null)
+ result.add(v);
+ }
+
+ public void transform(Object appInfo, Base source, StructureMap map, Base target) throws FHIRException {
+ TransformContext context = new TransformContext(appInfo);
+ log("Start Transform "+map.getUrl());
+ StructureMapGroupComponent g = map.getGroup().get(0);
+
+ Variables vars = new Variables();
+ vars.add(VariableMode.INPUT, getInputName(g, StructureMapInputMode.SOURCE, "source"), source);
+ vars.add(VariableMode.OUTPUT, getInputName(g, StructureMapInputMode.TARGET, "target"), target);
+
+ executeGroup("", context, map, vars, g);
+ if (target instanceof Element)
+ ((Element) target).sort();
+ }
+
+ private String getInputName(StructureMapGroupComponent g, StructureMapInputMode mode, String def) throws DefinitionException {
+ String name = null;
+ for (StructureMapGroupInputComponent inp : g.getInput()) {
+ if (inp.getMode() == mode)
+ if (name != null)
+ throw new DefinitionException("This engine does not support multiple source inputs");
+ else
+ name = inp.getName();
+ }
+ return name == null ? def : name;
+ }
+
+ private void executeGroup(String indent, TransformContext context, StructureMap map, Variables vars, StructureMapGroupComponent group) throws FHIRException {
+ log(indent+"Group : "+group.getName());
+ // todo: check inputs
+ if (group.hasExtends()) {
+ ResolvedGroup rg = resolveGroupReference(map, group, group.getExtends());
+ executeGroup(indent+" ", context, rg.targetMap, vars, rg.target);
+ }
+
+ for (StructureMapGroupRuleComponent r : group.getRule()) {
+ executeRule(indent+" ", context, map, vars, group, r);
+ }
+ }
+
+ private void executeRule(String indent, TransformContext context, StructureMap map, Variables vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule) throws FHIRException {
+ log(indent+"rule : "+rule.getName());
+ if (rule.getName().contains("CarePlan.participant-unlink"))
+ System.out.println("debug");
+ Variables srcVars = vars.copy();
+ if (rule.getSource().size() != 1)
+ throw new FHIRException("Rule \""+rule.getName()+"\": not handled yet");
+ List source = processSource(rule.getName(), context, srcVars, rule.getSource().get(0));
+ if (source != null) {
+ for (Variables v : source) {
+ for (StructureMapGroupRuleTargetComponent t : rule.getTarget()) {
+ processTarget(rule.getName(), context, v, map, group, t, rule.getSource().size() == 1 ? rule.getSourceFirstRep().getVariable() : null);
+ }
+ if (rule.hasRule()) {
+ for (StructureMapGroupRuleComponent childrule : rule.getRule()) {
+ executeRule(indent +" ", context, map, v, group, childrule);
+ }
+ } else if (rule.hasDependent()) {
+ for (StructureMapGroupRuleDependentComponent dependent : rule.getDependent()) {
+ executeDependency(indent+" ", context, map, v, group, dependent);
+ }
+ } else if (rule.getSource().size() == 1 && rule.getSourceFirstRep().hasVariable() && rule.getTarget().size() == 1 && rule.getTargetFirstRep().hasVariable() && rule.getTargetFirstRep().getTransform() == StructureMapTransform.CREATE && !rule.getTargetFirstRep().hasParameter()) {
+ // simple inferred, map by type
+ Base src = v.get(VariableMode.INPUT, rule.getSourceFirstRep().getVariable());
+ Base tgt = v.get(VariableMode.OUTPUT, rule.getTargetFirstRep().getVariable());
+ String srcType = src.fhirType();
+ String tgtType = tgt.fhirType();
+ ResolvedGroup defGroup = resolveGroupByTypes(map, rule.getName(), group, srcType, tgtType);
+ Variables vdef = new Variables();
+ vdef.add(VariableMode.INPUT, defGroup.target.getInput().get(0).getName(), src);
+ vdef.add(VariableMode.OUTPUT, defGroup.target.getInput().get(1).getName(), tgt);
+ executeGroup(indent+" ", context, defGroup.targetMap, vdef, defGroup.target);
+ }
+ }
+ }
+ }
+
+ private void executeDependency(String indent, TransformContext context, StructureMap map, Variables vin, StructureMapGroupComponent group, StructureMapGroupRuleDependentComponent dependent) throws FHIRException {
+ ResolvedGroup rg = resolveGroupReference(map, group, dependent.getName());
+
+ if (rg.target.getInput().size() != dependent.getVariable().size()) {
+ throw new FHIRException("Rule '"+dependent.getName()+"' has "+Integer.toString(rg.target.getInput().size())+" but the invocation has "+Integer.toString(dependent.getVariable().size())+" variables");
+ }
+ Variables v = new Variables();
+ for (int i = 0; i < rg.target.getInput().size(); i++) {
+ StructureMapGroupInputComponent input = rg.target.getInput().get(i);
+ StringType rdp = dependent.getVariable().get(i);
+ String var = rdp.asStringValue();
+ VariableMode mode = input.getMode() == StructureMapInputMode.SOURCE ? VariableMode.INPUT : VariableMode.OUTPUT;
+ Base vv = vin.get(mode, var);
+ if (vv == null && mode == VariableMode.INPUT) //* once source, always source. but target can be treated as source at user convenient
+ vv = vin.get(VariableMode.OUTPUT, var);
+ if (vv == null)
+ throw new FHIRException("Rule '"+dependent.getName()+"' "+mode.toString()+" variable '"+input.getName()+"' named as '"+var+"' has no value");
+ v.add(mode, input.getName(), vv);
+ }
+ executeGroup(indent+" ", context, rg.targetMap, v, rg.target);
+ }
+
+ private String determineTypeFromSourceType(StructureMap map, StructureMapGroupComponent source, Base base, String[] types) throws FHIRException {
+ String type = base.fhirType();
+ String kn = "type^"+type;
+ if (source.hasUserData(kn))
+ return source.getUserString(kn);
+
+ ResolvedGroup res = new ResolvedGroup();
+ res.targetMap = null;
+ res.target = null;
+ for (StructureMapGroupComponent grp : map.getGroup()) {
+ if (matchesByType(map, grp, type)) {
+ if (res.targetMap == null) {
+ res.targetMap = map;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches looking for default rule for '"+type+"'");
+ }
+ }
+ if (res.targetMap != null) {
+ String result = getActualType(res.targetMap, res.target.getInput().get(1).getType());
+ source.setUserData(kn, result);
+ return result;
+ }
+
+ for (UriType imp : map.getImport()) {
+ List impMapList = findMatchingMaps(imp.getValue());
+ if (impMapList.size() == 0)
+ throw new FHIRException("Unable to find map(s) for "+imp.getValue());
+ for (StructureMap impMap : impMapList) {
+ if (!impMap.getUrl().equals(map.getUrl())) {
+ for (StructureMapGroupComponent grp : impMap.getGroup()) {
+ if (matchesByType(impMap, grp, type)) {
+ if (res.targetMap == null) {
+ res.targetMap = impMap;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches for default rule for '"+type+"' in "+res.targetMap.getUrl()+" ("+res.target.getName()+") and "+impMap.getUrl()+" ("+grp.getName()+")");
+ }
+ }
+ }
+ }
+ }
+ if (res.target == null)
+ throw new FHIRException("No matches found for default rule for '"+type+"' from "+map.getUrl());
+ String result = getActualType(res.targetMap, res.target.getInput().get(1).getType()); // should be .getType, but R2...
+ source.setUserData(kn, result);
+ return result;
+ }
+
+ private List findMatchingMaps(String value) {
+ List res = new ArrayList();
+ if (value.contains("*")) {
+ for (StructureMap sm : library.values()) {
+ if (urlMatches(value, sm.getUrl())) {
+ res.add(sm);
+ }
+ }
+ } else {
+ StructureMap sm = library.get(value);
+ if (sm != null)
+ res.add(sm);
+ }
+ Set check = new HashSet();
+ for (StructureMap sm : res) {
+ if (check.contains(sm.getUrl()))
+ throw new Error("duplicate");
+ else
+ check.add(sm.getUrl());
+ }
+ return res;
+ }
+
+ private boolean urlMatches(String mask, String url) {
+ return url.length() > mask.length() && url.startsWith(mask.substring(0, mask.indexOf("*"))) && url.endsWith(mask.substring(mask.indexOf("*")+1)) ;
+ }
+
+ private ResolvedGroup resolveGroupByTypes(StructureMap map, String ruleid, StructureMapGroupComponent source, String srcType, String tgtType) throws FHIRException {
+ String kn = "types^"+srcType+":"+tgtType;
+ if (source.hasUserData(kn))
+ return (ResolvedGroup) source.getUserData(kn);
+
+ ResolvedGroup res = new ResolvedGroup();
+ res.targetMap = null;
+ res.target = null;
+ for (StructureMapGroupComponent grp : map.getGroup()) {
+ if (matchesByType(map, grp, srcType, tgtType)) {
+ if (res.targetMap == null) {
+ res.targetMap = map;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches looking for rule for '"+srcType+"/"+tgtType+"', from rule '"+ruleid+"'");
+ }
+ }
+ if (res.targetMap != null) {
+ source.setUserData(kn, res);
+ return res;
+ }
+
+ for (UriType imp : map.getImport()) {
+ List impMapList = findMatchingMaps(imp.getValue());
+ if (impMapList.size() == 0)
+ throw new FHIRException("Unable to find map(s) for "+imp.getValue());
+ for (StructureMap impMap : impMapList) {
+ if (!impMap.getUrl().equals(map.getUrl())) {
+ for (StructureMapGroupComponent grp : impMap.getGroup()) {
+ if (matchesByType(impMap, grp, srcType, tgtType)) {
+ if (res.targetMap == null) {
+ res.targetMap = impMap;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches for rule for '"+srcType+"/"+tgtType+"' in "+res.targetMap.getUrl()+" and "+impMap.getUrl()+", from rule '"+ruleid+"'");
+ }
+ }
+ }
+ }
+ }
+ if (res.target == null)
+ throw new FHIRException("No matches found for rule for '"+srcType+"/"+tgtType+"' from "+map.getUrl()+", from rule '"+ruleid+"'");
+ source.setUserData(kn, res);
+ return res;
+ }
+
+
+ private boolean matchesByType(StructureMap map, StructureMapGroupComponent grp, String type) throws FHIRException {
+ if (grp.getTypeMode() != StructureMapGroupTypeMode.TYPEANDTYPES)
+ return false;
+ if (grp.getInput().size() != 2 || grp.getInput().get(0).getMode() != StructureMapInputMode.SOURCE || grp.getInput().get(1).getMode() != StructureMapInputMode.TARGET)
+ return false;
+ return matchesType(map, type, grp.getInput().get(0).getType());
+ }
+
+ private boolean matchesByType(StructureMap map, StructureMapGroupComponent grp, String srcType, String tgtType) throws FHIRException {
+ if (grp.getTypeMode() == StructureMapGroupTypeMode.NONE)
+ return false;
+ if (grp.getInput().size() != 2 || grp.getInput().get(0).getMode() != StructureMapInputMode.SOURCE || grp.getInput().get(1).getMode() != StructureMapInputMode.TARGET)
+ return false;
+ if (!grp.getInput().get(0).hasType() || !grp.getInput().get(1).hasType())
+ return false;
+ return matchesType(map, srcType, grp.getInput().get(0).getType()) && matchesType(map, tgtType, grp.getInput().get(1).getType());
+ }
+
+ private boolean matchesType(StructureMap map, String actualType, String statedType) throws FHIRException {
+ // check the aliases
+ for (StructureMapStructureComponent imp : map.getStructure()) {
+ if (imp.hasAlias() && statedType.equals(imp.getAlias())) {
+ StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
+ if (sd != null)
+ statedType = sd.getType();
+ break;
+ }
+ }
+
+ return actualType.equals(statedType);
+ }
+
+ private String getActualType(StructureMap map, String statedType) throws FHIRException {
+ // check the aliases
+ for (StructureMapStructureComponent imp : map.getStructure()) {
+ if (imp.hasAlias() && statedType.equals(imp.getAlias())) {
+ StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
+ if (sd == null)
+ throw new FHIRException("Unable to resolve structure "+imp.getUrl());
+ return sd.getId(); // should be sd.getType(), but R2...
+ }
+ }
+ return statedType;
+ }
+
+
+ private ResolvedGroup resolveGroupReference(StructureMap map, StructureMapGroupComponent source, String name) throws FHIRException {
+ String kn = "ref^"+name;
+ if (source.hasUserData(kn))
+ return (ResolvedGroup) source.getUserData(kn);
+
+ ResolvedGroup res = new ResolvedGroup();
+ res.targetMap = null;
+ res.target = null;
+ for (StructureMapGroupComponent grp : map.getGroup()) {
+ if (grp.getName().equals(name)) {
+ if (res.targetMap == null) {
+ res.targetMap = map;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches for rule '"+name+"'");
+ }
+ }
+ if (res.targetMap != null) {
+ source.setUserData(kn, res);
+ return res;
+ }
+
+ for (UriType imp : map.getImport()) {
+ List impMapList = findMatchingMaps(imp.getValue());
+ if (impMapList.size() == 0)
+ throw new FHIRException("Unable to find map(s) for "+imp.getValue());
+ for (StructureMap impMap : impMapList) {
+ if (!impMap.getUrl().equals(map.getUrl())) {
+ for (StructureMapGroupComponent grp : impMap.getGroup()) {
+ if (grp.getName().equals(name)) {
+ if (res.targetMap == null) {
+ res.targetMap = impMap;
+ res.target = grp;
+ } else
+ throw new FHIRException("Multiple possible matches for rule '"+name+"' in "+res.targetMap.getUrl()+" and "+impMap.getUrl());
+ }
+ }
+ }
+ }
+ }
+ if (res.target == null)
+ throw new FHIRException("No matches found for rule '"+name+"'. Reference found in "+map.getUrl());
+ source.setUserData(kn, res);
+ return res;
+ }
+
+ private List processSource(String ruleId, TransformContext context, Variables vars, StructureMapGroupRuleSourceComponent src) throws FHIRException {
+ List items;
+ if (src.getContext().equals("@search")) {
+ ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_SEARCH_EXPRESSION);
+ if (expr == null) {
+ expr = fpe.parse(src.getElement());
+ src.setUserData(MAP_SEARCH_EXPRESSION, expr);
+ }
+ String search = fpe.evaluateToString(vars, null, new StringType(), expr); // string is a holder of nothing to ensure that variables are processed correctly
+ items = services.performSearch(context.appInfo, search);
+ } else {
+ items = new ArrayList();
+ Base b = vars.get(VariableMode.INPUT, src.getContext());
+ if (b == null)
+ throw new FHIRException("Unknown input variable "+src.getContext());
+
+ if (!src.hasElement())
+ items.add(b);
+ else {
+ getChildrenByName(b, src.getElement(), items);
+ if (items.size() == 0 && src.hasDefaultValue())
+ items.add(src.getDefaultValue());
+ }
+ }
+
+ if (src.hasType()) {
+ List remove = new ArrayList();
+ for (Base item : items) {
+ if (item != null && !isType(item, src.getType())) {
+ remove.add(item);
+ }
+ }
+ items.removeAll(remove);
+ }
+
+ if (src.hasCondition()) {
+ ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_WHERE_EXPRESSION);
+ if (expr == null) {
+ expr = fpe.parse(src.getCondition());
+ // fpe.check(context.appInfo, ??, ??, expr)
+ src.setUserData(MAP_WHERE_EXPRESSION, expr);
+ }
+ List remove = new ArrayList();
+ for (Base item : items) {
+ if (!fpe.evaluateToBoolean(vars, null, item, expr))
+ remove.add(item);
+ }
+ items.removeAll(remove);
+ }
+
+ if (src.hasCheck()) {
+ ExpressionNode expr = (ExpressionNode) src.getUserData(MAP_WHERE_CHECK);
+ if (expr == null) {
+ expr = fpe.parse(src.getCheck());
+ // fpe.check(context.appInfo, ??, ??, expr)
+ src.setUserData(MAP_WHERE_CHECK, expr);
+ }
+ List remove = new ArrayList();
+ for (Base item : items) {
+ if (!fpe.evaluateToBoolean(vars, null, item, expr))
+ throw new FHIRException("Rule \""+ruleId+"\": Check condition failed");
+ }
+ }
+
+
+ if (src.hasListMode() && !items.isEmpty()) {
+ switch (src.getListMode()) {
+ case FIRST:
+ Base bt = items.get(0);
+ items.clear();
+ items.add(bt);
+ break;
+ case NOTFIRST:
+ if (items.size() > 0)
+ items.remove(0);
+ break;
+ case LAST:
+ bt = items.get(items.size()-1);
+ items.clear();
+ items.add(bt);
+ break;
+ case NOTLAST:
+ if (items.size() > 0)
+ items.remove(items.size()-1);
+ break;
+ case ONLYONE:
+ if (items.size() > 1)
+ throw new FHIRException("Rule \""+ruleId+"\": Check condition failed: the collection has more than one item");
+ break;
+ case NULL:
+ }
+ }
+ List result = new ArrayList();
+ for (Base r : items) {
+ Variables v = vars.copy();
+ if (src.hasVariable())
+ v.add(VariableMode.INPUT, src.getVariable(), r);
+ result.add(v);
+ }
+ return result;
+ }
+
+
+ private boolean isType(Base item, String type) {
+ if (type.equals(item.fhirType()))
+ return true;
+ return false;
+ }
+
+ private void processTarget(String ruleId, TransformContext context, Variables vars, StructureMap map, StructureMapGroupComponent group, StructureMapGroupRuleTargetComponent tgt, String srcVar) throws FHIRException {
+ Base dest = null;
+ if (tgt.hasContext()) {
+ dest = vars.get(VariableMode.OUTPUT, tgt.getContext());
+ if (dest == null)
+ throw new FHIRException("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
+ if (!tgt.hasElement())
+ throw new FHIRException("Rule \""+ruleId+"\": Not supported yet");
+ }
+ Base v = null;
+ if (tgt.hasTransform()) {
+ v = runTransform(ruleId, context, map, group, tgt, vars, dest, tgt.getElement(), srcVar);
+ if (v != null && dest != null)
+ v = dest.setProperty(tgt.getElement().hashCode(), tgt.getElement(), v); // reset v because some implementations may have to rewrite v when setting the value
+ } else if (dest != null)
+ v = dest.makeProperty(tgt.getElement().hashCode(), tgt.getElement());
+ if (tgt.hasVariable() && v != null)
+ vars.add(VariableMode.OUTPUT, tgt.getVariable(), v);
+ }
+
+ private Base runTransform(String ruleId, TransformContext context, StructureMap map, StructureMapGroupComponent group, StructureMapGroupRuleTargetComponent tgt, Variables vars, Base dest, String element, String srcVar) throws FHIRException {
+ try {
+ switch (tgt.getTransform()) {
+ case CREATE :
+ String tn;
+ if (tgt.getParameter().isEmpty()) {
+ // we have to work out the type. First, we see if there is a single type for the target. If there is, we use that
+ String[] types = dest.getTypesForProperty(element.hashCode(), element);
+ if (types.length == 1 && !"*".equals(types[0]) && !types[0].equals("Resource"))
+ tn = types[0];
+ else if (srcVar != null) {
+ tn = determineTypeFromSourceType(map, group, vars.get(VariableMode.INPUT, srcVar), types);
+ } else
+ throw new Error("Cannot determine type implicitly because there is no single input variable");
+ } else
+ tn = getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString());
+ Base res = services != null ? services.createType(context.getAppInfo(), tn) : ResourceFactory.createResourceOrType(tn);
+ if (res.isResource() && !res.fhirType().equals("Parameters")) {
+// res.setIdBase(tgt.getParameter().size() > 1 ? getParamString(vars, tgt.getParameter().get(0)) : UUID.randomUUID().toString().toLowerCase());
+ if (services != null)
+ res = services.createResource(context.getAppInfo(), res);
+ }
+ if (tgt.hasUserData("profile"))
+ res.setUserData("profile", tgt.getUserData("profile"));
+ return res;
+ case COPY :
+ return getParam(vars, tgt.getParameter().get(0));
+ case EVALUATE :
+ ExpressionNode expr = (ExpressionNode) tgt.getUserData(MAP_EXPRESSION);
+ if (expr == null) {
+ expr = fpe.parse(getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString()));
+ tgt.setUserData(MAP_WHERE_EXPRESSION, expr);
+ }
+ List v = fpe.evaluate(vars, null, tgt.getParameter().size() == 2 ? getParam(vars, tgt.getParameter().get(0)) : new BooleanType(false), expr);
+ if (v.size() == 0)
+ return null;
+ else if (v.size() != 1)
+ throw new FHIRException("Rule \""+ruleId+"\": Evaluation of "+expr.toString()+" returned "+Integer.toString(v.size())+" objects");
+ else
+ return v.get(0);
+
+ case TRUNCATE :
+ String src = getParamString(vars, tgt.getParameter().get(0));
+ String len = getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString());
+ if (Utilities.isInteger(len)) {
+ int l = Integer.parseInt(len);
+ if (src.length() > l)
+ src = src.substring(0, l);
+ }
+ return new StringType(src);
+ case ESCAPE :
+ throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
+ case CAST :
+ throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
+ case APPEND :
+ throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
+ case TRANSLATE :
+ return translate(context, map, vars, tgt.getParameter());
+ case REFERENCE :
+ Base b = getParam(vars, tgt.getParameter().get(0));
+ if (b == null)
+ throw new FHIRException("Rule \""+ruleId+"\": Unable to find parameter "+((IdType) tgt.getParameter().get(0).getValue()).asStringValue());
+ if (!b.isResource())
+ throw new FHIRException("Rule \""+ruleId+"\": Transform engine cannot point at an element of type "+b.fhirType());
+ else {
+ String id = b.getIdBase();
+ if (id == null) {
+ id = UUID.randomUUID().toString().toLowerCase();
+ b.setIdBase(id);
+ }
+ return new Reference().setReference(b.fhirType()+"/"+id);
+ }
+ case DATEOP :
+ throw new Error("Rule \""+ruleId+"\": Transform "+tgt.getTransform().toCode()+" not supported yet");
+ case UUID :
+ return new IdType(UUID.randomUUID().toString());
+ case POINTER :
+ b = getParam(vars, tgt.getParameter().get(0));
+ if (b instanceof Resource)
+ return new UriType("urn:uuid:"+((Resource) b).getId());
+ else
+ throw new FHIRException("Rule \""+ruleId+"\": Transform engine cannot point at an element of type "+b.fhirType());
+ case CC:
+ CodeableConcept cc = new CodeableConcept();
+ cc.addCoding(buildCoding(getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString()), getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString())));
+ return cc;
+ case C:
+ Coding c = buildCoding(getParamStringNoNull(vars, tgt.getParameter().get(0), tgt.toString()), getParamStringNoNull(vars, tgt.getParameter().get(1), tgt.toString()));
+ return c;
+ default:
+ throw new Error("Rule \""+ruleId+"\": Transform Unknown: "+tgt.getTransform().toCode());
+ }
+ } catch (Exception e) {
+ throw new FHIRException("Exception executing transform "+tgt.toString()+" on Rule \""+ruleId+"\": "+e.getMessage(), e);
+ }
+ }
+
+
+ private Coding buildCoding(String uri, String code) throws FHIRException {
+ // if we can get this as a valueSet, we will
+ String system = null;
+ String display = null;
+ ValueSet vs = Utilities.noString(uri) ? null : worker.fetchResourceWithException(ValueSet.class, uri);
+ if (vs != null) {
+ ValueSetExpansionOutcome vse = worker.expandVS(vs, true, false);
+ if (vse.getError() != null)
+ throw new FHIRException(vse.getError());
+ CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
+ for (ValueSetExpansionContainsComponent t : vse.getValueset().getExpansion().getContains()) {
+ if (t.hasCode())
+ b.append(t.getCode());
+ if (code.equals(t.getCode()) && t.hasSystem()) {
+ system = t.getSystem();
+ display = t.getDisplay();
+ break;
+ }
+ if (code.equalsIgnoreCase(t.getDisplay()) && t.hasSystem()) {
+ system = t.getSystem();
+ display = t.getDisplay();
+ break;
+ }
+ }
+ if (system == null)
+ throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)");
+ } else
+ system = uri;
+ ValidationResult vr = worker.validateCode(system, code, null);
+ if (vr != null && vr.getDisplay() != null)
+ display = vr.getDisplay();
+ return new Coding().setSystem(system).setCode(code).setDisplay(display);
+ }
+
+
+ private String getParamStringNoNull(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter, String message) throws FHIRException {
+ Base b = getParam(vars, parameter);
+ if (b == null)
+ throw new FHIRException("Unable to find a value for "+parameter.toString()+". Context: "+message);
+ if (!b.hasPrimitiveValue())
+ throw new FHIRException("Found a value for "+parameter.toString()+", but it has a type of "+b.fhirType()+" and cannot be treated as a string. Context: "+message);
+ return b.primitiveValue();
+ }
+
+ private String getParamString(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
+ Base b = getParam(vars, parameter);
+ if (b == null || !b.hasPrimitiveValue())
+ return null;
+ return b.primitiveValue();
+ }
+
+
+ private Base getParam(Variables vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
+ Type p = parameter.getValue();
+ if (!(p instanceof IdType))
+ return p;
+ else {
+ String n = ((IdType) p).asStringValue();
+ Base b = vars.get(VariableMode.INPUT, n);
+ if (b == null)
+ b = vars.get(VariableMode.OUTPUT, n);
+ if (b == null)
+ throw new DefinitionException("Variable "+n+" not found ("+vars.summary()+")");
+ return b;
+ }
+ }
+
+
+ private Base translate(TransformContext context, StructureMap map, Variables vars, List parameter) throws FHIRException {
+ Base src = getParam(vars, parameter.get(0));
+ String id = getParamString(vars, parameter.get(1));
+ String fld = parameter.size() > 2 ? getParamString(vars, parameter.get(2)) : null;
+ return translate(context, map, src, id, fld);
+ }
+
+ private class SourceElementComponentWrapper {
+ private ConceptMapGroupComponent group;
+ private SourceElementComponent comp;
+ public SourceElementComponentWrapper(ConceptMapGroupComponent group, SourceElementComponent comp) {
+ super();
+ this.group = group;
+ this.comp = comp;
+ }
+ }
+ public Base translate(TransformContext context, StructureMap map, Base source, String conceptMapUrl, String fieldToReturn) throws FHIRException {
+ Coding src = new Coding();
+ if (source.isPrimitive()) {
+ src.setCode(source.primitiveValue());
+ } else if ("Coding".equals(source.fhirType())) {
+ Base[] b = source.getProperty("system".hashCode(), "system", true);
+ if (b.length == 1)
+ src.setSystem(b[0].primitiveValue());
+ b = source.getProperty("code".hashCode(), "code", true);
+ if (b.length == 1)
+ src.setCode(b[0].primitiveValue());
+ } else if ("CE".equals(source.fhirType())) {
+ Base[] b = source.getProperty("codeSystem".hashCode(), "codeSystem", true);
+ if (b.length == 1)
+ src.setSystem(b[0].primitiveValue());
+ b = source.getProperty("code".hashCode(), "code", true);
+ if (b.length == 1)
+ src.setCode(b[0].primitiveValue());
+ } else
+ throw new FHIRException("Unable to translate source "+source.fhirType());
+
+ String su = conceptMapUrl;
+ if (conceptMapUrl.equals("http://hl7.org/fhir/ConceptMap/special-oid2uri")) {
+ String uri = worker.oid2Uri(src.getCode());
+ if (uri == null)
+ uri = "urn:oid:"+src.getCode();
+ if ("uri".equals(fieldToReturn))
+ return new UriType(uri);
+ else
+ throw new FHIRException("Error in return code");
+ } else {
+ ConceptMap cmap = null;
+ if (conceptMapUrl.startsWith("#")) {
+ for (Resource r : map.getContained()) {
+ if (r instanceof ConceptMap && ((ConceptMap) r).getId().equals(conceptMapUrl.substring(1))) {
+ cmap = (ConceptMap) r;
+ su = map.getUrl()+conceptMapUrl;
+ }
+ }
+ if (cmap == null)
+ throw new FHIRException("Unable to translate - cannot find map "+conceptMapUrl);
+ } else
+ cmap = worker.fetchResource(ConceptMap.class, conceptMapUrl);
+ Coding outcome = null;
+ boolean done = false;
+ String message = null;
+ if (cmap == null) {
+ if (services == null)
+ message = "No map found for "+conceptMapUrl;
+ else {
+ outcome = services.translate(context.appInfo, src, conceptMapUrl);
+ done = true;
+ }
+ } else {
+ List list = new ArrayList();
+ for (ConceptMapGroupComponent g : cmap.getGroup()) {
+ for (SourceElementComponent e : g.getElement()) {
+ if (!src.hasSystem() && src.getCode().equals(e.getCode()))
+ list.add(new SourceElementComponentWrapper(g, e));
+ else if (src.hasSystem() && src.getSystem().equals(g.getSource()) && src.getCode().equals(e.getCode()))
+ list.add(new SourceElementComponentWrapper(g, e));
+ }
+ }
+ if (list.size() == 0)
+ done = true;
+ else if (list.get(0).comp.getTarget().size() == 0)
+ message = "Concept map "+su+" found no translation for "+src.getCode();
+ else {
+ for (TargetElementComponent tgt : list.get(0).comp.getTarget()) {
+ if (tgt.getEquivalence() == null || EnumSet.of( ConceptMapEquivalence.EQUAL , ConceptMapEquivalence.RELATEDTO , ConceptMapEquivalence.EQUIVALENT, ConceptMapEquivalence.WIDER).contains(tgt.getEquivalence())) {
+ if (done) {
+ message = "Concept map "+su+" found multiple matches for "+src.getCode();
+ done = false;
+ } else {
+ done = true;
+ outcome = new Coding().setCode(tgt.getCode()).setSystem(list.get(0).group.getTarget());
+ }
+ } else if (tgt.getEquivalence() == ConceptMapEquivalence.UNMATCHED) {
+ done = true;
+ }
+ }
+ if (!done)
+ message = "Concept map "+su+" found no usable translation for "+src.getCode();
+ }
+ }
+ if (!done)
+ throw new FHIRException(message);
+ if (outcome == null)
+ return null;
+ if ("code".equals(fieldToReturn))
+ return new CodeType(outcome.getCode());
+ else
+ return outcome;
+ }
+ }
+
+
+ public Map getLibrary() {
+ return library;
+ }
+
+ public class PropertyWithType {
+ private String path;
+ private Property baseProperty;
+ private Property profileProperty;
+ private TypeDetails types;
+ public PropertyWithType(String path, Property baseProperty, Property profileProperty, TypeDetails types) {
+ super();
+ this.baseProperty = baseProperty;
+ this.profileProperty = profileProperty;
+ this.path = path;
+ this.types = types;
+ }
+
+ public TypeDetails getTypes() {
+ return types;
+ }
+ public String getPath() {
+ return path;
+ }
+
+ public Property getBaseProperty() {
+ return baseProperty;
+ }
+
+ public void setBaseProperty(Property baseProperty) {
+ this.baseProperty = baseProperty;
+ }
+
+ public Property getProfileProperty() {
+ return profileProperty;
+ }
+
+ public void setProfileProperty(Property profileProperty) {
+ this.profileProperty = profileProperty;
+ }
+
+ public String summary() {
+ return path;
+ }
+
+ }
+
+ public class VariableForProfiling {
+ private VariableMode mode;
+ private String name;
+ private PropertyWithType property;
+
+ public VariableForProfiling(VariableMode mode, String name, PropertyWithType property) {
+ super();
+ this.mode = mode;
+ this.name = name;
+ this.property = property;
+ }
+ public VariableMode getMode() {
+ return mode;
+ }
+ public String getName() {
+ return name;
+ }
+ public PropertyWithType getProperty() {
+ return property;
+ }
+ public String summary() {
+ return name+": "+property.summary();
+ }
+ }
+
+ public class VariablesForProfiling {
+ private List list = new ArrayList();
+ private boolean optional;
+ private boolean repeating;
+
+ public VariablesForProfiling(boolean optional, boolean repeating) {
+ this.optional = optional;
+ this.repeating = repeating;
+ }
+
+ public void add(VariableMode mode, String name, String path, Property property, TypeDetails types) {
+ add(mode, name, new PropertyWithType(path, property, null, types));
+ }
+
+ public void add(VariableMode mode, String name, String path, Property baseProperty, Property profileProperty, TypeDetails types) {
+ add(mode, name, new PropertyWithType(path, baseProperty, profileProperty, types));
+ }
+
+ public void add(VariableMode mode, String name, PropertyWithType property) {
+ VariableForProfiling vv = null;
+ for (VariableForProfiling v : list)
+ if ((v.mode == mode) && v.getName().equals(name))
+ vv = v;
+ if (vv != null)
+ list.remove(vv);
+ list.add(new VariableForProfiling(mode, name, property));
+ }
+
+ public VariablesForProfiling copy(boolean optional, boolean repeating) {
+ VariablesForProfiling result = new VariablesForProfiling(optional, repeating);
+ result.list.addAll(list);
+ return result;
+ }
+
+ public VariablesForProfiling copy() {
+ VariablesForProfiling result = new VariablesForProfiling(optional, repeating);
+ result.list.addAll(list);
+ return result;
+ }
+
+ public VariableForProfiling get(VariableMode mode, String name) {
+ if (mode == null) {
+ for (VariableForProfiling v : list)
+ if ((v.mode == VariableMode.OUTPUT) && v.getName().equals(name))
+ return v;
+ for (VariableForProfiling v : list)
+ if ((v.mode == VariableMode.INPUT) && v.getName().equals(name))
+ return v;
+ }
+ for (VariableForProfiling v : list)
+ if ((v.mode == mode) && v.getName().equals(name))
+ return v;
+ return null;
+ }
+
+ public String summary() {
+ CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder();
+ CommaSeparatedStringBuilder t = new CommaSeparatedStringBuilder();
+ for (VariableForProfiling v : list)
+ if (v.mode == VariableMode.INPUT)
+ s.append(v.summary());
+ else
+ t.append(v.summary());
+ return "source variables ["+s.toString()+"], target variables ["+t.toString()+"]";
+ }
+ }
+
+ public class StructureMapAnalysis {
+ private List profiles = new ArrayList();
+ private XhtmlNode summary;
+ public List getProfiles() {
+ return profiles;
+ }
+ public XhtmlNode getSummary() {
+ return summary;
+ }
+
+ }
+
+ /**
+ * Given a structure map, return a set of analyses on it.
+ *
+ * Returned:
+ * - a list or profiles for what it will create. First profile is the target
+ * - a table with a summary (in xhtml) for easy human undertanding of the mapping
+ *
+ *
+ * @param appInfo
+ * @param map
+ * @return
+ * @throws Exception
+ */
+ public StructureMapAnalysis analyse(Object appInfo, StructureMap map) throws Exception {
+ ids.clear();
+ StructureMapAnalysis result = new StructureMapAnalysis();
+ TransformContext context = new TransformContext(appInfo);
+ VariablesForProfiling vars = new VariablesForProfiling(false, false);
+ StructureMapGroupComponent start = map.getGroup().get(0);
+ for (StructureMapGroupInputComponent t : start.getInput()) {
+ PropertyWithType ti = resolveType(map, t.getType(), t.getMode());
+ if (t.getMode() == StructureMapInputMode.SOURCE)
+ vars.add(VariableMode.INPUT, t.getName(), ti);
+ else
+ vars.add(VariableMode.OUTPUT, t.getName(), createProfile(map, result.profiles, ti, start.getName(), start));
+ }
+
+ result.summary = new XhtmlNode(NodeType.Element, "table").setAttribute("class", "grid");
+ XhtmlNode tr = result.summary.addTag("tr");
+ tr.addTag("td").addTag("b").addText("Source");
+ tr.addTag("td").addTag("b").addText("Target");
+
+ log("Start Profiling Transform "+map.getUrl());
+ analyseGroup("", context, map, vars, start, result);
+ ProfileUtilities pu = new ProfileUtilities(worker, null, pkp);
+ for (StructureDefinition sd : result.getProfiles())
+ pu.cleanUpDifferential(sd);
+ return result;
+ }
+
+
+ private void analyseGroup(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapAnalysis result) throws Exception {
+ log(indent+"Analyse Group : "+group.getName());
+ // todo: extends
+ // todo: check inputs
+ XhtmlNode tr = result.summary.addTag("tr").setAttribute("class", "diff-title");
+ XhtmlNode xs = tr.addTag("td");
+ XhtmlNode xt = tr.addTag("td");
+ for (StructureMapGroupInputComponent inp : group.getInput()) {
+ if (inp.getMode() == StructureMapInputMode.SOURCE)
+ noteInput(vars, inp, VariableMode.INPUT, xs);
+ if (inp.getMode() == StructureMapInputMode.TARGET)
+ noteInput(vars, inp, VariableMode.OUTPUT, xt);
+ }
+ for (StructureMapGroupRuleComponent r : group.getRule()) {
+ analyseRule(indent+" ", context, map, vars, group, r, result);
+ }
+ }
+
+
+ private void noteInput(VariablesForProfiling vars, StructureMapGroupInputComponent inp, VariableMode mode, XhtmlNode xs) {
+ VariableForProfiling v = vars.get(mode, inp.getName());
+ if (v != null)
+ xs.addText("Input: "+v.property.getPath());
+ }
+
+ private void analyseRule(String indent, TransformContext context, StructureMap map, VariablesForProfiling vars, StructureMapGroupComponent group, StructureMapGroupRuleComponent rule, StructureMapAnalysis result) throws Exception {
+ log(indent+"Analyse rule : "+rule.getName());
+ XhtmlNode tr = result.summary.addTag("tr");
+ XhtmlNode xs = tr.addTag("td");
+ XhtmlNode xt = tr.addTag("td");
+
+ VariablesForProfiling srcVars = vars.copy();
+ if (rule.getSource().size() != 1)
+ throw new Exception("Rule \""+rule.getName()+"\": not handled yet");
+ VariablesForProfiling source = analyseSource(rule.getName(), context, srcVars, rule.getSourceFirstRep(), xs);
+
+ TargetWriter tw = new TargetWriter();
+ for (StructureMapGroupRuleTargetComponent t : rule.getTarget()) {
+ analyseTarget(rule.getName(), context, source, map, t, rule.getSourceFirstRep().getVariable(), tw, result.profiles, rule.getName());
+ }
+ tw.commit(xt);
+
+ for (StructureMapGroupRuleComponent childrule : rule.getRule()) {
+ analyseRule(indent+" ", context, map, source, group, childrule, result);
+ }
+// for (StructureMapGroupRuleDependentComponent dependent : rule.getDependent()) {
+// executeDependency(indent+" ", context, map, v, group, dependent); // do we need group here?
+// }
+ }
+
+ public class StringPair {
+ private String var;
+ private String desc;
+ public StringPair(String var, String desc) {
+ super();
+ this.var = var;
+ this.desc = desc;
+ }
+ public String getVar() {
+ return var;
+ }
+ public String getDesc() {
+ return desc;
+ }
+ }
+ public class TargetWriter {
+ private Map newResources = new HashMap();
+ private List assignments = new ArrayList();
+ private List keyProps = new ArrayList();
+ private CommaSeparatedStringBuilder txt = new CommaSeparatedStringBuilder();
+
+ public void newResource(String var, String name) {
+ newResources.put(var, name);
+ txt.append("new "+name);
+ }
+
+ public void valueAssignment(String context, String desc) {
+ assignments.add(new StringPair(context, desc));
+ txt.append(desc);
+ }
+
+ public void keyAssignment(String context, String desc) {
+ keyProps.add(new StringPair(context, desc));
+ txt.append(desc);
+ }
+ public void commit(XhtmlNode xt) {
+ if (newResources.size() == 1 && assignments.size() == 1 && newResources.containsKey(assignments.get(0).getVar()) && keyProps.size() == 1 && newResources.containsKey(keyProps.get(0).getVar()) ) {
+ xt.addText("new "+assignments.get(0).desc+" ("+keyProps.get(0).desc.substring(keyProps.get(0).desc.indexOf(".")+1)+")");
+ } else if (newResources.size() == 1 && assignments.size() == 1 && newResources.containsKey(assignments.get(0).getVar()) && keyProps.size() == 0) {
+ xt.addText("new "+assignments.get(0).desc);
+ } else {
+ xt.addText(txt.toString());
+ }
+ }
+ }
+
+ private VariablesForProfiling analyseSource(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMapGroupRuleSourceComponent src, XhtmlNode td) throws Exception {
+ VariableForProfiling var = vars.get(VariableMode.INPUT, src.getContext());
+ if (var == null)
+ throw new FHIRException("Rule \""+ruleId+"\": Unknown input variable "+src.getContext());
+ PropertyWithType prop = var.getProperty();
+
+ boolean optional = false;
+ boolean repeating = false;
+
+ if (src.hasCondition()) {
+ optional = true;
+ }
+
+ if (src.hasElement()) {
+ Property element = prop.getBaseProperty().getChild(prop.types.getType(), src.getElement());
+ if (element == null)
+ throw new Exception("Rule \""+ruleId+"\": Unknown element name "+src.getElement());
+ if (element.getDefinition().getMin() == 0)
+ optional = true;
+ if (element.getDefinition().getMax().equals("*"))
+ repeating = true;
+ VariablesForProfiling result = vars.copy(optional, repeating);
+ TypeDetails type = new TypeDetails(CollectionStatus.SINGLETON);
+ for (TypeRefComponent tr : element.getDefinition().getType()) {
+ if (!tr.hasCode())
+ throw new Error("Rule \""+ruleId+"\": Element has no type");
+ ProfiledType pt = new ProfiledType(tr.getCode());
+ if (tr.hasProfile())
+ pt.addProfile(tr.getProfile());
+ if (element.getDefinition().hasBinding())
+ pt.addBinding(element.getDefinition().getBinding());
+ type.addType(pt);
+ }
+ td.addText(prop.getPath()+"."+src.getElement());
+ if (src.hasVariable())
+ result.add(VariableMode.INPUT, src.getVariable(), new PropertyWithType(prop.getPath()+"."+src.getElement(), element, null, type));
+ return result;
+ } else {
+ td.addText(prop.getPath()); // ditto!
+ return vars.copy(optional, repeating);
+ }
+ }
+
+
+ private void analyseTarget(String ruleId, TransformContext context, VariablesForProfiling vars, StructureMap map, StructureMapGroupRuleTargetComponent tgt, String tv, TargetWriter tw, List profiles, String sliceName) throws Exception {
+ VariableForProfiling var = null;
+ if (tgt.hasContext()) {
+ var = vars.get(VariableMode.OUTPUT, tgt.getContext());
+ if (var == null)
+ throw new Exception("Rule \""+ruleId+"\": target context not known: "+tgt.getContext());
+ if (!tgt.hasElement())
+ throw new Exception("Rule \""+ruleId+"\": Not supported yet");
+ }
+
+
+ TypeDetails type = null;
+ if (tgt.hasTransform()) {
+ type = analyseTransform(context, map, tgt, var, vars);
+ // profiling: dest.setProperty(tgt.getElement().hashCode(), tgt.getElement(), v);
+ } else {
+ Property vp = var.property.baseProperty.getChild(tgt.getElement(), tgt.getElement());
+ if (vp == null)
+ throw new Exception("Unknown Property "+tgt.getElement()+" on "+var.property.path);
+
+ type = new TypeDetails(CollectionStatus.SINGLETON, vp.getType(tgt.getElement()));
+ }
+
+ if (tgt.getTransform() == StructureMapTransform.CREATE) {
+ String s = getParamString(vars, tgt.getParameter().get(0));
+ if (worker.getResourceNames().contains(s))
+ tw.newResource(tgt.getVariable(), s);
+ } else {
+ boolean mapsSrc = false;
+ for (StructureMapGroupRuleTargetParameterComponent p : tgt.getParameter()) {
+ Type pr = p.getValue();
+ if (pr instanceof IdType && ((IdType) pr).asStringValue().equals(tv))
+ mapsSrc = true;
+ }
+ if (mapsSrc) {
+ if (var == null)
+ throw new Error("Rule \""+ruleId+"\": Attempt to assign with no context");
+ tw.valueAssignment(tgt.getContext(), var.property.getPath()+"."+tgt.getElement()+getTransformSuffix(tgt.getTransform()));
+ } else if (tgt.hasContext()) {
+ if (isSignificantElement(var.property, tgt.getElement())) {
+ String td = describeTransform(tgt);
+ if (td != null)
+ tw.keyAssignment(tgt.getContext(), var.property.getPath()+"."+tgt.getElement()+" = "+td);
+ }
+ }
+ }
+ Type fixed = generateFixedValue(tgt);
+
+ PropertyWithType prop = updateProfile(var, tgt.getElement(), type, map, profiles, sliceName, fixed, tgt);
+ if (tgt.hasVariable())
+ if (tgt.hasElement())
+ vars.add(VariableMode.OUTPUT, tgt.getVariable(), prop);
+ else
+ vars.add(VariableMode.OUTPUT, tgt.getVariable(), prop);
+ }
+
+ private Type generateFixedValue(StructureMapGroupRuleTargetComponent tgt) {
+ if (!allParametersFixed(tgt))
+ return null;
+ if (!tgt.hasTransform())
+ return null;
+ switch (tgt.getTransform()) {
+ case COPY: return tgt.getParameter().get(0).getValue();
+ case TRUNCATE: return null;
+ //case ESCAPE:
+ //case CAST:
+ //case APPEND:
+ case TRANSLATE: return null;
+ //case DATEOP,
+ //case UUID,
+ //case POINTER,
+ //case EVALUATE,
+ case CC:
+ CodeableConcept cc = new CodeableConcept();
+ cc.addCoding(buildCoding(tgt.getParameter().get(0).getValue(), tgt.getParameter().get(1).getValue()));
+ return cc;
+ case C:
+ return buildCoding(tgt.getParameter().get(0).getValue(), tgt.getParameter().get(1).getValue());
+ case QTY: return null;
+ //case ID,
+ //case CP,
+ default:
+ return null;
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private Coding buildCoding(Type value1, Type value2) {
+ return new Coding().setSystem(((PrimitiveType) value1).asStringValue()).setCode(((PrimitiveType) value2).asStringValue()) ;
+ }
+
+ private boolean allParametersFixed(StructureMapGroupRuleTargetComponent tgt) {
+ for (StructureMapGroupRuleTargetParameterComponent p : tgt.getParameter()) {
+ Type pr = p.getValue();
+ if (pr instanceof IdType)
+ return false;
+ }
+ return true;
+ }
+
+ private String describeTransform(StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
+ switch (tgt.getTransform()) {
+ case COPY: return null;
+ case TRUNCATE: return null;
+ //case ESCAPE:
+ //case CAST:
+ //case APPEND:
+ case TRANSLATE: return null;
+ //case DATEOP,
+ //case UUID,
+ //case POINTER,
+ //case EVALUATE,
+ case CC: return describeTransformCCorC(tgt);
+ case C: return describeTransformCCorC(tgt);
+ case QTY: return null;
+ //case ID,
+ //case CP,
+ default:
+ return null;
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private String describeTransformCCorC(StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
+ if (tgt.getParameter().size() < 2)
+ return null;
+ Type p1 = tgt.getParameter().get(0).getValue();
+ Type p2 = tgt.getParameter().get(1).getValue();
+ if (p1 instanceof IdType || p2 instanceof IdType)
+ return null;
+ if (!(p1 instanceof PrimitiveType) || !(p2 instanceof PrimitiveType))
+ return null;
+ String uri = ((PrimitiveType) p1).asStringValue();
+ String code = ((PrimitiveType) p2).asStringValue();
+ if (Utilities.noString(uri))
+ throw new FHIRException("Describe Transform, but the uri is blank");
+ if (Utilities.noString(code))
+ throw new FHIRException("Describe Transform, but the code is blank");
+ Coding c = buildCoding(uri, code);
+ return NarrativeGenerator.describeSystem(c.getSystem())+"#"+c.getCode()+(c.hasDisplay() ? "("+c.getDisplay()+")" : "");
+ }
+
+
+ private boolean isSignificantElement(PropertyWithType property, String element) {
+ if ("Observation".equals(property.getPath()))
+ return "code".equals(element);
+ else if ("Bundle".equals(property.getPath()))
+ return "type".equals(element);
+ else
+ return false;
+ }
+
+ private String getTransformSuffix(StructureMapTransform transform) {
+ switch (transform) {
+ case COPY: return "";
+ case TRUNCATE: return " (truncated)";
+ //case ESCAPE:
+ //case CAST:
+ //case APPEND:
+ case TRANSLATE: return " (translated)";
+ //case DATEOP,
+ //case UUID,
+ //case POINTER,
+ //case EVALUATE,
+ case CC: return " (--> CodeableConcept)";
+ case C: return " (--> Coding)";
+ case QTY: return " (--> Quantity)";
+ //case ID,
+ //case CP,
+ default:
+ return " {??)";
+ }
+ }
+
+ private PropertyWithType updateProfile(VariableForProfiling var, String element, TypeDetails type, StructureMap map, List profiles, String sliceName, Type fixed, StructureMapGroupRuleTargetComponent tgt) throws FHIRException {
+ if (var == null) {
+ assert (Utilities.noString(element));
+ // 1. start the new structure definition
+ StructureDefinition sdn = worker.fetchResource(StructureDefinition.class, type.getType());
+ if (sdn == null)
+ throw new FHIRException("Unable to find definition for "+type.getType());
+ ElementDefinition edn = sdn.getSnapshot().getElementFirstRep();
+ PropertyWithType pn = createProfile(map, profiles, new PropertyWithType(sdn.getId(), new Property(worker, edn, sdn), null, type), sliceName, tgt);
+
+// // 2. hook it into the base bundle
+// if (type.getType().startsWith("http://hl7.org/fhir/StructureDefinition/") && worker.getResourceNames().contains(type.getType().substring(40))) {
+// StructureDefinition sd = var.getProperty().profileProperty.getStructure();
+// ElementDefinition ed = sd.getDifferential().addElement();
+// ed.setPath("Bundle.entry");
+// ed.setName(sliceName);
+// ed.setMax("1"); // well, it is for now...
+// ed = sd.getDifferential().addElement();
+// ed.setPath("Bundle.entry.fullUrl");
+// ed.setMin(1);
+// ed = sd.getDifferential().addElement();
+// ed.setPath("Bundle.entry.resource");
+// ed.setMin(1);
+// ed.addType().setCode(pn.getProfileProperty().getStructure().getType()).setProfile(pn.getProfileProperty().getStructure().getUrl());
+// }
+ return pn;
+ } else {
+ assert (!Utilities.noString(element));
+ Property pvb = var.getProperty().getBaseProperty();
+ Property pvd = var.getProperty().getProfileProperty();
+ Property pc = pvb.getChild(element, var.property.types);
+ if (pc == null)
+ throw new DefinitionException("Unable to find a definition for "+pvb.getDefinition().getPath()+"."+element);
+
+ // the profile structure definition (derived)
+ StructureDefinition sd = var.getProperty().profileProperty.getStructure();
+ ElementDefinition ednew = sd.getDifferential().addElement();
+ ednew.setPath(var.getProperty().profileProperty.getDefinition().getPath()+"."+pc.getName());
+ ednew.setUserData("slice-name", sliceName);
+ ednew.setFixed(fixed);
+ for (ProfiledType pt : type.getProfiledTypes()) {
+ if (pt.hasBindings())
+ ednew.setBinding(pt.getBindings().get(0));
+ if (pt.getUri().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
+ String t = pt.getUri().substring(40);
+ t = checkType(t, pc, pt.getProfiles());
+ if (t != null) {
+ if (pt.hasProfiles()) {
+ for (String p : pt.getProfiles())
+ if (t.equals("Reference"))
+ ednew.addType().setCode(t).setTargetProfile(p);
+ else
+ ednew.addType().setCode(t).setProfile(p);
+ } else
+ ednew.addType().setCode(t);
+ }
+ }
+ }
+
+ return new PropertyWithType(var.property.path+"."+element, pc, new Property(worker, ednew, sd), type);
+ }
+ }
+
+
+
+ private String checkType(String t, Property pvb, List profiles) throws FHIRException {
+ if (pvb.getDefinition().getType().size() == 1 && isCompatibleType(t, pvb.getDefinition().getType().get(0).getCode()) && profilesMatch(profiles, pvb.getDefinition().getType().get(0).getProfile()))
+ return null;
+ for (TypeRefComponent tr : pvb.getDefinition().getType()) {
+ if (isCompatibleType(t, tr.getCode()))
+ return tr.getCode(); // note what is returned - the base type, not the inferred mapping type
+ }
+ throw new FHIRException("The type "+t+" is not compatible with the allowed types for "+pvb.getDefinition().getPath()+" ("+pvb.getDefinition().typeSummary()+")");
+ }
+
+ private boolean profilesMatch(List profiles, String profile) {
+ return profiles == null || profiles.size() == 0 || (profiles.size() == 1 && profiles.get(0).equals(profile));
+ }
+
+ private boolean isCompatibleType(String t, String code) {
+ if (t.equals(code))
+ return true;
+ if (t.equals("string")) {
+ StructureDefinition sd = worker.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code);
+ if (sd != null && sd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/string"))
+ return true;
+ }
+ return false;
+ }
+
+ private TypeDetails analyseTransform(TransformContext context, StructureMap map, StructureMapGroupRuleTargetComponent tgt, VariableForProfiling var, VariablesForProfiling vars) throws FHIRException {
+ switch (tgt.getTransform()) {
+ case CREATE :
+ String p = getParamString(vars, tgt.getParameter().get(0));
+ return new TypeDetails(CollectionStatus.SINGLETON, p);
+ case COPY :
+ return getParam(vars, tgt.getParameter().get(0));
+ case EVALUATE :
+ ExpressionNode expr = (ExpressionNode) tgt.getUserData(MAP_EXPRESSION);
+ if (expr == null) {
+ expr = fpe.parse(getParamString(vars, tgt.getParameter().get(tgt.getParameter().size()-1)));
+ tgt.setUserData(MAP_WHERE_EXPRESSION, expr);
+ }
+ return fpe.check(vars, null, expr);
+
+////case TRUNCATE :
+//// String src = getParamString(vars, tgt.getParameter().get(0));
+//// String len = getParamString(vars, tgt.getParameter().get(1));
+//// if (Utilities.isInteger(len)) {
+//// int l = Integer.parseInt(len);
+//// if (src.length() > l)
+//// src = src.substring(0, l);
+//// }
+//// return new StringType(src);
+////case ESCAPE :
+//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
+////case CAST :
+//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
+////case APPEND :
+//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
+ case TRANSLATE :
+ return new TypeDetails(CollectionStatus.SINGLETON, "CodeableConcept");
+ case CC:
+ ProfiledType res = new ProfiledType("CodeableConcept");
+ if (tgt.getParameter().size() >= 2 && isParamId(vars, tgt.getParameter().get(1))) {
+ TypeDetails td = vars.get(null, getParamId(vars, tgt.getParameter().get(1))).property.types;
+ if (td != null && td.hasBinding())
+ // todo: do we need to check that there's no implicit translation her? I don't think we do...
+ res.addBinding(td.getBinding());
+ }
+ return new TypeDetails(CollectionStatus.SINGLETON, res);
+ case C:
+ return new TypeDetails(CollectionStatus.SINGLETON, "Coding");
+ case QTY:
+ return new TypeDetails(CollectionStatus.SINGLETON, "Quantity");
+ case REFERENCE :
+ VariableForProfiling vrs = vars.get(VariableMode.OUTPUT, getParamId(vars, tgt.getParameterFirstRep()));
+ if (vrs == null)
+ throw new FHIRException("Unable to resolve variable \""+getParamId(vars, tgt.getParameterFirstRep())+"\"");
+ String profile = vrs.property.getProfileProperty().getStructure().getUrl();
+ TypeDetails td = new TypeDetails(CollectionStatus.SINGLETON);
+ td.addType("Reference", profile);
+ return td;
+////case DATEOP :
+//// throw new Error("Transform "+tgt.getTransform().toCode()+" not supported yet");
+////case UUID :
+//// return new IdType(UUID.randomUUID().toString());
+////case POINTER :
+//// Base b = getParam(vars, tgt.getParameter().get(0));
+//// if (b instanceof Resource)
+//// return new UriType("urn:uuid:"+((Resource) b).getId());
+//// else
+//// throw new FHIRException("Transform engine cannot point at an element of type "+b.fhirType());
+ default:
+ throw new Error("Transform Unknown or not handled yet: "+tgt.getTransform().toCode());
+ }
+ }
+ private String getParamString(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
+ Type p = parameter.getValue();
+ if (p == null || p instanceof IdType)
+ return null;
+ if (!p.hasPrimitiveValue())
+ return null;
+ return p.primitiveValue();
+ }
+
+ private String getParamId(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
+ Type p = parameter.getValue();
+ if (p == null || !(p instanceof IdType))
+ return null;
+ return p.primitiveValue();
+ }
+
+ private boolean isParamId(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) {
+ Type p = parameter.getValue();
+ if (p == null || !(p instanceof IdType))
+ return false;
+ return vars.get(null, p.primitiveValue()) != null;
+ }
+
+ private TypeDetails getParam(VariablesForProfiling vars, StructureMapGroupRuleTargetParameterComponent parameter) throws DefinitionException {
+ Type p = parameter.getValue();
+ if (!(p instanceof IdType))
+ return new TypeDetails(CollectionStatus.SINGLETON, "http://hl7.org/fhir/StructureDefinition/"+p.fhirType());
+ else {
+ String n = ((IdType) p).asStringValue();
+ VariableForProfiling b = vars.get(VariableMode.INPUT, n);
+ if (b == null)
+ b = vars.get(VariableMode.OUTPUT, n);
+ if (b == null)
+ throw new DefinitionException("Variable "+n+" not found ("+vars.summary()+")");
+ return b.getProperty().getTypes();
+ }
+ }
+
+ private PropertyWithType createProfile(StructureMap map, List profiles, PropertyWithType prop, String sliceName, Base ctxt) throws DefinitionException {
+ if (prop.getBaseProperty().getDefinition().getPath().contains("."))
+ throw new DefinitionException("Unable to process entry point");
+
+ String type = prop.getBaseProperty().getDefinition().getPath();
+ String suffix = "";
+ if (ids.containsKey(type)) {
+ int id = ids.get(type);
+ id++;
+ ids.put(type, id);
+ suffix = "-"+Integer.toString(id);
+ } else
+ ids.put(type, 0);
+
+ StructureDefinition profile = new StructureDefinition();
+ profiles.add(profile);
+ profile.setDerivation(TypeDerivationRule.CONSTRAINT);
+ profile.setType(type);
+ profile.setBaseDefinition(prop.getBaseProperty().getStructure().getUrl());
+ profile.setName("Profile for "+profile.getType()+" for "+sliceName);
+ profile.setUrl(map.getUrl().replace("StructureMap", "StructureDefinition")+"-"+profile.getType()+suffix);
+ ctxt.setUserData("profile", profile.getUrl()); // then we can easily assign this profile url for validation later when we actually transform
+ profile.setId(map.getId()+"-"+profile.getType()+suffix);
+ profile.setStatus(map.getStatus());
+ profile.setExperimental(map.getExperimental());
+ profile.setDescription("Generated automatically from the mapping by the Java Reference Implementation");
+ for (ContactDetail c : map.getContact()) {
+ ContactDetail p = profile.addContact();
+ p.setName(c.getName());
+ for (ContactPoint cc : c.getTelecom())
+ p.addTelecom(cc);
+ }
+ profile.setDate(map.getDate());
+ profile.setCopyright(map.getCopyright());
+ profile.setFhirVersion(Constants.VERSION);
+ profile.setKind(prop.getBaseProperty().getStructure().getKind());
+ profile.setAbstract(false);
+ ElementDefinition ed = profile.getDifferential().addElement();
+ ed.setPath(profile.getType());
+ prop.profileProperty = new Property(worker, ed, profile);
+ return prop;
+ }
+
+ private PropertyWithType resolveType(StructureMap map, String type, StructureMapInputMode mode) throws Exception {
+ for (StructureMapStructureComponent imp : map.getStructure()) {
+ if ((imp.getMode() == StructureMapModelMode.SOURCE && mode == StructureMapInputMode.SOURCE) ||
+ (imp.getMode() == StructureMapModelMode.TARGET && mode == StructureMapInputMode.TARGET)) {
+ StructureDefinition sd = worker.fetchResource(StructureDefinition.class, imp.getUrl());
+ if (sd == null)
+ throw new Exception("Import "+imp.getUrl()+" cannot be resolved");
+ if (sd.getId().equals(type)) {
+ return new PropertyWithType(sd.getType(), new Property(worker, sd.getSnapshot().getElement().get(0), sd), null, new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()));
+ }
+ }
+ }
+ throw new Exception("Unable to find structure definition for "+type+" in imports");
+ }
+
+
+ public StructureMap generateMapFromMappings(StructureDefinition sd) throws IOException, FHIRException {
+ String id = getLogicalMappingId(sd);
+ if (id == null)
+ return null;
+ String prefix = ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_MAPPING_PREFIX);
+ String suffix = ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_MAPPING_SUFFIX);
+ if (prefix == null || suffix == null)
+ return null;
+ // we build this by text. Any element that has a mapping, we put it's mappings inside it....
+ StringBuilder b = new StringBuilder();
+ b.append(prefix);
+
+ ElementDefinition root = sd.getSnapshot().getElementFirstRep();
+ String m = getMapping(root, id);
+ if (m != null)
+ b.append(m+"\r\n");
+ addChildMappings(b, id, "", sd, root, false);
+ b.append("\r\n");
+ b.append(suffix);
+ b.append("\r\n");
+ StructureMap map = parse(b.toString());
+ map.setId(tail(map.getUrl()));
+ if (!map.hasStatus())
+ map.setStatus(PublicationStatus.DRAFT);
+ map.getText().setStatus(NarrativeStatus.GENERATED);
+ map.getText().setDiv(new XhtmlNode(NodeType.Element, "div"));
+ map.getText().getDiv().addTag("pre").addText(render(map));
+ return map;
+ }
+
+
+ private String tail(String url) {
+ return url.substring(url.lastIndexOf("/")+1);
+ }
+
+
+ private void addChildMappings(StringBuilder b, String id, String indent, StructureDefinition sd, ElementDefinition ed, boolean inner) throws DefinitionException {
+ boolean first = true;
+ List children = ProfileUtilities.getChildMap(sd, ed);
+ for (ElementDefinition child : children) {
+ if (first && inner) {
+ b.append(" then {\r\n");
+ first = false;
+ }
+ String map = getMapping(child, id);
+ if (map != null) {
+ b.append(indent+" "+child.getPath()+": "+map);
+ addChildMappings(b, id, indent+" ", sd, child, true);
+ b.append("\r\n");
+ }
+ }
+ if (!first && inner)
+ b.append(indent+"}");
+
+ }
+
+
+ private String getMapping(ElementDefinition ed, String id) {
+ for (ElementDefinitionMappingComponent map : ed.getMapping())
+ if (id.equals(map.getIdentity()))
+ return map.getMap();
+ return null;
+ }
+
+
+ private String getLogicalMappingId(StructureDefinition sd) {
+ String id = null;
+ for (StructureDefinitionMappingComponent map : sd.getMapping()) {
+ if ("http://hl7.org/fhir/logical".equals(map.getUri()))
+ return map.getIdentity();
+ }
+ return null;
+ }
+
+}
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
index 8fd30f66f53..59265c38c73 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
@@ -11,6 +11,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
+import com.helger.commons.io.stream.StringInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.*;
@@ -29,7 +30,6 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.base.Charsets;
-import com.phloc.commons.io.streams.StringInputStream;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml
index b4175479294..0c0a87fdbb1 100644
--- a/hapi-fhir-structures-hl7org-dstu2/pom.xml
+++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml
@@ -122,8 +122,8 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
@@ -133,8 +133,8 @@
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml
index 3d0e3e37049..e6e9eca0d9f 100644
--- a/hapi-fhir-structures-r4/pom.xml
+++ b/hapi-fhir-structures-r4/pom.xml
@@ -125,13 +125,13 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
@@ -192,11 +192,6 @@
spring-webtest
-
- org.springframework
- spring-context
- test
-
diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java
index feee569839c..1ed5df4b552 100644
--- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java
+++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/context/BaseWorkerContext.java
@@ -6,13 +6,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@@ -250,7 +244,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return laterVersion(newParts[i], oldParts[i]);
}
// This should never happen
- throw new Error("Delimited versions have exact match for delimiter '"+delimiter+"' : "+newParts+" vs "+oldParts);
+ throw new Error("Delimited versions have exact match for delimiter '"+delimiter+"' : "+ Arrays.asList(newParts)+" vs "+Arrays.asList(oldParts));
}
protected void seeMetadataResource(T r, Map map, boolean addId) throws FHIRException {
@@ -1045,6 +1039,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return (T) maps.get(uri);
if (transforms.containsKey(uri))
return (T) transforms.get(uri);
+ if (questionnaires.containsKey(uri))
+ return (T) questionnaires.get(uri);
return null;
} else if (class_ == StructureDefinition.class) {
return (T) structures.get(uri);
@@ -1052,6 +1048,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return (T) valueSets.get(uri);
} else if (class_ == CodeSystem.class) {
return (T) codeSystems.get(uri);
+ } else if (class_ == ConceptMap.class) {
+ return (T) maps.get(uri);
} else if (class_ == OperationDefinition.class) {
OperationDefinition od = operations.get(uri);
return (T) od;
@@ -1069,7 +1067,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
}
}
if (class_ == Questionnaire.class)
- return null;
+ return (T) questionnaires.get(uri);
if (class_ == null) {
if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet"))
return null;
diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
index ef4f05d3e69..80e2f7400bd 100644
--- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
+++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
@@ -1,10 +1,11 @@
package org.hl7.fhir.r4.utils;
//import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
-
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.util.ElementUtil;
+
import org.apache.commons.lang3.NotImplementedException;
+import org.apache.http.protocol.ExecutionContext;
import org.fhir.ucum.Decimal;
import org.fhir.ucum.Pair;
import org.fhir.ucum.UcumException;
@@ -24,8 +25,13 @@ import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.utilities.Utilities;
import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.*;
+import static org.apache.commons.lang3.StringUtils.length;
+
/**
*
* @author Grahame Grieve
@@ -731,7 +737,7 @@ public class FHIRPathEngine {
String s = lexer.take();
if (s.equals("year") || s.equals("years"))
ucum = "a";
- else if (s.equals("month") || s.equals("month"))
+ else if (s.equals("month") || s.equals("months"))
ucum = "mo";
else if (s.equals("week") || s.equals("weeks"))
ucum = "wk";
@@ -2072,8 +2078,9 @@ public class FHIRPathEngine {
result.add(item);
} else
getChildrenByName(item, exp.getName(), result);
- // todo: GG 1st April 201 - why do this?
if (result.size() == 0 && atEntry && context.appInfo != null) {
+ // well, we didn't get a match on the name - we'll see if the name matches a constant known by the context.
+ // (if the name does match, and the user wants to get the constant value, they'll have to try harder...
Base temp = hostServices.resolveConstant(context.appInfo, exp.getName());
if (temp != null) {
result.add(temp);
@@ -3280,7 +3287,7 @@ public class FHIRPathEngine {
return Quantity.fromUcum(v, s.substring(1, s.length()-1));
if (s.equals("year") || s.equals("years"))
return Quantity.fromUcum(v, "a");
- else if (s.equals("month") || s.equals("month"))
+ else if (s.equals("month") || s.equals("months"))
return Quantity.fromUcum(v, "mo");
else if (s.equals("week") || s.equals("weeks"))
return Quantity.fromUcum(v, "wk");
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4Test.java
index 7a4b587456b..18876a64970 100644
--- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/GenericClientR4Test.java
@@ -17,6 +17,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
+import com.helger.commons.io.stream.StringInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.*;
@@ -36,7 +37,6 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.base.Charsets;
-import com.phloc.commons.io.streams.StringInputStream;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
@@ -93,7 +93,7 @@ public class GenericClientR4Test {
return body;
}
- private ArgumentCaptor prepareClientForSearchResponse() throws IOException, ClientProtocolException {
+ private ArgumentCaptor prepareClientForSearchResponse() throws IOException {
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@@ -102,7 +102,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -143,7 +143,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -187,7 +187,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -234,7 +234,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -278,7 +278,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -322,7 +322,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -366,7 +366,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -408,7 +408,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -472,7 +472,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -504,14 +504,14 @@ public class GenericClientR4Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
@@ -553,14 +553,14 @@ public class GenericClientR4Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
@@ -592,7 +592,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -619,7 +619,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -664,7 +664,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -704,7 +704,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -740,7 +740,7 @@ public class GenericClientR4Test {
private int myCount = 0;
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
final String respString;
if (myCount == 1 || myCount == 2) {
ourLog.info("Encoding patient");
@@ -788,7 +788,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
final String respString;
if (myAnswerCount >= 1) {
ourLog.info("Encoding patient");
@@ -828,7 +828,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public StringInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public StringInputStream answer(InvocationOnMock theInvocation) {
return new StringInputStream("HELLO", Charsets.UTF_8);
}
});
@@ -853,7 +853,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public StringInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public StringInputStream answer(InvocationOnMock theInvocation) {
return new StringInputStream("not implemented", Charsets.UTF_8);
}
});
@@ -884,7 +884,7 @@ public class GenericClientR4Test {
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value((String) null))
.and(Patient.BIRTHDATE.exactly().day((Date) null))
- .and(Patient.GENDER.exactly().code((String) null))
+ .and(Patient.GENDER.exactly().code(null))
.and(Patient.ORGANIZATION.hasId((String) null))
.returnBundle(Bundle.class)
.execute();
@@ -915,7 +915,7 @@ public class GenericClientR4Test {
}
@Test
- public void testPatchInvalid() throws Exception {
+ public void testPatchInvalid() {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
try {
@@ -941,7 +941,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -980,7 +980,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1019,7 +1019,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1057,7 +1057,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1095,7 +1095,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1133,7 +1133,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1180,7 +1180,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
}
});
@@ -1225,7 +1225,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
}
});
@@ -1282,7 +1282,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1313,7 +1313,7 @@ public class GenericClientR4Test {
// when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1331,7 +1331,7 @@ public class GenericClientR4Test {
}
@Test
- public void testRevIncludeRecursive() throws ClientProtocolException, IOException {
+ public void testRevIncludeRecursive() throws IOException {
ArgumentCaptor capt = prepareClientForSearchResponse();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@@ -1361,7 +1361,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1468,7 +1468,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1578,7 +1578,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1670,7 +1670,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1756,7 +1756,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer() {
@Override
- public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public InputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
}
});
@@ -1793,7 +1793,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1830,7 +1830,7 @@ public class GenericClientR4Test {
}
@Test
- public void testTransactionWithInvalidBody() throws Exception {
+ public void testTransactionWithInvalidBody() {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
// Transaction
@@ -1881,7 +1881,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -1920,14 +1920,14 @@ public class GenericClientR4Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
@@ -1968,14 +1968,14 @@ public class GenericClientR4Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
@@ -2013,7 +2013,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -2050,7 +2050,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -2071,7 +2071,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -2099,14 +2099,14 @@ public class GenericClientR4Test {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer() {
@Override
- public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ public Header[] answer(InvocationOnMock theInvocation) {
return new Header[] {};
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
}
});
@@ -2139,7 +2139,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
@@ -2177,7 +2177,7 @@ public class GenericClientR4Test {
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
@Override
- public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ public ReaderInputStream answer(InvocationOnMock theInvocation) {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
diff --git a/hapi-fhir-tutorial/skeleton-project/pom.xml b/hapi-fhir-tutorial/skeleton-project/pom.xml
index 050113b9d87..164f5cc4200 100644
--- a/hapi-fhir-tutorial/skeleton-project/pom.xml
+++ b/hapi-fhir-tutorial/skeleton-project/pom.xml
@@ -65,8 +65,8 @@
Schematron resource validation. Otherwise, they may be omitted.
-->
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematronSaxon-HE
@@ -75,8 +75,8 @@
- com.phloc
- phloc-commons
+ com.helger
+ ph-commons4.3.3
@@ -123,4 +123,4 @@
-
\ No newline at end of file
+
diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml
index bc8351450de..01d6f30eb26 100644
--- a/hapi-fhir-validation/pom.xml
+++ b/hapi-fhir-validation/pom.xml
@@ -173,13 +173,13 @@
test
- com.phloc
- phloc-schematron
+ com.helger
+ ph-schematrontest
- com.phloc
- phloc-commons
+ com.helger
+ ph-commonstest
diff --git a/osgi/hapi-fhir-karaf-features/src/main/resources/features.xml b/osgi/hapi-fhir-karaf-features/src/main/resources/features.xml
index 6c576fe2b1c..449ad9db4f7 100644
--- a/osgi/hapi-fhir-karaf-features/src/main/resources/features.xml
+++ b/osgi/hapi-fhir-karaf-features/src/main/resources/features.xml
@@ -89,9 +89,14 @@
mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xmlresolver/${servicemix_xmlresolver_version}
+ mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/${aries_spifly_version}mvn:com.google.code.findbugs/jsr305/${jsr305_version}
- mvn:com.phloc/phloc-schematron/${phloc_schematron_version}
- mvn:com.phloc/phloc-commons/${phloc_commons_version}
+ mvn:com.helger/ph-commons/${ph_commons_version}
+ mvn:com.helger/ph-collection/${ph_commons_version}
+ mvn:com.helger/ph-xml/${ph_commons_version}
+ mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jaxb-impl/${jaxb_bundle_version}
+ mvn:com.helger/ph-jaxb/${ph_commons_version}
+ mvn:com.helger/ph-schematron/${ph_schematron_version}
diff --git a/osgi/hapi-fhir-karaf-integration-tests/pom.xml b/osgi/hapi-fhir-karaf-integration-tests/pom.xml
index b569623d79c..7cbd6f1ddc8 100644
--- a/osgi/hapi-fhir-karaf-integration-tests/pom.xml
+++ b/osgi/hapi-fhir-karaf-integration-tests/pom.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+4.0.0
@@ -98,6 +98,7 @@
${project.version}
+
org.ops4j.pax.exam
@@ -122,6 +123,12 @@
${apache_karaf_version}testtar.gz
+
+
+ org.ops4j.pax.logging
+ pax-logging-api
+
+ org.apache.karaf.shell
diff --git a/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/ValidationConstants.java b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/ValidationConstants.java
new file mode 100644
index 00000000000..7278e4d80bd
--- /dev/null
+++ b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/ValidationConstants.java
@@ -0,0 +1,7 @@
+package ca.uhn.fhir.tests.integration.karaf;
+
+public class ValidationConstants {
+
+ public static final boolean SCHEMATRON_ENABLED = true;
+
+}
diff --git a/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2/Dstu2ResourceValidatorDstu2Test.java b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2/Dstu2ResourceValidatorDstu2Test.java
index ff2cc52a836..6b296b30bb5 100644
--- a/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2/Dstu2ResourceValidatorDstu2Test.java
+++ b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2/Dstu2ResourceValidatorDstu2Test.java
@@ -26,6 +26,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.tests.integration.karaf.ValidationConstants;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.SchemaBaseValidator;
import ca.uhn.fhir.validation.ValidationFailureException;
@@ -85,7 +86,7 @@ public class Dstu2ResourceValidatorDstu2Test {
private FhirValidator createFhirValidator() {
FhirValidator val = ourCtx.newValidator();
val.setValidateAgainstStandardSchema(true);
- val.setValidateAgainstStandardSchematron(true);
+ val.setValidateAgainstStandardSchematron(ValidationConstants.SCHEMATRON_ENABLED);
return val;
}
@@ -156,7 +157,9 @@ public class Dstu2ResourceValidatorDstu2Test {
FhirValidator val = ourCtx.newValidator();
val.setValidateAgainstStandardSchema(true);
- val.setValidateAgainstStandardSchematron(false);
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.validate(p);
@@ -246,7 +249,9 @@ public class Dstu2ResourceValidatorDstu2Test {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
ValidationResult result = val.validateWithResult(messageString);
@@ -299,7 +304,9 @@ public class Dstu2ResourceValidatorDstu2Test {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
ValidationResult result = val.validateWithResult(messageString);
diff --git a/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java
index d0918aa628f..356ef1b1d3a 100644
--- a/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java
+++ b/osgi/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java
@@ -6,19 +6,12 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.tests.integration.karaf.ValidationConstants;
import ca.uhn.fhir.validation.*;
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.Validate;
import org.hamcrest.core.StringContains;
-import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
-import org.hl7.fhir.dstu3.context.IWorkerContext;
-import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
-import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.*;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
@@ -33,14 +26,10 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
-import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_VALIDATION_DSTU3;
-import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
-import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.CoreOptions.*;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
/**
@@ -145,7 +134,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult output = val.validateWithResult(p);
@@ -162,7 +153,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult output = val.validateWithResult(p);
@@ -232,7 +225,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(q);
@@ -273,7 +268,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(encoded);
@@ -321,7 +318,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(messageString);
@@ -374,7 +373,9 @@ public class ResourceValidatorDstu3FeatureTest {
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
- val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ if (ValidationConstants.SCHEMATRON_ENABLED) {
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ }
val.registerValidatorModule(new FhirInstanceValidator());
ValidationResult result = val.validateWithResult(messageString);
diff --git a/pom.xml b/pom.xml
index 17638a99c7b..584b36a5aeb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,20 @@
jitpack.iohttps://jitpack.io
+
+ false
+
+
@@ -444,16 +457,17 @@
4.1.4
+ 1.0.102.6.21.102.53.6
- 10.14.1.0
+ 10.14.2.02.0.1823.02.8.12.3.0
- 2.3.0
+ 2.3.02.25.19.4.8.v201711213.0.2
@@ -464,11 +478,12 @@
5.7.1.Final4.4.64.5.3
+ 2.2.11_15.5.42.5.31.8
- 2.7.1
- 4.4.11
+ 5.0.4
+ 9.1.19.5.1-5_11.2_55.0.3.RELEASE
@@ -572,14 +587,14 @@
23.0
- com.phloc
- phloc-schematron
- ${phloc_schematron_version}
+ com.helger
+ ph-schematron
+ ${ph_schematron_version}
- com.phloc
- phloc-commons
- ${phloc_commons_version}
+ com.helger
+ ph-commons
+ ${ph_commons_version}com.squareup.okhttp3
@@ -601,6 +616,11 @@
commons-codec${commons_codec_version}
+
+ org.apache.commons
+ commons-collections4
+ 4.1
+ org.apache.commonscommons-compress
@@ -658,16 +678,6 @@
gson${gson_version}
-
- com.sun.xml.bind
- jaxb-core
- ${jaxb_core_version}
-
-
- com.sun.xml.bind
- jaxb-impl
- ${jaxb_core_version}
- javax.mailjavax.mail-api
@@ -952,6 +962,11 @@
javax.json1.0.4
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ ${jaxb_runtime_version}
+ org.glassfish.jersey.corejersey-server
@@ -1182,6 +1197,11 @@
maven-antrun-plugin1.8
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 3.1.0
+ org.apache.maven.pluginsmaven-compiler-plugin
@@ -1251,7 +1271,7 @@
org.apache.maven.pluginsmaven-failsafe-plugin
- 2.19.1
+ 2.20org.apache.maven.plugins
@@ -1278,7 +1298,7 @@
org.apache.maven.pluginsmaven-surefire-plugin
- 2.20.1
+ 2.21.0truerandom
@@ -1509,24 +1529,29 @@
org.apache.maven.pluginsmaven-enforcer-plugin
+ 1.4.1enforce-javaenforce
-
-
-
- [1.8,)
-
- The hapi-fhir Maven build requires JDK version 1.8 or higher.
-
-
-
-
+
+
+
+ 3.5
+
+
+
+ 1.8
+
+ The hapi-fhir Maven build requires JDK version 1.8.x.
+
+
+
+ org.codehaus.mojo
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b63c37f738b..d6bf91ee1c1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -162,6 +162,11 @@
an unspecified error was thrown by the JPA server. An HTTP 409
(Conflict) with an informative error message is now thrown.
+
+ A bug in the JPA server's DSTU2 transaction processing routine caused it
+ to occasionally consume two database connections, which could lead to deadlocks
+ under heavy load. This has been fixed.
+
diff --git a/src/site/xdoc/doc_cli.xml b/src/site/xdoc/doc_cli.xml
index 8eccb4afe17..fb1a811bc3f 100644
--- a/src/site/xdoc/doc_cli.xml
+++ b/src/site/xdoc/doc_cli.xml
@@ -1,135 +1,146 @@
-
-
-
-
- Command Line Tool
- James Agnew
-
-
-
-
-
-
- hapi-fhir-cli is the HAPI FHIR Command Line tool. It features a number of HAPI's
- built-in features as easy to use command line options.
-
-
-
-
- You can get the tool by downloading it from our
- GitHub Releases page
- (look for the archive named hapi-fhir-[version]-cli.tar.bz2 on OSX/Linux or hapi-fhir-[version]-cli.zip on Windows).
-
-
- When you have downloaded the archive (either ZIP or tar.bz2), expand it into a directory
- where you will keep it, and add this directory to your path.
-
-
- You can now try the tool out by executing the following command: hapi-fhir-cli
-
-
- This command should show a help screen, as shown in the screeenshot below.
-
-
-
-
-
-
- Note on Java version support: The HAPI library is designed to
- work in Java 6+, but the Command Line Tool required a minimum of Java 8. This
- is because the Jetty Web Server that is used within the tool has this requirement.
-
-
- The tool should work correctly on any system that has Java 8 (or newer) installed. If
- it is not working correctly, first try the following command to test if Java is installed:
- $ java -version
-
-
- If this command does not produce output similar to the following, you should install/reinstall
- Java.
-
-
-
- If this does not help, please post a question on our
- Google Group.
-
-
-
-
-
-
-
- The CLI tool can be used to start a local, fully functional FHIR server which you can use
- for testing. To start this server, simply issue the command hapi-fhir-cli run-server
- as shown in the example below:
-
- Note that by default this server will not be populated with any resources at all. You can
- easily populate it with the FHIR example resources by leaving it running and opening
- a second terminal window, then using the hapi-fhir-cli upload-examples command
- (see the section below).
-
-
- The server uses a local Derby database instance for storage. You may want to execute
- this command in an empty directory, which you can clear if you want to reset the server.
-
-
-
-
-
- The upload-examples command downloads the complete set of FHIR example resources from
- the HL7 website, and uploads them to a server of your choice. This can be useful to
- populate a server with test data.
-
-
- To execute this command, uploading test resources to a local CLI server, issue
- the following: hapi-fhir-cli upload-examples -t http://localhost:8080/baseDstu2
-
-
- Note that this command may take a surprisingly long time to complete because of the
- large number of examples.
-
-
-
-
-
-
- The HAPI FHIR JPA server has a terminology server, and has the ability to
- be populated with "external" code systems. These code systems are systems
- that contain large numbers of codes, so the codes are not stored directly
- inside the resource body.
-
-
- HAPI has methods for uploading several popular code systems into its tables
- using the distribution files produced by the respective code systems. This
- is done using the upload-terminology command. The following
- examples show how to do this for several popular code systems.
-
-
- Note that the path and exact filename of the terminology files will likely
- need to be adjusted for your local disk structure.
-
+ hapi-fhir-cli is the HAPI FHIR Command Line tool. It features a number of HAPI's
+ built-in features as easy to use command line options.
+
+
+
+
+ You can get the tool by downloading it from our
+ GitHub Releases page
+ (look for the archive named hapi-fhir-[version]-cli.tar.bz2 on OSX/Linux or hapi-fhir-[version]-cli.zip on Windows).
+
+
+ When you have downloaded the archive (either ZIP or tar.bz2), expand it into a directory
+ where you will keep it, and add this directory to your path.
+
+
+ You can now try the tool out by executing the following command: hapi-fhir-cli
+
+
+ This command should show a help screen, as shown in the screeenshot below.
+
+
+
+
+
+
+
+ hapi-fhir-cli is available as a Homebrew package
+ for Mac. It can be installed using the following command:
+
+ brew install hapi-fhir-cli
+
+
+
+
+
+
+ Note on Java version support: The HAPI library is designed to
+ work in Java 6+, but the Command Line Tool required a minimum of Java 8. This
+ is because the Jetty Web Server that is used within the tool has this requirement.
+
+
+ The tool should work correctly on any system that has Java 8 (or newer) installed. If
+ it is not working correctly, first try the following command to test if Java is installed:
+ $ java -version
+
+
+ If this command does not produce output similar to the following, you should install/reinstall
+ Java.
+
+
+
+ If this does not help, please post a question on our
+ Google Group.
+
+
+
+
+
+
+
+ The CLI tool can be used to start a local, fully functional FHIR server which you can use
+ for testing. To start this server, simply issue the command hapi-fhir-cli run-server
+ as shown in the example below:
+
+ Note that by default this server will not be populated with any resources at all. You can
+ easily populate it with the FHIR example resources by leaving it running and opening
+ a second terminal window, then using the hapi-fhir-cli upload-examples command
+ (see the section below).
+
+
+ The server uses a local Derby database instance for storage. You may want to execute
+ this command in an empty directory, which you can clear if you want to reset the server.
+
+
+
+
+
+ The upload-examples command downloads the complete set of FHIR example resources from
+ the HL7 website, and uploads them to a server of your choice. This can be useful to
+ populate a server with test data.
+
+
+ To execute this command, uploading test resources to a local CLI server, issue
+ the following: hapi-fhir-cli upload-examples -t http://localhost:8080/baseDstu2
+
+
+ Note that this command may take a surprisingly long time to complete because of the
+ large number of examples.
+
+
+
+
+
+
+ The HAPI FHIR JPA server has a terminology server, and has the ability to
+ be populated with "external" code systems. These code systems are systems
+ that contain large numbers of codes, so the codes are not stored directly
+ inside the resource body.
+
+
+ HAPI has methods for uploading several popular code systems into its tables
+ using the distribution files produced by the respective code systems. This
+ is done using the upload-terminology command. The following
+ examples show how to do this for several popular code systems.
+
+
+ Note that the path and exact filename of the terminology files will likely
+ need to be adjusted for your local disk structure.
+
- HAPI supportes two types of validation, both of which are described in the
- sections below.
-
-
-
- Parser Validation
- is validation at runtime during the parsing
- of a resource. It can be used to catch input data that is impossible to
- fit into the HAPI data model. For
- example, it can be used to throw exceptions
- or display error messages if a resource being parsed contains elements for which
- there are no appropriate fields in a HAPI data structure. This is useful in order to ensure
- that no data is being lost during parsing, but is less comprehensive than resource validation
- against raw text data.
-
-
- Resource Validation
- is validation of the raw or parsed resource against
- the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet)
- as well as against custom profiles which have been developed.
-
-
-
-
-
-
-
-
-
- Parser validation is controlled by calling
- setParserErrorHandler(IParserErrorHandler)
- on
- either the FhirContext or on individual parser instances. This method
- takes an
- IParserErrorHandler
- , which is a callback that
- will be invoked any time a parse issue is detected.
-
-
- There are two implementations of
- IParserErrorHandler
- worth
- mentioning. You can also supply your own implementation if you want.
-
-
-
- LenientErrorHandler
- logs any errors but does not abort parsing. By default this handler is used, and it
- logs errors at "warning" level. It can also be configured to silently
- ignore issues.
-
-
- StrictErrorHandler
- throws a
- DataFormatException
- if any errors are detected.
-
-
-
-
- The following example shows how to configure a parser to use strict validation.
-
-
-
-
-
-
-
- You can also configure the error handler at the FhirContext level, which is useful
- for clients.
-
-
-
-
-
-
-
- FhirContext level validators can also be useful on servers.
-
-
-
-
-
-
-
-
-
-
-
-
-
- HAPI provides a built-in and configurable mechanism for validating resources.
- This mechanism is called the
- Resource Validator.
-
-
- The resource validator is an extendible and modular system, and you
- can configure it in a number of ways in order to get the specific
- type of validation you want to achieve.
-
-
-
- The validator can be manually invoked at any time by creating a
- validator and configuring it with one or more
- IValidatorModule
- instances.
-
-
-
-
-
-
-
-
-
-
-
-
- FHIR resource definitions are distributed with a set of XML schema files (XSD)
- as well as a set of XML Schematron (SCH) files. These two sets of files are
- complimentary to each other, meaning that in order to claim compliance to the
- FHIR specification, your resources must validate against both sets.
-
-
- The two sets of files are included with HAPI, and it uses them to perform
- validation.
-
-
-
-
-
- In order to use HAPI's Schematron support, a libaray called
- Phloc-Schematron
- is used, so this library must be added to your classpath (or Maven POM file, Gradle
- file, etc.)
- Note that this library is specified as an optional dependency
- by HAPI FHIR
- so you need to explicitly include it if you want to use this
- functionality.
-
-
- See
- Downloads
- for more information on how
- to add it.
-
-
-
-
-
-
- To validate a resource instance, a new validator instance is requested
- from the FHIR Context. This validator is then applied against
- a specific resource
- instance, as shown in the example below.
-
-
-
-
-
-
-
-
-
-
-
- The following example shows how to load a set of resources from files
- on disk and validate each one.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- HAPI also supports validation against StructureDefinition
- resources. This functionality uses the HL7 "InstanceValidator", which is able
- to check a resource for conformance to FHIR profiles
- (StructureDefinitions, ValueSets, and CodeSystems),
- including validating fields, extensions, and codes for conformance to their given ValueSets.
-
-
- StructureDefinition validation can be used to validate a resource against the
- official structure definitions (produced by HL7) as well as against custom
- definitions provided either by HL7 or by the user.
-
-
-
- The instance validator is experimental in the DSTU2 mode, but has become very stable
- and full-featured in DSTU3 mode. Use with caution when validating DSTU2 resources using
- instance validator.
-
-
-
-
-
- To use this functionality, you must add the following two dependencies
- to your classpath (or Maven POM file, Gradle file, etc.):
-
-
-
-
- hapi-fhir-structures-hl7org-dstu2
- : This file contains the "reference implementation"
- structures and tooling. You need to include it even if you are not using the RI model
- (the StructureDefinition validation will work against HAPI structures as well)
-
-
- hapi-fhir-validation-resources-dstu2
- : This file contains the official FHIR
- StructureDefinition files, and the ValueSets needed to support them.
-
- To execute the validator, you simply create an instance of
- FhirInstanceValidator
- and register it to new validator, as shown in the example below.
-
-
-
- Note that the example below uses the official FHIR StructureDefintions and ValueSets
- to validate the resource. It will not work unless you include the
- hapi-fhir-validation-resources-[version].jar to your classpath.
-
-
-
-
-
-
-
-
-
-
-
-
- The FhirInstanceValidator relies on the
- IValidationSupport
- interface to load StructureDefinitions, and validate codes.
-
-
- By default, the
- DefaultProfileValidationSupport
- implementation is used. This implementation loads the FHIR profiles from the
- validator resources JAR. If you want to use your own profiles, you may wish to
- supply your own implementation.
-
+ HAPI supportes two types of validation, both of which are described in the
+ sections below.
+
+
+
+ Parser Validation
+ is validation at runtime during the parsing
+ of a resource. It can be used to catch input data that is impossible to
+ fit into the HAPI data model. For
+ example, it can be used to throw exceptions
+ or display error messages if a resource being parsed contains elements for which
+ there are no appropriate fields in a HAPI data structure. This is useful in order to ensure
+ that no data is being lost during parsing, but is less comprehensive than resource validation
+ against raw text data.
+
+
+ Resource Validation
+ is validation of the raw or parsed resource against
+ the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet)
+ as well as against custom profiles which have been developed.
+
+
+
+
+
+
+
+
+
+ Parser validation is controlled by calling
+ setParserErrorHandler(IParserErrorHandler)
+ on
+ either the FhirContext or on individual parser instances. This method
+ takes an
+ IParserErrorHandler
+ , which is a callback that
+ will be invoked any time a parse issue is detected.
+
+
+ There are two implementations of
+ IParserErrorHandler
+ worth
+ mentioning. You can also supply your own implementation if you want.
+
+
+
+ LenientErrorHandler
+ logs any errors but does not abort parsing. By default this handler is used, and it
+ logs errors at "warning" level. It can also be configured to silently
+ ignore issues.
+
+
+ StrictErrorHandler
+ throws a
+ DataFormatException
+ if any errors are detected.
+
+
+
+
+ The following example shows how to configure a parser to use strict validation.
+
+
+
+
+
+
+
+ You can also configure the error handler at the FhirContext level, which is useful
+ for clients.
+
+
+
+
+
+
+
+ FhirContext level validators can also be useful on servers.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HAPI provides a built-in and configurable mechanism for validating resources.
+ This mechanism is called the
+ Resource Validator.
+
+
+ The resource validator is an extendible and modular system, and you
+ can configure it in a number of ways in order to get the specific
+ type of validation you want to achieve.
+
+
+
+ The validator can be manually invoked at any time by creating a
+ validator and configuring it with one or more
+ IValidatorModule
+ instances.
+
+
+
+
+
+
+
+
+
+
+
+
+ FHIR resource definitions are distributed with a set of XML schema files (XSD)
+ as well as a set of XML Schematron (SCH) files. These two sets of files are
+ complimentary to each other, meaning that in order to claim compliance to the
+ FHIR specification, your resources must validate against both sets.
+
+
+ The two sets of files are included with HAPI, and it uses them to perform
+ validation.
+
+
+
+
+
+ In order to use HAPI's Schematron support, a libaray called
+ Ph-Schematron
+ is used, so this library must be added to your classpath (or Maven POM file, Gradle
+ file, etc.)
+ Note that this library is specified as an optional dependency
+ by HAPI FHIR
+ so you need to explicitly include it if you want to use this
+ functionality.
+
+
+ See
+ Downloads
+ for more information on how
+ to add it.
+
+
+
+
+
+
+ To validate a resource instance, a new validator instance is requested
+ from the FHIR Context. This validator is then applied against
+ a specific resource
+ instance, as shown in the example below.
+
+
+
+
+
+
+
+
+
+
+
+ The following example shows how to load a set of resources from files
+ on disk and validate each one.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HAPI also supports validation against StructureDefinition
+ resources. This functionality uses the HL7 "InstanceValidator", which is able
+ to check a resource for conformance to FHIR profiles
+ (StructureDefinitions, ValueSets, and CodeSystems),
+ including validating fields, extensions, and codes for conformance to their given ValueSets.
+
+
+ StructureDefinition validation can be used to validate a resource against the
+ official structure definitions (produced by HL7) as well as against custom
+ definitions provided either by HL7 or by the user.
+
+
+
+ The instance validator is experimental in the DSTU2 mode, but has become very stable
+ and full-featured in DSTU3 mode. Use with caution when validating DSTU2 resources using
+ instance validator.
+
+
+
+
+
+ To use this functionality, you must add the following two dependencies
+ to your classpath (or Maven POM file, Gradle file, etc.):
+
+
+
+
+ hapi-fhir-structures-hl7org-dstu2
+ : This file contains the "reference implementation"
+ structures and tooling. You need to include it even if you are not using the RI model
+ (the StructureDefinition validation will work against HAPI structures as well)
+
+
+ hapi-fhir-validation-resources-dstu2
+ : This file contains the official FHIR
+ StructureDefinition files, and the ValueSets needed to support them.
+
+ To execute the validator, you simply create an instance of
+ FhirInstanceValidator
+ and register it to new validator, as shown in the example below.
+
+
+
+ Note that the example below uses the official FHIR StructureDefintions and ValueSets
+ to validate the resource. It will not work unless you include the
+ hapi-fhir-validation-resources-[version].jar to your classpath.
+
+
+
+
+
+
+
+
+
+
+
+
+ The FhirInstanceValidator relies on the
+ IValidationSupport
+ interface to load StructureDefinitions, and validate codes.
+
+
+ By default, the
+ DefaultProfileValidationSupport
+ implementation is used. This implementation loads the FHIR profiles from the
+ validator resources JAR. If you want to use your own profiles, you may wish to
+ supply your own implementation.
+
If you are using the
Schematron Validatioon
- module, you will also need to include the Phloc library on your
- classpath.
+ module, you will also need to include the Ph-Schematron library on your
+ classpath. (Note that prior to HAPI FHIR 3.4.0 we used Phloc-Schamtron
+ instead, but that lirary has been discontinued)
If you are using Maven, this library is not added by default (it is
@@ -556,16 +557,15 @@ System.setProperty("javax.xml.stream.XMLEventFactory", "com.ctc.wstx.stax.WstxEv
dependencies to your project POM.xml
-
diff --git a/tests/hapi-fhir-base-test-mindeps-server/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java b/tests/hapi-fhir-base-test-mindeps-server/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java
index 17248c1a681..387aac67e46 100644
--- a/tests/hapi-fhir-base-test-mindeps-server/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java
+++ b/tests/hapi-fhir-base-test-mindeps-server/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java
@@ -26,7 +26,7 @@ public class ValidatorTest {
assertEquals("This parser is for FHIR version DSTU2 - Can not encode a structure for version DSTU3", e.getMessage());
}
- // Phloc is not onthe classpath
+ // Ph-Schematron is not onthe classpath
assertTrue(val.isValidateAgainstStandardSchema());
assertFalse(val.isValidateAgainstStandardSchematron());
@@ -34,7 +34,7 @@ public class ValidatorTest {
val.setValidateAgainstStandardSchematron(true);
fail();
} catch (IllegalArgumentException e) {
- assertEquals("Phloc-schematron library not found on classpath, can not enable perform schematron validation", e.getMessage());
+ assertEquals("Ph-schematron library not found on classpath, can not enable perform schematron validation", e.getMessage());
}
}