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}