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.HashSet;
032 import java.util.List;
033 import java.util.Set;
034
035 import edu.uchsc.ccp.iaa.Annotation;
036 import edu.uchsc.ccp.iaa.IAA;
037
038 public class SpansOverlapSimpleFeatureMatcher implements Matcher {
039
040 /**
041 * This method will return an annotation that has overlapping spans and the
042 * same simple features. It is not required that the annotation class match.
043 * Preference will be given to an annotation that has the same class and
044 * spans. Secondary preference will be given to an annotation with the same
045 * span followed by an the shortest annotation with overlapping spans and
046 * the same simple features. If no annotation has overlapping spans and the
047 * same simple features, then null is returned.
048 *
049 * @param matchResult
050 * will be set to:
051 * <ul>
052 * <li>TRIVIAL_NONMATCH if there are no overlapping annotations
053 * with the passed in annotation
054 * <li>NONTRIVIAL_MATCH if there is an annotation that is
055 * overlapping and the Annotation.compareSimpleFeatures returns
056 * NONTRIVIAL_MATCH
057 * <li>TRIVIAL_MATCH if there is an annotation that is
058 * overlapping and the Annotation.compareSimpleFeatures returns
059 * TRIVIAL_MATCH <br>
060 * Note: if there is a trivial_match then there cannot possibly
061 * be a NONTRIVIAL_MATCH because one of the simple features of
062 * the passed in annotation must have a null value or there are
063 * no simple features.
064 * <li>NONTRIVIAL_NONMATCH if there an annotation that is
065 * overlapping and the Annotation.compareSimpleFeatures returns
066 * NONTRIVIAL_NONMATCH
067 * <li>TRIVIAL_NONMATCH if there is no match or non-trivial
068 * non-match found.
069 * @return will return the first nontrivial match that it finds preferring
070 * @see edu.uchsc.ccp.iaa.matcher.Matcher#match(Annotation, String, Set,
071 * IAA, MatchResult)
072 * @see edu.uchsc.ccp.iaa.matcher.MatchResult#NONTRIVIAL_MATCH
073 * @see edu.uchsc.ccp.iaa.matcher.MatchResult#NONTRIVIAL_NONMATCH
074 * @see edu.uchsc.ccp.iaa.matcher.MatchResult#TRIVIAL_MATCH
075 * @see edu.uchsc.ccp.iaa.matcher.MatchResult#TRIVIAL_NONMATCH
076 */
077
078 public Annotation match(Annotation annotation, String compareSetName, Set<Annotation> excludeAnnotations, IAA iaa,
079 MatchResult matchResult) {
080
081 Annotation spansExactSimpleFeatureMatch = SpansExactSimpleFeatureMatcher.match(annotation, compareSetName, iaa,
082 excludeAnnotations, matchResult);
083
084 // if TRIVIAL_MATCH then we do not have to worry about there being an
085 // overlapping NONTRIVIAL_MATCH further down
086 // because we know that a trivial match is the best we can do.
087 if (spansExactSimpleFeatureMatch != null
088 && (matchResult.getResult() == MatchResult.NONTRIVIAL_MATCH || matchResult.getResult() == MatchResult.TRIVIAL_MATCH)) {
089 return spansExactSimpleFeatureMatch;
090 }
091
092 Set<Annotation> candidateAnnotations = new HashSet<Annotation>(iaa.getOverlappingAnnotations(annotation,
093 compareSetName));
094 candidateAnnotations.removeAll(excludeAnnotations);
095
096 // we are going to collect all matches because we want to return the
097 // shortest of the matches if there is more than one.
098 List<Annotation> nontrivialMatches = new ArrayList<Annotation>();
099 List<Annotation> trivialMatches = new ArrayList<Annotation>();
100
101 boolean nontrivialNonmatch = false;
102
103 for (Annotation candidateAnnotation : candidateAnnotations) {
104 if (!excludeAnnotations.contains(candidateAnnotation)) {
105 int result = Annotation.compareSimpleFeatures(annotation, candidateAnnotation);
106 if (result == MatchResult.NONTRIVIAL_MATCH) {
107 nontrivialMatches.add(candidateAnnotation);
108 } else if (result == MatchResult.TRIVIAL_MATCH) {
109 trivialMatches.add(candidateAnnotation);
110 }
111 if (result == MatchResult.NONTRIVIAL_NONMATCH) {
112 nontrivialNonmatch = true;
113 }
114 }
115 }
116
117 if (nontrivialMatches.size() > 0) {
118 matchResult.setResult(MatchResult.NONTRIVIAL_MATCH);
119 if (nontrivialMatches.size() == 1) {
120 return nontrivialMatches.iterator().next();
121 } else {
122 return Annotation.getShortestAnnotation(nontrivialMatches);
123 }
124 }
125 if (trivialMatches.size() > 0) {
126 matchResult.setResult(MatchResult.TRIVIAL_MATCH);
127 if (trivialMatches.size() == 1) {
128 return trivialMatches.iterator().next();
129 } else {
130 return Annotation.getShortestAnnotation(trivialMatches);
131 }
132 }
133
134 if (nontrivialNonmatch)
135 matchResult.setResult(MatchResult.NONTRIVIAL_NONMATCH);
136 else
137 matchResult.setResult(MatchResult.TRIVIAL_NONMATCH);
138 return null;
139 }
140
141 public String getName() {
142 return "Simple slots matcher (with overlapping spans)";
143 }
144
145 public String getDescription() {
146 return "Annotations match if they have overlapping spans and the same value for simple slots (e.g. slots that are primitive values such as integer and String). Only slots that are specified must match.";
147 }
148
149 public boolean returnsTrivials() {
150 return true;
151 }
152
153 }