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 }