001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.api.model.predicate; 015 016import com.fasterxml.jackson.annotation.JsonIgnore; 017 018import org.gbif.api.annotation.Experimental; 019import org.gbif.api.model.common.search.SearchParameter; 020import org.gbif.api.util.SearchTypeValidator; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Objects; 026import java.util.Optional; 027import java.util.StringJoiner; 028 029import jakarta.annotation.Nullable; 030import jakarta.validation.constraints.NotNull; 031import jakarta.validation.constraints.Size; 032 033import com.fasterxml.jackson.annotation.JsonCreator; 034import com.fasterxml.jackson.annotation.JsonProperty; 035 036import io.swagger.v3.oas.annotations.media.Schema; 037 038import static org.gbif.api.util.PreconditionUtils.checkArgument; 039 040/** 041 * This predicate checks if its {@code key} contains any of its {@code values}. 042 */ 043@Schema( 044 description = "This predicate checks if its `key` is equal to any of its `values`." 045) 046public class InPredicate<S extends SearchParameter> implements Predicate { 047 048 @Schema( 049 description = "The search parameter to test." 050 ) 051 @NotNull private final S key; 052 053 @Schema( 054 description = "The list of value to test for." 055 ) 056 @NotNull 057 @Size(min = 1) 058 private final Collection<String> values; 059 060 @Schema( 061 description = "Whether to match letter case (UPPER or lower case) on string value comparisons." 062 ) 063 @Experimental 064 @Nullable 065 private final Boolean matchCase; 066 067 @Schema( 068 description = "Specify which taxonomy to use." 069 ) 070 @Experimental 071 @Nullable 072 private final String checklistKey; 073 074 public InPredicate( 075 S key, 076 Collection<String> values, 077 Boolean matchCase 078 ) { 079 this(key, values, matchCase, null); 080 } 081 082 @JsonCreator 083 public InPredicate( 084 @JsonProperty("key") S key, 085 @JsonProperty("values") Collection<String> values, 086 @Nullable @JsonProperty(value = "matchCase") Boolean matchCase, 087 @Nullable @JsonProperty(value = "checklistKey") String checklistKey 088 ) { 089 this.matchCase = matchCase; 090 this.checklistKey = checklistKey; 091 Objects.requireNonNull(key, "<key> may not be null"); 092 Objects.requireNonNull(values, "<values> may not be null"); 093 checkArgument(!values.isEmpty(), "<values> may not be empty"); 094 // make sure the value is of the right type according to the key given 095 for (String value : values) { 096 if (value == null) { 097 throw new NullPointerException(); 098 } 099 SearchTypeValidator.validate(key, value); 100 } 101 102 this.key = key; 103 this.values = Collections.unmodifiableList(new ArrayList<>(values)); 104 } 105 106 public S getKey() { 107 return key; 108 } 109 110 public Collection<String> getValues() { 111 return values; 112 } 113 114 /** 115 * This flag enables the use of case-sensitive matches and aggregations on certain search parameters. 116 * <p> 117 * Fields that support this feature are: occurrenceId, recordedBy, samplingProtocol, catalogNumber, collectionCode, 118 * institutionCode, eventId, parentEventId, waterBody, stateProvince, recordNumber, identifiedBy, organismId and locality. 119 * <p> 120 * This is an experimental feature and its implementation map change or be removed at any time. 121 */ 122 @Experimental 123 @JsonIgnore 124 public Boolean isMatchCase() { 125 return Optional.ofNullable(matchCase).orElse(Boolean.FALSE); 126 } 127 128 @JsonProperty("matchCase") 129 public Boolean getMatchCase() { 130 return matchCase; 131 } 132 133 @Experimental 134 public String getChecklistKey() { 135 return checklistKey; 136 } 137 138 @Override 139 public boolean equals(Object o) { 140 if (this == o) { 141 return true; 142 } 143 if (o == null || getClass() != o.getClass()) { 144 return false; 145 } 146 InPredicate<S> that = (InPredicate<S>) o; 147 return key == that.key 148 && Objects.equals(values, that.values) 149 && matchCase == that.matchCase 150 && Objects.equals(checklistKey, that.checklistKey); 151 } 152 153 @Override 154 public int hashCode() { 155 return Objects.hash(key, values, matchCase, checklistKey); 156 } 157 158 @Override 159 public String toString() { 160 return new StringJoiner(", ", InPredicate.class.getSimpleName() + "[", "]") 161 .add("key=" + key) 162 .add("values=" + values) 163 .add("matchCase=" + matchCase) 164 .add("checklistKey=" + checklistKey) 165 .toString(); 166 } 167}