package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.measure.quantity.Quantity;
import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.base.composite.BaseHumanNameDt;
import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContactDt;
import ca.uhn.fhir.model.dstu.composite.DurationDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implements ISearchParamExtractor {
@Override
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
return Collections.emptySet();
}
@Override
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null) {
continue;
}
ResourceIndexedSearchParamDate nextEntity;
if (nextObject instanceof BaseDateTimeDt) {
BaseDateTimeDt nextValue = (BaseDateTimeDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
} else if (nextObject instanceof PeriodDt) {
PeriodDt nextValue = (PeriodDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart().getValue(), nextValue.getEnd().getValue());
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
return retVal;
}
@Override
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<ResourceIndexedSearchParamNumber>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof DurationDt) {
DurationDt nextValue = (DurationDt) nextObject;
if (nextValue.getValue().isEmpty()) {
continue;
}
if (new UriDt(BaseHapiFhirDao.UCUM_NS).equals(nextValue.getSystem())) {
if (isNotBlank(nextValue.getCode().getValue())) {
Unit<? extends Quantity> unit = Unit.valueOf(nextValue.getCode().getValue());
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
double dayValue = dayConverter.convert(nextValue.getValue().getValue().doubleValue());
DurationDt newValue = new DurationDt();
newValue.setSystem(BaseHapiFhirDao.UCUM_NS);
newValue.setCode(NonSI.DAY.toString());
newValue.setValue(dayValue);
nextValue = newValue;
/*
* @SuppressWarnings("unchecked") PhysicsUnit<? extends
* org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends
* org.unitsofmeasurement.quantity.Quantity<?>>)
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if
* (unit.isCompatible(UCUM.DAY)) {
*
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit
* = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt
* newValue = new DurationDt(); newValue.setSystem(UCUM_NS);
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue;
* }
*/
}
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
if (nextValue.getValue().isEmpty()) {
continue;
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else if (nextObject instanceof IntegerDt) {
IntegerDt nextValue = (IntegerDt) nextObject;
if (nextValue.getValue() == null) {
continue;
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
@Override
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<ResourceIndexedSearchParamQuantity>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
if (nextValue.getValue().isEmpty()) {
continue;
}
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
@Override
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<ResourceIndexedSearchParamString>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
// TODO: implement phoenetic, and any others that have no path
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
String searchTerm = nextValue.getValueAsString();
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (nextObject instanceof BaseHumanNameDt) {
ArrayList<StringDt> allNames = new ArrayList<StringDt>();
HumanNameDt nextHumanName = (HumanNameDt) nextObject;
allNames.addAll(nextHumanName.getFamily());
allNames.addAll(nextHumanName.getGiven());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof AddressDt) {
ArrayList<StringDt> allNames = new ArrayList<StringDt>();
AddressDt nextAddress = (AddressDt) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCity());
allNames.add(nextAddress.getState());
allNames.add(nextAddress.getCountry());
allNames.add(nextAddress.getZip());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else if (nextObject instanceof ContactDt) {
ContactDt nextContact = (ContactDt) nextObject;
if (nextContact.getValue().isEmpty() == false) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
}
}
}
}
}
return retVal;
}
@Override
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<String> systems = new ArrayList<String>();
List<String> codes = new ArrayList<String>();
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject instanceof IdentifierDt) {
IdentifierDt nextValue = (IdentifierDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
systems.add(nextValue.getSystem().getValueAsString());
codes.add(nextValue.getValue().getValue());
} else if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
if (nextValue.isEmpty()) {
continue;
}
systems.add(null);
codes.add(nextValue.getValueAsString());
} else if (nextObject instanceof CodeableConceptDt) {
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
if (!nextCC.getText().isEmpty()) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(nextCC.getText().getValue()), nextCC.getText().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
for (CodingDt nextCoding : nextCC.getCoding()) {
if (nextCoding.isEmpty()) {
continue;
}
String nextSystem = nextCoding.getSystem().getValueAsString();
String nextCode = nextCoding.getCode().getValue();
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
systems.add(nextSystem);
codes.add(nextCode);
}
if (!nextCoding.getDisplay().isEmpty()) {
systems.add(null);
codes.add(nextCoding.getDisplay().getValue());
}
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
Set<Pair<String, String>> haveValues = new HashSet<Pair<String, String>>();
for (int i = 0; i < systems.size(); i++) {
String system = systems.get(i);
String code = codes.get(i);
if (isBlank(system) && isBlank(code)) {
continue;
}
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
}
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
}
Pair<String, String> nextPair = Pair.of(system, code);
if (haveValues.contains(nextPair)) {
continue;
}
haveValues.add(nextPair);
ResourceIndexedSearchParamToken nextEntity;
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
return retVal;
}
@Override
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
return Collections.emptySet();
}
}