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