001 /*
002 * The contents of this file are subject to the Mozilla Public
003 * License Version 1.1 (the "License"); you may not use this file
004 * except in compliance with the License. You may obtain a copy of
005 * the License at http://www.mozilla.org/MPL/
006 *
007 * Software distributed under the License is distributed on an "AS
008 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
009 * implied. See the License for the specific language governing
010 * rights and limitations under the License.
011 *
012 * The Original Code is Knowtator.
013 *
014 * The Initial Developer of the Original Code is University of Colorado.
015 * Copyright (C) 2005 - 2008. All Rights Reserved.
016 *
017 * Knowtator was developed by the Center for Computational Pharmacology
018 * (http://compbio.uchcs.edu) at the University of Colorado Health
019 * Sciences Center School of Medicine with support from the National
020 * Library of Medicine.
021 *
022 * Current information about Knowtator can be obtained at
023 * http://knowtator.sourceforge.net/
024 *
025 * Contributor(s):
026 * Philip V. Ogren <philip@ogren.info> (Original Author)
027 */
028 package edu.uchsc.ccp.iaa.matcher;
029
030 import java.util.ArrayList;
031 import java.util.HashMap;
032 import java.util.HashSet;
033 import java.util.List;
034 import java.util.Map;
035 import java.util.Set;
036
037 import edu.uchsc.ccp.iaa.Annotation;
038 import edu.uchsc.ccp.iaa.IAA;
039
040 public class FeatureMatcher implements Matcher {
041 boolean matchClasses = true;
042
043 int matchSpans = Annotation.SPANS_OVERLAP_COMPARISON;
044
045 Set<String> comparedSimpleFeatures = new HashSet<String>();
046
047 Map<String, ComplexFeatureMatchCriteria> comparedComplexFeatures = new HashMap<String, ComplexFeatureMatchCriteria>();
048
049 String name;
050
051 public FeatureMatcher() {
052 this("Feature Matcher");
053 }
054
055 public FeatureMatcher(String name) {
056 this.name = name;
057 }
058
059 public void setMatchClasses(boolean matchClasses) {
060 this.matchClasses = matchClasses;
061 }
062
063 public void setMatchSpans(int matchSpans) {
064 this.matchSpans = matchSpans;
065 }
066
067 public void addComparedSimpleFeatures(String simpleFeatureName) {
068 comparedSimpleFeatures.add(simpleFeatureName);
069 }
070
071 public void addComparedComplexFeature(String complexFeatureName, ComplexFeatureMatchCriteria matchCriteria) {
072 comparedComplexFeatures.put(complexFeatureName, matchCriteria);
073 }
074
075 public Annotation match(Annotation annotation, String compareSetName, Set<Annotation> excludeAnnotations, IAA iaa,
076 MatchResult matchResult) {
077 Set<Annotation> candidateAnnotations = new HashSet<Annotation>();
078 if (matchClasses) {
079 if (matchSpans == Annotation.SPANS_EXACT_COMPARISON) {
080 candidateAnnotations.addAll(iaa.getAnnotationsOfSameType(annotation, compareSetName));
081 candidateAnnotations.retainAll(iaa.getExactlyOverlappingAnnotations(annotation, compareSetName));
082 } else if (matchSpans == Annotation.SPANS_OVERLAP_COMPARISON) {
083 // Set<Annotation> someAnnotations =
084 // iaa.getExactlyOverlappingAnnotations(annotation,
085 // compareSetName);
086 //
087 // Set<Annotation> someAnnotations =
088 // iaa.getExactlyOverlappingAnnotations(annotation,
089 // compareSetName);
090 //
091 candidateAnnotations.addAll(iaa.getExactlyOverlappingAnnotations(annotation, compareSetName));
092 candidateAnnotations.addAll(iaa.getOverlappingAnnotations(annotation, compareSetName));
093 candidateAnnotations.retainAll(iaa.getAnnotationsOfSameType(annotation, compareSetName));
094 }
095 } else {
096 if (matchSpans == Annotation.SPANS_EXACT_COMPARISON) {
097 // we want all annotations that have the exactly matching spans,
098 // but we want them ordered in the following way:
099 // 1) annotations with the same class and same spans
100 // 2) annotations with the same spans
101 candidateAnnotations.addAll(iaa.getAnnotationsOfSameType(annotation, compareSetName));
102 Set<Annotation> exactlyOverlappingAnnotations = iaa.getExactlyOverlappingAnnotations(annotation,
103 compareSetName);
104 candidateAnnotations.retainAll(exactlyOverlappingAnnotations);
105 candidateAnnotations.addAll(exactlyOverlappingAnnotations);
106 } else if (matchSpans == Annotation.SPANS_OVERLAP_COMPARISON) {
107 // we want all annotations that are overlapping, but we want
108 // them ordered in the following way:
109 // 1) annotations with the same class and same spans
110 // 2) annotations with the same class and overlapping spans
111 // 3) annotations with the same spans
112 // 4) annotations with overlapping spans
113 Set<Annotation> classAnnotations = iaa.getAnnotationsOfSameType(annotation, compareSetName);
114 Set<Annotation> exactlyOverlappingAnnotations = iaa.getExactlyOverlappingAnnotations(annotation,
115 compareSetName);
116 Set<Annotation> overlappingAnnotations = iaa.getOverlappingAnnotations(annotation, compareSetName);
117
118 Set<Annotation> classAndExactSpanAnnotations = new HashSet<Annotation>(classAnnotations);
119 classAndExactSpanAnnotations.retainAll(exactlyOverlappingAnnotations);
120
121 Set<Annotation> classAndOverlappingSpanAnnotations = new HashSet<Annotation>(classAnnotations);
122 classAndOverlappingSpanAnnotations.retainAll(overlappingAnnotations);
123
124 candidateAnnotations.addAll(classAndExactSpanAnnotations);
125 candidateAnnotations.addAll(classAndOverlappingSpanAnnotations);
126 candidateAnnotations.addAll(exactlyOverlappingAnnotations);
127 candidateAnnotations.addAll(overlappingAnnotations);
128 } else {
129 // we want all annotations that are in the other set, but we
130 // want them ordered in the following way:
131 // 1) annotations with the same class and same spans
132 // 2) annotations with the same class and overlapping spans
133 // 3) annotations with the same spans
134 // 4) annotations with overlapping spans
135 // 5) annotations with the same class
136 // 6) all other annotations
137 Set<Annotation> classAnnotations = iaa.getAnnotationsOfSameType(annotation, compareSetName);
138 Set<Annotation> exactlyOverlappingAnnotations = iaa.getExactlyOverlappingAnnotations(annotation,
139 compareSetName);
140 Set<Annotation> overlappingAnnotations = iaa.getOverlappingAnnotations(annotation, compareSetName);
141
142 Set<Annotation> classAndExactSpanAnnotations = new HashSet<Annotation>(classAnnotations);
143 classAndExactSpanAnnotations.retainAll(exactlyOverlappingAnnotations);
144
145 Set<Annotation> classAndOverlappingSpanAnnotations = new HashSet<Annotation>(classAnnotations);
146 classAndOverlappingSpanAnnotations.retainAll(overlappingAnnotations);
147
148 candidateAnnotations.addAll(classAndExactSpanAnnotations);
149 candidateAnnotations.addAll(classAndOverlappingSpanAnnotations);
150 candidateAnnotations.addAll(exactlyOverlappingAnnotations);
151 candidateAnnotations.addAll(overlappingAnnotations);
152 candidateAnnotations.addAll(classAnnotations);
153 candidateAnnotations.addAll(iaa.getAnnotationSets().get(compareSetName));
154 }
155 }
156
157 candidateAnnotations.removeAll(excludeAnnotations);
158
159 if (candidateAnnotations.size() == 0) {
160 matchResult.setResult(MatchResult.TRIVIAL_NONMATCH);
161 return null;
162 }
163
164 // we are going to collect all matches because we want to return the
165 // shortest of the matches if there is more than one.
166 List<Annotation> nontrivialMatches = new ArrayList<Annotation>();
167 List<Annotation> trivialMatches = new ArrayList<Annotation>();
168 boolean nontrivialNonmatch = false;
169
170 for (Annotation candidateAnnotation : candidateAnnotations) {
171 int result;
172 if (comparedSimpleFeatures.size() > 0)
173 result = Annotation.compareSimpleFeatures(annotation, candidateAnnotation, comparedSimpleFeatures);
174 else
175 result = MatchResult.NONTRIVIAL_MATCH;
176
177 if (result == MatchResult.TRIVIAL_NONMATCH)
178 continue;
179
180 for (String complexFeatureName : comparedComplexFeatures.keySet()) {
181 ComplexFeatureMatchCriteria matchCriteria = comparedComplexFeatures.get(complexFeatureName);
182
183 int complexResult = Annotation.compareComplexFeature(annotation, candidateAnnotation,
184 complexFeatureName, matchCriteria.matchSpans, matchCriteria.matchClasses,
185 matchCriteria.comparedSimpleFeatures,
186 matchCriteria.trivialSimpleFeatureMatchesCauseTrivialMatch);
187
188 if (complexResult == MatchResult.TRIVIAL_NONMATCH) {
189 result = MatchResult.TRIVIAL_NONMATCH;
190 break;
191 } else if (complexResult == MatchResult.NONTRIVIAL_NONMATCH
192 && (result == MatchResult.NONTRIVIAL_MATCH || result == MatchResult.TRIVIAL_MATCH)) {
193 result = MatchResult.NONTRIVIAL_NONMATCH;
194 } else if (complexResult == MatchResult.TRIVIAL_MATCH && result == MatchResult.NONTRIVIAL_MATCH) {
195 result = MatchResult.TRIVIAL_MATCH;
196 }
197 }
198
199 if (result == MatchResult.TRIVIAL_NONMATCH) {
200 continue;
201 } else if (result == MatchResult.NONTRIVIAL_NONMATCH) {
202 nontrivialNonmatch = true;
203 } else if (result == MatchResult.TRIVIAL_MATCH) {
204 trivialMatches.add(candidateAnnotation);
205 } else if (result == MatchResult.NONTRIVIAL_MATCH) {
206 nontrivialMatches.add(candidateAnnotation);
207 }
208 }
209
210 if (nontrivialMatches.size() > 0) {
211 matchResult.setResult(MatchResult.NONTRIVIAL_MATCH);
212 if (nontrivialMatches.size() == 1)
213 return nontrivialMatches.iterator().next();
214 else
215 return Annotation.getShortestAnnotation(nontrivialMatches);
216 }
217 if (trivialMatches.size() > 0) {
218 matchResult.setResult(MatchResult.TRIVIAL_MATCH);
219 if (trivialMatches.size() == 1)
220 return trivialMatches.iterator().next();
221 else
222 return Annotation.getShortestAnnotation(trivialMatches);
223 }
224
225 if (nontrivialNonmatch)
226 matchResult.setResult(MatchResult.NONTRIVIAL_NONMATCH);
227 else
228 matchResult.setResult(MatchResult.TRIVIAL_NONMATCH);
229 return null;
230 }
231
232 public String getName() {
233 return name;
234 }
235
236 public String getDescription() {
237 return "";
238 }
239
240 public boolean returnsTrivials() {
241 return true;
242 }
243
244 public int getMatchSpans() {
245 return matchSpans;
246 }
247
248 }