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 SpansExactSimpleFeatureMatcher implements Matcher {
039            // public static final String FEATURE_NAMES =
040            // SpansExactSimpleFeatureMatcher.class.getName()+".FEATURE_NAMES";
041    
042            /**
043             * This method will return an annotation that has the exact same spans and
044             * simple features. It is not required that the annotation class match.
045             * Preference will be given to an annotation that has the same class as well
046             * as spans and simple features. If one does not exist, then null is
047             * returned.
048             */
049            public Annotation match(Annotation annotation, String compareSetName, Set<Annotation> excludeAnnotations, IAA iaa,
050                            MatchResult matchResult) {
051                    return match(annotation, compareSetName, iaa, excludeAnnotations, matchResult);
052            }
053    
054            /**
055             * This method will return an annotation that has the exact same spans and
056             * simple features. It is not required that the annotation class match.
057             * Preference will be given to an annotation that has the same class as well
058             * as spans and simple features. If one does not exist, then null is
059             * returned.
060             * 
061             * @param annotation
062             * @param compareSetName
063             * @param iaa
064             * @param excludeAnnotations
065             * @param matchResult
066             *            will be set to:
067             *            <ul>
068             *            <li>TRIVIAL_NONMATCH if there are no exactly overlapping
069             *            annotations with the passed in annotation
070             *            <li>NONTRIVIAL_MATCH if there is an annotation that is exactly
071             *            overlapping and the Annotation.compareSimpleFeatures returns
072             *            NONTRIVIAL_MATCH
073             *            <li>TRIVIAL_MATCH if there is an annotation that is exactly
074             *            overlapping and the Annotation.compareSimpleFeatures returns
075             *            TRIVIAL_MATCH <br>
076             *            Note: if there is a trivial_match then there cannot possibly
077             *            be a NONTRIVIAL_MATCH because one of the simple features of
078             *            the passed in annotation must have a null value or there are
079             *            no simple features.
080             *            <li>NONTRIVIAL_NONMATCH if there an annotation that is exactly
081             *            overlapping and the Annotation.compareSimpleFeatures returns
082             *            NONTRIVIAL_NONMATCH
083             *            <li>TRIVIAL_NONMATCH if there is no match or non-trivial
084             *            non-match found.
085             * @return will return the first nontrivial match that it finds preferring
086             * @see edu.uchsc.ccp.iaa.matcher.Matcher#match(Annotation, String, Set,
087             *      IAA, MatchResult)
088             * @see edu.uchsc.ccp.iaa.matcher.MatchResult#NONTRIVIAL_MATCH
089             * @see edu.uchsc.ccp.iaa.matcher.MatchResult#NONTRIVIAL_NONMATCH
090             * @see edu.uchsc.ccp.iaa.matcher.MatchResult#TRIVIAL_MATCH
091             * @see edu.uchsc.ccp.iaa.matcher.MatchResult#TRIVIAL_NONMATCH
092             */
093    
094            public static Annotation match(Annotation annotation, String compareSetName, IAA iaa,
095                            Set<Annotation> excludeAnnotations, MatchResult matchResult) {
096                    // prefer class and span matches over just span matches
097                    Set<Annotation> classAndSpanMatches = ClassAndSpanMatcher.matches(annotation, compareSetName, iaa,
098                                    excludeAnnotations, false);
099                    Set<Annotation> exactlyOverlappingAnnotations = new HashSet<Annotation>(iaa.getExactlyOverlappingAnnotations(
100                                    annotation, compareSetName));
101                    exactlyOverlappingAnnotations.removeAll(classAndSpanMatches);
102                    exactlyOverlappingAnnotations.removeAll(excludeAnnotations);
103    
104                    List<Annotation> candidateAnnotations = new ArrayList<Annotation>(classAndSpanMatches.size()
105                                    + exactlyOverlappingAnnotations.size());
106                    for (Annotation candidateAnnotation : classAndSpanMatches) {
107                            candidateAnnotations.add(candidateAnnotation);
108                    }
109                    for (Annotation candidateAnnotation : exactlyOverlappingAnnotations) {
110                            candidateAnnotations.add(candidateAnnotation);
111                    }
112    
113                    boolean nontrivialNonmatch = false;
114    
115                    for (Annotation candidateAnnotation : candidateAnnotations) {
116                            if (!excludeAnnotations.contains(candidateAnnotation)) {
117                                    int result = Annotation.compareSimpleFeatures(annotation, candidateAnnotation);
118                                    // if there is a trivial_match then there cannot possibly be a
119                                    // NONTRIVIAL_MATCH
120                                    // because one of the simple features of the passed in
121                                    // annotation must have a null value
122                                    // or there are no simple features.
123                                    if (result == MatchResult.NONTRIVIAL_MATCH || result == MatchResult.TRIVIAL_MATCH) {
124                                            matchResult.setResult(result);
125                                            return candidateAnnotation;
126                                    }
127                                    if (result == MatchResult.NONTRIVIAL_NONMATCH) {
128                                            nontrivialNonmatch = true;
129                                    }
130                            }
131                    }
132    
133                    if (nontrivialNonmatch)
134                            matchResult.setResult(MatchResult.NONTRIVIAL_NONMATCH);
135                    else
136                            matchResult.setResult(MatchResult.TRIVIAL_NONMATCH);
137                    return null;
138            }
139    
140            public String getName() {
141                    return "Simple slots matcher (with same spans)";
142            }
143    
144            public String getDescription() {
145                    return "Annotations match if they have the same 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.";
146            }
147    
148            public boolean returnsTrivials() {
149                    return true;
150            }
151    
152    }