001/*- 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.interceptor.model; 021 022import ca.uhn.fhir.model.api.IModelJson; 023import ca.uhn.fhir.util.JsonUtil; 024import com.fasterxml.jackson.annotation.JsonProperty; 025import com.fasterxml.jackson.core.JsonProcessingException; 026import com.fasterxml.jackson.databind.ObjectMapper; 027import jakarta.annotation.Nonnull; 028import jakarta.annotation.Nullable; 029import org.apache.commons.lang3.Validate; 030import org.apache.commons.lang3.builder.EqualsBuilder; 031import org.apache.commons.lang3.builder.HashCodeBuilder; 032import org.apache.commons.lang3.builder.ToStringBuilder; 033import org.apache.commons.lang3.builder.ToStringStyle; 034 035import java.time.LocalDate; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.Collection; 039import java.util.Collections; 040import java.util.List; 041import java.util.Objects; 042import java.util.stream.Collectors; 043 044import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; 045 046/** 047 * @since 5.0.0 048 */ 049public class RequestPartitionId implements IModelJson { 050 private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId(); 051 private static final ObjectMapper ourObjectMapper = 052 new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); 053 054 @JsonProperty("partitionDate") 055 private final LocalDate myPartitionDate; 056 057 @JsonProperty("allPartitions") 058 private final boolean myAllPartitions; 059 060 @JsonProperty("partitionIds") 061 private final List<Integer> myPartitionIds; 062 063 @JsonProperty("partitionNames") 064 private final List<String> myPartitionNames; 065 066 /** 067 * Constructor for a single partition 068 */ 069 private RequestPartitionId( 070 @Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { 071 myPartitionIds = toListOrNull(thePartitionId); 072 myPartitionNames = toListOrNull(thePartitionName); 073 myPartitionDate = thePartitionDate; 074 myAllPartitions = false; 075 } 076 077 /** 078 * Constructor for a multiple partition 079 */ 080 private RequestPartitionId( 081 @Nullable List<String> thePartitionName, 082 @Nullable List<Integer> thePartitionId, 083 @Nullable LocalDate thePartitionDate) { 084 myPartitionIds = toListOrNull(thePartitionId); 085 myPartitionNames = toListOrNull(thePartitionName); 086 myPartitionDate = thePartitionDate; 087 myAllPartitions = false; 088 } 089 090 /** 091 * Constructor for all partitions 092 */ 093 private RequestPartitionId() { 094 super(); 095 myPartitionDate = null; 096 myPartitionNames = null; 097 myPartitionIds = null; 098 myAllPartitions = true; 099 } 100 101 public static RequestPartitionId fromJson(String theJson) throws JsonProcessingException { 102 return ourObjectMapper.readValue(theJson, RequestPartitionId.class); 103 } 104 105 public boolean isAllPartitions() { 106 return myAllPartitions; 107 } 108 109 public boolean isPartitionCovered(Integer thePartitionId) { 110 return isAllPartitions() || getPartitionIds().contains(thePartitionId); 111 } 112 113 @Nullable 114 public LocalDate getPartitionDate() { 115 return myPartitionDate; 116 } 117 118 @Nullable 119 public List<String> getPartitionNames() { 120 return myPartitionNames; 121 } 122 123 @Nonnull 124 public List<Integer> getPartitionIds() { 125 Validate.notNull(myPartitionIds, "Partition IDs have not been set"); 126 return myPartitionIds; 127 } 128 129 @Override 130 public String toString() { 131 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 132 if (hasPartitionIds()) { 133 b.append("ids", getPartitionIds()); 134 } 135 if (hasPartitionNames()) { 136 b.append("names", getPartitionNames()); 137 } 138 if (myAllPartitions) { 139 b.append("allPartitions", myAllPartitions); 140 } 141 return b.build(); 142 } 143 144 @Override 145 public boolean equals(Object theO) { 146 if (this == theO) { 147 return true; 148 } 149 150 if (theO == null || getClass() != theO.getClass()) { 151 return false; 152 } 153 154 RequestPartitionId that = (RequestPartitionId) theO; 155 156 EqualsBuilder b = new EqualsBuilder(); 157 b.append(myAllPartitions, that.myAllPartitions); 158 b.append(myPartitionDate, that.myPartitionDate); 159 b.append(myPartitionIds, that.myPartitionIds); 160 b.append(myPartitionNames, that.myPartitionNames); 161 return b.isEquals(); 162 } 163 164 @Override 165 public int hashCode() { 166 return new HashCodeBuilder(17, 37) 167 .append(myPartitionDate) 168 .append(myAllPartitions) 169 .append(myPartitionIds) 170 .append(myPartitionNames) 171 .toHashCode(); 172 } 173 174 public String toJson() { 175 return JsonUtil.serializeOrInvalidRequest(this); 176 } 177 178 @Nullable 179 public Integer getFirstPartitionIdOrNull() { 180 if (myPartitionIds != null) { 181 return myPartitionIds.get(0); 182 } 183 return null; 184 } 185 186 public String getFirstPartitionNameOrNull() { 187 if (myPartitionNames != null) { 188 return myPartitionNames.get(0); 189 } 190 return null; 191 } 192 193 /** 194 * Returns true if this request partition contains only one partition ID and it is the DEFAULT partition ID (null) 195 */ 196 public boolean isDefaultPartition() { 197 if (isAllPartitions()) { 198 return false; 199 } 200 return hasPartitionIds() 201 && getPartitionIds().size() == 1 202 && getPartitionIds().get(0) == null; 203 } 204 205 public boolean hasPartitionId(Integer thePartitionId) { 206 Validate.notNull(myPartitionIds, "Partition IDs not set"); 207 return myPartitionIds.contains(thePartitionId); 208 } 209 210 public boolean hasPartitionIds() { 211 return myPartitionIds != null; 212 } 213 214 public boolean hasPartitionNames() { 215 return myPartitionNames != null; 216 } 217 218 public boolean hasDefaultPartitionId() { 219 return getPartitionIds().contains(null); 220 } 221 222 public List<Integer> getPartitionIdsWithoutDefault() { 223 return getPartitionIds().stream().filter(Objects::nonNull).collect(Collectors.toList()); 224 } 225 226 @Nullable 227 private static <T> List<T> toListOrNull(@Nullable Collection<T> theList) { 228 if (theList != null) { 229 if (theList.size() == 1) { 230 return Collections.singletonList(theList.iterator().next()); 231 } 232 return Collections.unmodifiableList(new ArrayList<>(theList)); 233 } 234 return null; 235 } 236 237 @Nullable 238 private static <T> List<T> toListOrNull(@Nullable T theObject) { 239 if (theObject != null) { 240 return Collections.singletonList(theObject); 241 } 242 return null; 243 } 244 245 @SafeVarargs 246 @Nullable 247 private static <T> List<T> toListOrNull(@Nullable T... theObject) { 248 if (theObject != null) { 249 return Arrays.asList(theObject); 250 } 251 return null; 252 } 253 254 @Nonnull 255 public static RequestPartitionId allPartitions() { 256 return ALL_PARTITIONS; 257 } 258 259 @Nonnull 260 public static RequestPartitionId defaultPartition() { 261 return fromPartitionIds(Collections.singletonList(null)); 262 } 263 264 @Nonnull 265 public static RequestPartitionId defaultPartition(@Nullable LocalDate thePartitionDate) { 266 return fromPartitionIds(Collections.singletonList(null), thePartitionDate); 267 } 268 269 @Nonnull 270 public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) { 271 return fromPartitionIds(Collections.singletonList(thePartitionId)); 272 } 273 274 @Nonnull 275 public static RequestPartitionId fromPartitionId( 276 @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { 277 return new RequestPartitionId(null, Collections.singletonList(thePartitionId), thePartitionDate); 278 } 279 280 @Nonnull 281 public static RequestPartitionId fromPartitionIds(@Nonnull Collection<Integer> thePartitionIds) { 282 return fromPartitionIds(thePartitionIds, null); 283 } 284 285 @Nonnull 286 public static RequestPartitionId fromPartitionIds( 287 @Nonnull Collection<Integer> thePartitionIds, @Nullable LocalDate thePartitionDate) { 288 return new RequestPartitionId(null, toListOrNull(thePartitionIds), thePartitionDate); 289 } 290 291 @Nonnull 292 public static RequestPartitionId fromPartitionIds(Integer... thePartitionIds) { 293 return new RequestPartitionId(null, toListOrNull(thePartitionIds), null); 294 } 295 296 @Nonnull 297 public static RequestPartitionId fromPartitionName(@Nullable String thePartitionName) { 298 return fromPartitionName(thePartitionName, null); 299 } 300 301 @Nonnull 302 public static RequestPartitionId fromPartitionName( 303 @Nullable String thePartitionName, @Nullable LocalDate thePartitionDate) { 304 return new RequestPartitionId(thePartitionName, null, thePartitionDate); 305 } 306 307 @Nonnull 308 public static RequestPartitionId fromPartitionNames(@Nullable List<String> thePartitionNames) { 309 return new RequestPartitionId(toListOrNull(thePartitionNames), null, null); 310 } 311 312 @Nonnull 313 public static RequestPartitionId fromPartitionNames(String... thePartitionNames) { 314 return new RequestPartitionId(toListOrNull(thePartitionNames), null, null); 315 } 316 317 @Nonnull 318 public static RequestPartitionId fromPartitionIdAndName( 319 @Nullable Integer thePartitionId, @Nullable String thePartitionName) { 320 return new RequestPartitionId(thePartitionName, thePartitionId, null); 321 } 322 323 @Nonnull 324 public static RequestPartitionId forPartitionIdAndName( 325 @Nullable Integer thePartitionId, @Nullable String thePartitionName, @Nullable LocalDate thePartitionDate) { 326 return new RequestPartitionId(thePartitionName, thePartitionId, thePartitionDate); 327 } 328 329 @Nonnull 330 public static RequestPartitionId forPartitionIdsAndNames( 331 List<String> thePartitionNames, List<Integer> thePartitionIds, LocalDate thePartitionDate) { 332 return new RequestPartitionId(thePartitionNames, thePartitionIds, thePartitionDate); 333 } 334 335 /** 336 * Create a string representation suitable for use as a cache key. Null aware. 337 * <p> 338 * Returns the partition IDs (numeric) as a joined string with a space between, using the string "null" for any null values 339 */ 340 public static String stringifyForKey(@Nonnull RequestPartitionId theRequestPartitionId) { 341 String retVal = "(all)"; 342 if (!theRequestPartitionId.isAllPartitions()) { 343 assert theRequestPartitionId.hasPartitionIds(); 344 retVal = theRequestPartitionId.getPartitionIds().stream() 345 .map(t -> defaultIfNull(t, "null").toString()) 346 .collect(Collectors.joining(" ")); 347 } 348 return retVal; 349 } 350 351 public String asJson() throws JsonProcessingException { 352 return ourObjectMapper.writeValueAsString(this); 353 } 354}