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.collections.lookup; 015 016import java.util.Arrays; 017import java.util.Comparator; 018import java.util.HashSet; 019import java.util.Objects; 020import java.util.Set; 021import java.util.StringJoiner; 022 023/** 024 * Used in the {@link LookupResult} to express how good an entity matches. It also adds some remarks 025 * to help understand how the match was done. 026 * 027 * @param <T> it can be parameterized. To be used mainly with {@link 028 * org.gbif.api.model.collections.Collection} or {@link 029 * org.gbif.api.model.collections.Institution} 030 */ 031public class Match<T extends EntityMatched> { 032 033 public static final Comparator<MatchType> MATCH_TYPE_COMPARATOR = 034 (t1, t2) -> { 035 if (t1 == null) { 036 return t2 == null ? 0 : 1; 037 } else if (t2 == null) { 038 return -1; 039 } 040 041 if (t1 == t2) { 042 return 0; 043 } 044 if (t1 == Match.MatchType.EXACT) { 045 return -1; 046 } 047 if (t2 == Match.MatchType.EXACT) { 048 return 1; 049 } 050 return t1.compareTo(t2); 051 }; 052 053 private MatchType matchType; 054 private Status status; 055 private Set<Reason> reasons; 056 private T entityMatched; 057 058 public static <T extends EntityMatched> Match<T> exact(T entity, Reason... reasons) { 059 return exact(entity, new HashSet<>(Arrays.asList(reasons))); 060 } 061 062 public static <T extends EntityMatched> Match<T> exact(T entity, Set<Reason> reasons) { 063 Match<T> match = new Match<>(); 064 match.setEntityMatched(entity); 065 match.setMatchType(MatchType.EXACT); 066 if (reasons != null) { 067 match.setReasons(reasons); 068 } 069 return match; 070 } 071 072 public static <T extends EntityMatched> Match<T> fuzzy(T entity, Reason... reasons) { 073 return fuzzy(entity, new HashSet<>(Arrays.asList(reasons))); 074 } 075 076 public static <T extends EntityMatched> Match<T> fuzzy(T entity, Set<Reason> reasons) { 077 Match<T> match = new Match<>(); 078 match.setEntityMatched(entity); 079 match.setMatchType(MatchType.FUZZY); 080 if (reasons != null) { 081 match.setReasons(reasons); 082 } 083 return match; 084 } 085 086 public static <T extends EntityMatched> Match<T> none() { 087 Match<T> match = new Match<>(); 088 match.setMatchType(MatchType.NONE); 089 return match; 090 } 091 092 public static <T extends EntityMatched> Match<T> none(Status status) { 093 Match<T> match = new Match<>(); 094 match.setMatchType(MatchType.NONE); 095 match.setStatus(status); 096 return match; 097 } 098 099 public static <T extends EntityMatched> Match<T> explicitMapping(T entity, Reason... reasons) { 100 return explicitMapping(entity, new HashSet<>(Arrays.asList(reasons))); 101 } 102 103 public static <T extends EntityMatched> Match<T> explicitMapping(T entity, Set<Reason> reasons) { 104 Match<T> match = new Match<>(); 105 match.setEntityMatched(entity); 106 match.setMatchType(MatchType.EXPLICIT_MAPPING); 107 if (reasons != null) { 108 match.setReasons(reasons); 109 } 110 return match; 111 } 112 113 public MatchType getMatchType() { 114 return matchType; 115 } 116 117 public void setMatchType(MatchType matchType) { 118 this.matchType = matchType; 119 } 120 121 public Status getStatus() { 122 return status; 123 } 124 125 public void setStatus(Status status) { 126 this.status = status; 127 } 128 129 public Set<Reason> getReasons() { 130 return reasons; 131 } 132 133 public void setReasons(Set<Reason> reasons) { 134 this.reasons = reasons; 135 } 136 137 public Match<T> addReason(Reason reason) { 138 if (reasons == null) { 139 reasons = new HashSet<>(); 140 } 141 reasons.add(reason); 142 return this; 143 } 144 145 public T getEntityMatched() { 146 return entityMatched; 147 } 148 149 public void setEntityMatched(T entityMatched) { 150 this.entityMatched = entityMatched; 151 } 152 153 public enum MatchType { 154 EXACT, 155 FUZZY, 156 EXPLICIT_MAPPING, 157 NONE; 158 } 159 160 public enum Reason { 161 CODE_MATCH, 162 IDENTIFIER_MATCH, 163 ALTERNATIVE_CODE_MATCH, 164 NAME_MATCH, 165 KEY_MATCH, 166 DIFFERENT_OWNER, 167 BELONGS_TO_INSTITUTION_MATCHED, 168 INST_COLL_MISMATCH, 169 COUNTRY_MATCH 170 } 171 172 public enum Status { 173 ACCEPTED, 174 AMBIGUOUS, 175 AMBIGUOUS_EXPLICIT_MAPPINGS, 176 AMBIGUOUS_OWNER, 177 AMBIGUOUS_INSTITUTION_MISMATCH, 178 DOUBTFUL; 179 } 180 181 @Override 182 public boolean equals(Object o) { 183 if (this == o) { 184 return true; 185 } 186 if (o == null || getClass() != o.getClass()) { 187 return false; 188 } 189 Match<?> match = (Match<?>) o; 190 return matchType == match.matchType 191 && status == match.status 192 && Objects.equals(reasons, match.reasons) 193 && Objects.equals(entityMatched, match.entityMatched); 194 } 195 196 @Override 197 public int hashCode() { 198 return Objects.hash(matchType, status, reasons, entityMatched); 199 } 200 201 @Override 202 public String toString() { 203 return new StringJoiner(", ", Match.class.getSimpleName() + "[", "]") 204 .add("matchType=" + matchType) 205 .add("status=" + status) 206 .add("reasons=" + reasons) 207 .add("entityMatched=" + entityMatched) 208 .toString(); 209 } 210}