Skip to content

Commit

Permalink
Merge branch 'master' into feat/support-head-metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
jkiddo committed Oct 26, 2024
2 parents 734ac3f + ebe140e commit a9995e4
Show file tree
Hide file tree
Showing 480 changed files with 13,070 additions and 23,206 deletions.
2 changes: 1 addition & 1 deletion hapi-deployable-pom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.7.0-SNAPSHOT</version>

<relativePath>../pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-android/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.7.0-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
2 changes: 1 addition & 1 deletion hapi-fhir-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.7.0-SNAPSHOT</version>

<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class BaseRuntimeElementDefinition<T extends IBase> {

private static final Class<Void> VOID_CLASS = Void.class;
private final Class<? extends T> myImplementingClass;
private final String myName;
private final boolean myStandardType;
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<>());
private final Map<Class<?>, Constructor<T>> myConstructors = new ConcurrentHashMap<>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<>();
Expand Down Expand Up @@ -84,27 +85,24 @@ private Constructor<T> getConstructor(@Nullable Object theArgument) {
argumentType = theArgument.getClass();
}

Constructor<T> retVal = myConstructors.get(argumentType);
if (retVal == null) {
Constructor<T> retVal = myConstructors.computeIfAbsent(argumentType, type -> {
for (Constructor<?> next : getImplementingClass().getConstructors()) {
if (argumentType == VOID_CLASS) {
if (type == VOID_CLASS) {
if (next.getParameterTypes().length == 0) {
retVal = (Constructor<T>) next;
break;
}
} else if (next.getParameterTypes().length == 1) {
if (next.getParameterTypes()[0].isAssignableFrom(argumentType)) {
retVal = (Constructor<T>) next;
break;
return (Constructor<T>) next;
}
} else if (next.getParameterTypes().length == 1 && next.getParameterTypes()[0].isAssignableFrom(type)) {
return (Constructor<T>) next;
}
}
if (retVal == null) {
throw new ConfigurationException(Msg.code(1695) + "Class " + getImplementingClass()
+ " has no constructor with a single argument of type " + argumentType);
}
myConstructors.put(argumentType, retVal);
return null;
});

if (retVal == null) {
throw new ConfigurationException(Msg.code(1695) + "Class " + getImplementingClass()
+ " has no constructor with a single argument of type " + argumentType);
}

return retVal;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ public CodeValidationResult setCode(String theCode) {
return this;
}

String getCodeSystemName() {
public String getCodeSystemName() {
return myCodeSystemName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2174,6 +2174,32 @@ public enum Pointcut implements IPointcut {
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"),

/**
* <b>Storage Hook:</b>
* Invoked when a partition has been deleted, typically meaning the <code>$partition-management-delete-partition</code>
* operation has been invoked.
* <p>
* This hook will only be called if
* partitioning is enabled in the JPA server.
* </p>
* <p>
* Hooks may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.interceptor.model.RequestPartitionId - The ID of the partition that was deleted.
* </li>
* </ul>
* <p>
* Hooks must return void.
* </p>
*/
STORAGE_PARTITION_DELETED(
// Return type
void.class,
// Params
"ca.uhn.fhir.interceptor.model.RequestPartitionId"),

/**
* <b>Storage Hook:</b>
* Invoked before any partition aware FHIR operation, when the selected partition has been identified (ie. after the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ public RequestPartitionId mergeIds(RequestPartitionId theOther) {
return RequestPartitionId.allPartitions();
}

// don't know why this is required - otherwise PartitionedStrictTransactionR4Test fails
if (this.equals(theOther)) {
return this;
}

List<Integer> thisPartitionIds = getPartitionIds();
List<Integer> otherPartitionIds = theOther.getPartitionIds();
List<Integer> newPartitionIds = Stream.concat(thisPartitionIds.stream(), otherPartitionIds.stream())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public class Constants {
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BEARER = "Bearer ";
public static final String HEADER_CACHE_CONTROL = "Cache-Control";
public static final String HEADER_CLIENT_TIMEZONE = "Timezone";
public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
public static final String HEADER_CONTENT_LOCATION = "Content-Location";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public interface IRestfulClientFactory {
*/
public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 10000;

/**
* Default value for {@link #getConnectionTimeToLive()}
*/
public static final int DEFAULT_CONNECTION_TTL = 5000;

/**
* Default value for {@link #getServerValidationModeEnum()}
*/
Expand Down Expand Up @@ -75,6 +80,16 @@ public interface IRestfulClientFactory {
*/
int getConnectTimeout();

/**
* Gets the connection time to live, in milliseconds. This is the amount of time to keep connections alive for reuse.
* <p>
* The default value for this setting is defined by {@link #DEFAULT_CONNECTION_TTL}
* </p>
*/
default int getConnectionTimeToLive() {
return DEFAULT_CONNECTION_TTL;
}

/**
* Returns the HTTP client instance. This method will not return null.
* @param theUrl
Expand Down Expand Up @@ -179,6 +194,14 @@ IHttpClient getHttpClient(
*/
void setConnectTimeout(int theConnectTimeout);

/**
* Sets the connection time to live, in milliseconds. This is the amount of time to keep connections alive for reuse.
* <p>
* The default value for this setting is defined by {@link #DEFAULT_CONNECTION_TTL}
* </p>
*/
default void setConnectionTimeToLive(int theConnectionTimeToLive) {}

/**
* Sets the Apache HTTP client instance to be used by any new restful clients created by this factory. If set to
* <code>null</code>, a new HTTP client with default settings will be created.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ public DateRangeParam(DateParam theDateParam) {
theDateParam.setValueAsString(DateUtils.getCompletedDate(theDateParam.getValueAsString())
.getRight());
}
// there is only one value; we will set it as the lower bound
// as a >= operation
validateAndSet(theDateParam, null);
break;
case ENDS_BEFORE:
Expand All @@ -121,6 +123,9 @@ public DateRangeParam(DateParam theDateParam) {
theDateParam.setValueAsString(DateUtils.getCompletedDate(theDateParam.getValueAsString())
.getLeft());
}

// there is only one value; we will set it as the upper bound
// as a <= operation
validateAndSet(null, theDateParam);
break;
default:
Expand Down Expand Up @@ -318,8 +323,8 @@ public Integer getLowerBoundAsDateInteger() {
case NOT_EQUAL:
break;
case LESSTHAN:
case APPROXIMATE:
case LESSTHAN_OR_EQUALS:
case APPROXIMATE:
case ENDS_BEFORE:
throw new IllegalStateException(
Msg.code(1926) + "Invalid lower bound comparator: " + myLowerBound.getPrefix());
Expand Down Expand Up @@ -383,9 +388,9 @@ private static Date getLowerBoundAsInstant(@Nonnull DateParam theLowerBound) {
case NOT_EQUAL:
case GREATERTHAN_OR_EQUALS:
break;
case LESSTHAN_OR_EQUALS:
case LESSTHAN:
case APPROXIMATE:
case LESSTHAN_OR_EQUALS:
case ENDS_BEFORE:
throw new IllegalStateException(
Msg.code(1928) + "Invalid lower bound comparator: " + theLowerBound.getPrefix());
Expand Down Expand Up @@ -470,10 +475,13 @@ public List<DateParam> getValuesAsQueryTokens() {
if (myLowerBound != null && myLowerBound.getMissing() != null) {
retVal.add((myLowerBound));
} else {
if (myLowerBound != null && !myLowerBound.isEmpty()) {
boolean hasLowerBound = myLowerBound != null && !myLowerBound.isEmpty();
boolean hasUpperBound = myUpperBound != null && !myUpperBound.isEmpty();

if (hasLowerBound) {
retVal.add((myLowerBound));
}
if (myUpperBound != null && !myUpperBound.isEmpty()) {
if (hasUpperBound) {
retVal.add((myUpperBound));
}
}
Expand Down
93 changes: 93 additions & 0 deletions hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package ca.uhn.fhir.util;

import ca.uhn.fhir.i18n.Msg;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
Expand All @@ -28,11 +29,20 @@
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TimeZone;

/**
Expand Down Expand Up @@ -93,6 +103,89 @@ public final class DateUtils {
*/
private DateUtils() {}

/**
* Calculate a LocalDateTime with any missing date/time data points defaulting to the earliest values (ex 0 for hour)
* from a TemporalAccessor or empty if it doesn't contain a year.
*
* @param theTemporalAccessor The TemporalAccessor containing date/time information
* @return A LocalDateTime or empty
*/
public static Optional<LocalDateTime> extractLocalDateTimeForRangeStartOrEmpty(
TemporalAccessor theTemporalAccessor) {
if (theTemporalAccessor.isSupported(ChronoField.YEAR)) {
final int year = theTemporalAccessor.get(ChronoField.YEAR);
final Month month = Month.of(getTimeUnitIfSupported(theTemporalAccessor, ChronoField.MONTH_OF_YEAR, 1));
final int day = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.DAY_OF_MONTH, 1);
final int hour = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.HOUR_OF_DAY, 0);
final int minute = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.MINUTE_OF_HOUR, 0);
final int seconds = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.SECOND_OF_MINUTE, 0);

return Optional.of(LocalDateTime.of(year, month, day, hour, minute, seconds));
}

return Optional.empty();
}

/**
* Calculate a LocalDateTime with any missing date/time data points defaulting to the latest values (ex 23 for hour)
* from a TemporalAccessor or empty if it doesn't contain a year.
*
* @param theTemporalAccessor The TemporalAccessor containing date/time information
* @return A LocalDateTime or empty
*/
public static Optional<LocalDateTime> extractLocalDateTimeForRangeEndOrEmpty(TemporalAccessor theTemporalAccessor) {
if (theTemporalAccessor.isSupported(ChronoField.YEAR)) {
final int year = theTemporalAccessor.get(ChronoField.YEAR);
final Month month = Month.of(getTimeUnitIfSupported(theTemporalAccessor, ChronoField.MONTH_OF_YEAR, 12));
final int day = getTimeUnitIfSupported(
theTemporalAccessor,
ChronoField.DAY_OF_MONTH,
YearMonth.of(year, month).atEndOfMonth().getDayOfMonth());
final int hour = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.HOUR_OF_DAY, 23);
final int minute = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.MINUTE_OF_HOUR, 59);
final int seconds = getTimeUnitIfSupported(theTemporalAccessor, ChronoField.SECOND_OF_MINUTE, 59);

return Optional.of(LocalDateTime.of(year, month, day, hour, minute, seconds));
}

return Optional.empty();
}

/**
* With the provided DateTimeFormatter, parse a date time String or return empty if the String doesn't correspond
* to the formatter.
*
* @param theDateTimeString A date/time String in some date format
* @param theSupportedDateTimeFormatter The DateTimeFormatter we expect corresponds to the String
* @return The parsed TemporalAccessor or empty
*/
public static Optional<TemporalAccessor> parseDateTimeStringIfValid(
String theDateTimeString, DateTimeFormatter theSupportedDateTimeFormatter) {
Objects.requireNonNull(theSupportedDateTimeFormatter);
Preconditions.checkArgument(StringUtils.isNotBlank(theDateTimeString));

try {
return Optional.of(theSupportedDateTimeFormatter.parse(theDateTimeString));
} catch (Exception exception) {
return Optional.empty();
}
}

private static int getTimeUnitIfSupported(
TemporalAccessor theTemporalAccessor, TemporalField theTemporalField, int theDefaultValue) {
return getTimeUnitIfSupportedOrEmpty(theTemporalAccessor, theTemporalField)
.orElse(theDefaultValue);
}

private static Optional<Integer> getTimeUnitIfSupportedOrEmpty(
TemporalAccessor theTemporalAccessor, TemporalField theTemporalField) {
if (theTemporalAccessor.isSupported(theTemporalField)) {
return Optional.of(theTemporalAccessor.get(theTemporalField));
}

return Optional.empty();
}

/**
* A factory for {@link SimpleDateFormat}s. The instances are stored in a
* threadlocal way because SimpleDateFormat is not thread safe as noted in
Expand Down
8 changes: 8 additions & 0 deletions hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ public IBase cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissin
+ theTarget.getClass().getName());
}

if (theSource instanceof IBaseReference && theTarget instanceof IBaseReference) {
IBaseReference sourceReference = (IBaseReference) theSource;
IBaseReference targetReference = (IBaseReference) theTarget;
if (sourceReference.getResource() != null) {
targetReference.setResource(sourceReference.getResource());
}
}

BaseRuntimeElementCompositeDefinition<?> sourceDef =
(BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass());
BaseRuntimeElementCompositeDefinition<?> targetDef =
Expand Down
Loading

0 comments on commit a9995e4

Please sign in to comment.