001/*- 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2023 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 org.apache.commons.lang3.Validate; 028import org.apache.commons.lang3.builder.EqualsBuilder; 029import org.apache.commons.lang3.builder.HashCodeBuilder; 030import org.apache.commons.lang3.builder.ToStringBuilder; 031import org.apache.commons.lang3.builder.ToStringStyle; 032 033import javax.annotation.Nonnull; 034import javax.annotation.Nullable; 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.stream.Collectors; 042 043import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; 044 045/** 046 * @since 5.0.0 047 */ 048public class RequestPartitionId implements IModelJson { 049 private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId(); 050 private static final ObjectMapper ourObjectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); 051 @JsonProperty("partitionDate") 052 private final LocalDate myPartitionDate; 053 @JsonProperty("allPartitions") 054 private final boolean myAllPartitions; 055 @JsonProperty("partitionIds") 056 private final List<Integer> myPartitionIds; 057 @JsonProperty("partitionNames") 058 private final List<String> myPartitionNames; 059 060 /** 061 * Constructor for a single partition 062 */ 063 private RequestPartitionId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { 064 myPartitionIds = toListOrNull(thePartitionId); 065 myPartitionNames = toListOrNull(thePartitionName); 066 myPartitionDate = thePartitionDate; 067 myAllPartitions = false; 068 } 069 070 /** 071 * Constructor for a multiple partition 072 */ 073 private RequestPartitionId(@Nullable List<String> thePartitionName, @Nullable List<Integer> thePartitionId, @Nullable LocalDate thePartitionDate) { 074 myPartitionIds = toListOrNull(thePartitionId); 075 myPartitionNames = toListOrNull(thePartitionName); 076 myPartitionDate = thePartitionDate; 077 myAllPartitions = false; 078 } 079 080 /** 081 * Constructor for all partitions 082 */ 083 private RequestPartitionId() { 084 super(); 085 myPartitionDate = null; 086 myPartitionNames = null; 087 myPartitionIds = null; 088 myAllPartitions = true; 089 } 090 091 public static RequestPartitionId fromJson(String theJson) throws JsonProcessingException { 092 return ourObjectMapper.readValue(theJson, RequestPartitionId.class); 093 } 094 095 public boolean isAllPartitions() { 096 return myAllPartitions; 097 } 098 099 @Nullable 100 public LocalDate getPartitionDate() { 101 return myPartitionDate; 102 } 103 104 @Nullable 105 public List<String> getPartitionNames() { 106 return myPartitionNames; 107 } 108 109 @Nonnull 110 public List<Integer> getPartitionIds() { 111 Validate.notNull(myPartitionIds, "Partition IDs have not been set"); 112 return myPartitionIds; 113 } 114 115 @Override 116 public String toString() { 117 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 118 if (hasPartitionIds()) { 119 b.append("ids", getPartitionIds()); 120 } 121 if (hasPartitionNames()) { 122 b.append("names", getPartitionNames()); 123 } 124 return b.build(); 125 } 126 127 @Override 128 public boolean equals(Object theO) { 129 if (this == theO) { 130 return true; 131 } 132 133 if (theO == null || getClass() != theO.getClass()) { 134 return false; 135 } 136 137 RequestPartitionId that = (RequestPartitionId) theO; 138 139 EqualsBuilder b = new EqualsBuilder(); 140 b.append(myAllPartitions, that.myAllPartitions); 141 b.append(myPartitionDate, that.myPartitionDate); 142 b.append(myPartitionIds, that.myPartitionIds); 143 b.append(myPartitionNames, that.myPartitionNames); 144 return b.isEquals(); 145 } 146 147 @Override 148 public int hashCode() { 149 return new HashCodeBuilder(17, 37) 150 .append(myPartitionDate) 151 .append(myAllPartitions) 152 .append(myPartitionIds) 153 .append(myPartitionNames) 154 .toHashCode(); 155 } 156 157 public String toJson() { 158 return JsonUtil.serializeOrInvalidRequest(this); 159 } 160 161 @Nullable 162 public Integer getFirstPartitionIdOrNull() { 163 if (myPartitionIds != null) { 164 return myPartitionIds.get(0); 165 } 166 return null; 167 } 168 169 public String getFirstPartitionNameOrNull() { 170 if (myPartitionNames != null) { 171 return myPartitionNames.get(0); 172 } 173 return null; 174 } 175 176 /** 177 * Returns true if this request partition contains only one partition ID and it is the DEFAULT partition ID (null) 178 */ 179 public boolean isDefaultPartition() { 180 if (isAllPartitions()) { 181 return false; 182 } 183 return hasPartitionIds() && getPartitionIds().size() == 1 && getPartitionIds().get(0) == null; 184 } 185 186 public boolean hasPartitionId(Integer thePartitionId) { 187 Validate.notNull(myPartitionIds, "Partition IDs not set"); 188 return myPartitionIds.contains(thePartitionId); 189 } 190 191 public boolean hasPartitionIds() { 192 return myPartitionIds != null; 193 } 194 195 public boolean hasPartitionNames() { 196 return myPartitionNames != null; 197 } 198 199 public boolean hasDefaultPartitionId() { 200 return getPartitionIds().contains(null); 201 } 202 203 public List<Integer> getPartitionIdsWithoutDefault() { 204 return getPartitionIds().stream().filter(t -> t != null).collect(Collectors.toList()); 205 } 206 207 @Nullable 208 private static <T> List<T> toListOrNull(@Nullable Collection<T> theList) { 209 if (theList != null) { 210 if (theList.size() == 1) { 211 return Collections.singletonList(theList.iterator().next()); 212 } 213 return Collections.unmodifiableList(new ArrayList<>(theList)); 214 } 215 return null; 216 } 217 218 @Nullable 219 private static <T> List<T> toListOrNull(@Nullable T theObject) { 220 if (theObject != null) { 221 return Collections.singletonList(theObject); 222 } 223 return null; 224 } 225 226 @SafeVarargs 227 @Nullable 228 private static <T> List<T> toListOrNull(@Nullable T... theObject) { 229 if (theObject != null) { 230 return Arrays.asList(theObject); 231 } 232 return null; 233 } 234 235 @Nonnull 236 public static RequestPartitionId allPartitions() { 237 return ALL_PARTITIONS; 238 } 239 240 @Nonnull 241 public static RequestPartitionId defaultPartition() { 242 return fromPartitionIds(Collections.singletonList(null)); 243 } 244 245 @Nonnull 246 public static RequestPartitionId defaultPartition(@Nullable LocalDate thePartitionDate) { 247 return fromPartitionIds(Collections.singletonList(null), thePartitionDate); 248 } 249 250 @Nonnull 251 public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) { 252 return fromPartitionIds(Collections.singletonList(thePartitionId)); 253 } 254 255 @Nonnull 256 public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { 257 return new RequestPartitionId(null, Collections.singletonList(thePartitionId), thePartitionDate); 258 } 259 260 @Nonnull 261 public static RequestPartitionId fromPartitionIds(@Nonnull Collection<Integer> thePartitionIds) { 262 return fromPartitionIds(thePartitionIds, null); 263 } 264 265 @Nonnull 266 public static RequestPartitionId fromPartitionIds(@Nonnull Collection<Integer> thePartitionIds, @Nullable LocalDate thePartitionDate) { 267 return new RequestPartitionId(null, toListOrNull(thePartitionIds), thePartitionDate); 268 } 269 270 @Nonnull 271 public static RequestPartitionId fromPartitionIds(Integer... thePartitionIds) { 272 return new RequestPartitionId(null, toListOrNull(thePartitionIds), null); 273 } 274 275 @Nonnull 276 public static RequestPartitionId fromPartitionName(@Nullable String thePartitionName) { 277 return fromPartitionName(thePartitionName, null); 278 } 279 280 @Nonnull 281 public static RequestPartitionId fromPartitionName(@Nullable String thePartitionName, @Nullable LocalDate thePartitionDate) { 282 return new RequestPartitionId(thePartitionName, null, thePartitionDate); 283 } 284 285 @Nonnull 286 public static RequestPartitionId fromPartitionNames(@Nullable List<String> thePartitionNames) { 287 return new RequestPartitionId(toListOrNull(thePartitionNames), null, null); 288 } 289 290 @Nonnull 291 public static RequestPartitionId fromPartitionNames(String... thePartitionNames) { 292 return new RequestPartitionId(toListOrNull(thePartitionNames), null, null); 293 } 294 295 @Nonnull 296 public static RequestPartitionId fromPartitionIdAndName(@Nullable Integer thePartitionId, @Nullable String thePartitionName) { 297 return new RequestPartitionId(thePartitionName, thePartitionId, null); 298 } 299 300 @Nonnull 301 public static RequestPartitionId forPartitionIdAndName(@Nullable Integer thePartitionId, @Nullable String thePartitionName, @Nullable LocalDate thePartitionDate) { 302 return new RequestPartitionId(thePartitionName, thePartitionId, thePartitionDate); 303 } 304 305 @Nonnull 306 public static RequestPartitionId forPartitionIdsAndNames(List<String> thePartitionNames, List<Integer> thePartitionIds, LocalDate thePartitionDate) { 307 return new RequestPartitionId(thePartitionNames, thePartitionIds, thePartitionDate); 308 } 309 310 /** 311 * Create a string representation suitable for use as a cache key. Null aware. 312 * <p> 313 * Returns the partition IDs (numeric) as a joined string with a space between, using the string "null" for any null values 314 */ 315 public static String stringifyForKey(@Nonnull RequestPartitionId theRequestPartitionId) { 316 String retVal = "(all)"; 317 if (!theRequestPartitionId.isAllPartitions()) { 318 assert theRequestPartitionId.hasPartitionIds(); 319 retVal = theRequestPartitionId 320 .getPartitionIds() 321 .stream() 322 .map(t -> defaultIfNull(t, "null").toString()) 323 .collect(Collectors.joining(" ")); 324 } 325 return retVal; 326 } 327 328 public String asJson() throws JsonProcessingException { 329 return ourObjectMapper.writeValueAsString(this); 330 } 331}