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