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    }