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    
029    package edu.uchsc.ccp.knowtator;
030    
031    import java.util.ArrayList;
032    import java.util.Collection;
033    import java.util.Collections;
034    import java.util.Comparator;
035    import java.util.HashMap;
036    import java.util.HashSet;
037    import java.util.LinkedHashSet;
038    import java.util.List;
039    import java.util.Set;
040    import java.util.TreeSet;
041    
042    import javax.swing.JOptionPane;
043    
044    import org.apache.log4j.Logger;
045    
046    import edu.stanford.smi.protege.event.FrameAdapter;
047    import edu.stanford.smi.protege.event.FrameEvent;
048    import edu.stanford.smi.protege.model.Cls;
049    import edu.stanford.smi.protege.model.Frame;
050    import edu.stanford.smi.protege.model.FrameID;
051    import edu.stanford.smi.protege.model.Instance;
052    import edu.stanford.smi.protege.model.KnowledgeBase;
053    import edu.stanford.smi.protege.model.SimpleInstance;
054    import edu.stanford.smi.protege.model.Slot;
055    import edu.stanford.smi.protege.ui.FrameComparator;
056    import edu.uchsc.ccp.knowtator.event.AnnotationCreatedEvent;
057    import edu.uchsc.ccp.knowtator.event.AnnotationCreatedListener;
058    import edu.uchsc.ccp.knowtator.event.CurrentAnnotationsChangeEvent;
059    import edu.uchsc.ccp.knowtator.event.CurrentAnnotationsChangedListener;
060    import edu.uchsc.ccp.knowtator.event.EventHandler;
061    import edu.uchsc.ccp.knowtator.event.RefreshAnnotationsDisplayListener;
062    import edu.uchsc.ccp.knowtator.textsource.TextSource;
063    import edu.uchsc.ccp.knowtator.textsource.TextSourceAccessException;
064    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeEvent;
065    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeListener;
066    import edu.uchsc.ccp.knowtator.ui.ColorFrameRenderer;
067    import edu.uchsc.ccp.knowtator.ui.KnowtatorTextPane;
068    import edu.uchsc.ccp.knowtator.ui.TextViewer;
069    import edu.uchsc.ccp.knowtator.util.ConsensusAnnotations;
070    import edu.uchsc.ccp.knowtator.util.ConsensusException;
071    import edu.uchsc.ccp.knowtator.util.ConsensusSet;
072    
073    public class KnowtatorManager implements CurrentAnnotationsChangedListener, AnnotationCreatedListener,
074                    TextSourceChangeListener {
075            private KnowledgeBase kb;
076    
077            private KnowtatorProjectUtil kpu;
078    
079            private AnnotationUtil annotationUtil;
080    
081            private TextSourceUtil textSourceUtil;
082    
083            private SpanUtil spanUtil;
084    
085            private FilterUtil filterUtil;
086    
087            private MentionUtil mentionUtil;
088    
089            private DisplayColors displayColors;
090    
091            private BrowserTextUtil browserTextUtil;
092    
093            private KnowtatorTextPane textPane;
094    
095            private TextViewer textViewer;
096    
097            /*
098             * key is a class or instance that is annotated, value is a list of the
099             * annotations for that class or instance. Only annotations in
100             * partiallyFilteredAnnotations are included.
101             */
102            java.util.Map<Frame, List<SimpleInstance>> frameAnnotationsMap;
103    
104            ColorFrameRenderer renderer;
105    
106            List<SimpleInstance> filteredAnnotations;
107    
108            List<SimpleInstance> visibleFilteredAnnotations;
109    
110            List<SimpleInstance> partiallyFilteredAnnotations;
111    
112            List<SimpleInstance> selectableFilteredAnnotations;
113    
114            Cls selectedCls;
115    
116            SimpleInstance selectedAnnotation;
117    
118            SimpleInstance lastSelectedAnnotation; // the selectedAnnotation if not
119    
120            // null else the last non-null
121            // selectedAnnotation
122    
123            List<Span> selectedSpans;
124    
125            Comparator<SimpleInstance> annotationComparator;
126    
127            Frame fastAnnotateFrame = null;
128    
129            /** Set of Frames (classes) that will be shown in the fast annotate tool bar */
130            Set<FrameID> fastAnnotateFrameSet = null;
131    
132            boolean fastAnnotateMode = false;
133    
134            SimpleInstance selectedFilter = null;
135    
136            boolean consensusMode = false;
137    
138            ConsensusSet consensusSet = null;
139    
140            Logger logger = Logger.getLogger(KnowtatorManager.class);
141    
142            public KnowtatorManager(KnowtatorProjectUtil kpu) {
143                    this.kpu = kpu;
144                    this.kb = kpu.getKnowledgeBase();
145    
146                    annotationUtil = new AnnotationUtil(this);
147                    textSourceUtil = new TextSourceUtil(annotationUtil, kpu);
148                    annotationUtil.setTextSourceUtil(textSourceUtil);
149                    mentionUtil = new MentionUtil(kpu);
150                    annotationUtil.setMentionUtil(mentionUtil);
151                    mentionUtil.setAnnotationUtil(annotationUtil);
152                    filterUtil = new FilterUtil(this);
153                    displayColors = new DisplayColors(this);
154                    browserTextUtil = new BrowserTextUtil(annotationUtil, mentionUtil, kpu);
155                    spanUtil = new SpanUtil(this);
156    
157                    frameAnnotationsMap = new HashMap<Frame, List<SimpleInstance>>();
158                    renderer = new ColorFrameRenderer(this);
159    
160                    filteredAnnotations = new ArrayList<SimpleInstance>();
161                    visibleFilteredAnnotations = new ArrayList<SimpleInstance>();
162                    partiallyFilteredAnnotations = new ArrayList<SimpleInstance>();
163                    selectableFilteredAnnotations = new ArrayList<SimpleInstance>();
164    
165                    EventHandler.getInstance().setKnowtatorManager(this);
166                    EventHandler.getInstance().addCurrentAnnotationsChangedListener(this);
167                    EventHandler.getInstance().addAnnotationCreatedListener(this);
168    
169                    fastAnnotateFrameSet = new LinkedHashSet<FrameID>();
170                    initListener();
171            }
172    
173            private void initListener() {
174                    logger.debug("");
175    
176                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
177                    configuration.addFrameListener(new FrameAdapter() {
178                            public void ownSlotValueChanged(FrameEvent frameEvent) {
179                                    updateFilter(frameEvent.getSlot());
180                            }
181    
182                            public void ownSlotAdded(FrameEvent frameEvent) {
183                                    updateFilter(frameEvent.getSlot());
184                            }
185    
186                            public void ownSlotRemoved(FrameEvent frameEvent) {
187                                    updateFilter(frameEvent.getSlot());
188                            }
189    
190                            private void updateFilter(Slot slot) {
191                                    if (slot.equals(kpu.getSelectedFilterSlot())) {
192                                            SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
193                                            SimpleInstance configurationFilter = (SimpleInstance) configuration.getDirectOwnSlotValue(kpu
194                                                            .getSelectedFilterSlot());
195                                            // prevent infinite loop
196                                            if (configurationFilter != null
197                                                            && (selectedFilter == null || !selectedFilter.equals(configurationFilter))) {
198                                                    try {
199                                                            setSelectedFilter(configurationFilter);
200                                                    } catch (ConsensusException ce) {
201                                                            ce.printStackTrace();
202                                                    }
203                                            }
204                                    }
205                            }
206                    });
207    
208            }
209    
210            public KnowledgeBase getKnowledgeBase() {
211                    return kb;
212            }
213    
214            public void setNotifyText(String text) {
215                    logger.debug("");
216                    EventHandler.getInstance().fireNotifyTextChanged(text);
217            }
218    
219            public List<SimpleInstance> getCurrentAnnotationsForFrame(Frame frame) {
220                    logger.debug("");
221                    List<SimpleInstance> annotations = frameAnnotationsMap.get(frame);
222                    if (annotations == null)
223                            return Collections.emptyList();
224                    return Collections.unmodifiableList(frameAnnotationsMap.get(frame));
225            }
226    
227            public int getCurrentAnnotationCountForFrame(Frame frame) {
228                    int returnValue = 0;
229                    if (frameAnnotationsMap.containsKey(frame))
230                            returnValue = frameAnnotationsMap.get(frame).size();
231                    logger.debug("count = " + returnValue);
232                    return returnValue;
233            }
234    
235            public int getConsolidatedAnnotationCountForFrame(Frame frame) {
236                    logger.debug("");
237                    int returnValue = 0;
238                    if (frameAnnotationsMap.containsKey(frame)) {
239                            List<SimpleInstance> frameAnnotations = frameAnnotationsMap.get(frame);
240                            SimpleInstance teamAnnotator = consensusSet.getTeamAnnotator();
241                            for (SimpleInstance frameAnnotation : frameAnnotations) {
242                                    if (teamAnnotator.equals(annotationUtil.getAnnotator(frameAnnotation)))
243                                            returnValue++;
244                            }
245                    }
246                    return returnValue;
247            }
248    
249            /**
250             * @return the next annotation
251             * @see #selectNextAnnotation()
252             */
253            public SimpleInstance getNextAnnotation() {
254                    logger.debug("");
255                    return getNextPreviousAnnotation(1);
256            }
257    
258            /**
259             * 
260             * @return the previous annotation
261             * @see #selectPreviousAnnotation
262             */
263            public SimpleInstance getPreviousAnnotation() {
264                    logger.debug("");
265                    return getNextPreviousAnnotation(-1);
266            }
267    
268            private SimpleInstance getNextPreviousAnnotation(int delta) {
269                    logger.debug("");
270                    SimpleInstance mention = annotationUtil.getMention(selectedAnnotation);
271                    Frame mentionFrame = mentionUtil.getMentionFrame(mention);
272                    if (mentionFrame != null) {
273                            List<SimpleInstance> frameAnnotations = frameAnnotationsMap.get(mentionFrame);
274                            Comparator<SimpleInstance> annotationComparator = getAnnotationComparator();
275                            if (annotationComparator != null)
276                                    Collections.sort(frameAnnotations, annotationComparator);
277                            else
278                                    Collections.sort(frameAnnotations, new FrameComparator());
279    
280                            for (int i = 0; i < frameAnnotations.size(); i++) {
281                                    SimpleInstance frameAnnotation = frameAnnotations.get(i);
282                                    if (frameAnnotation.equals(selectedAnnotation)) {
283                                            if (i + delta >= 0 && i + delta < frameAnnotations.size())
284                                                    return frameAnnotations.get(i + delta);
285                                    }
286                            }
287                            if (frameAnnotations.size() > 0) {
288                                    if (delta > 0)
289                                            return frameAnnotations.get(0);
290                                    else
291                                            return frameAnnotations.get(frameAnnotations.size() - 1);
292                            }
293                    }
294                    return selectedAnnotation;
295            }
296    
297            /**
298             * Returns the annotations for the current text source that satisfy the
299             * currently selected filter.
300             */
301    
302            public List<SimpleInstance> getCurrentFilteredAnnotations() {
303                    logger.debug("");
304                    if (filteredAnnotations == null)
305                            return Collections.emptyList();
306                    return Collections.unmodifiableList(filteredAnnotations);
307            }
308    
309            public List<SimpleInstance> getVisibleFilteredAnnotations() {
310                    if (visibleFilteredAnnotations == null)
311                            return Collections.emptyList();
312                    return Collections.unmodifiableList(visibleFilteredAnnotations);
313            }
314    
315            /**
316             * returns the annotations for the current text source that satisfy the
317             * currently selected filter ignoring the type constraint.
318             */
319            public List<SimpleInstance> getCurrentPartiallyFilteredAnnotations() {
320                    logger.debug("");
321                    if (partiallyFilteredAnnotations == null)
322                            return Collections.emptyList();
323                    return Collections.unmodifiableList(partiallyFilteredAnnotations);
324            }
325    
326            public List<SimpleInstance> getCurrentSelectableFilteredAnnotations() {
327                    logger.debug("");
328                    if (selectableFilteredAnnotations == null)
329                            return Collections.emptyList();
330                    return Collections.unmodifiableList(selectableFilteredAnnotations);
331            }
332    
333            public void updateCurrentAnnotations() {
334                    logger.debug("");
335                    TextSource currentTextSource = textSourceUtil.getCurrentTextSource();
336                    if (currentTextSource == null)
337                            return;
338                    Collection<SimpleInstance> annotations = annotationUtil.getAnnotations(currentTextSource);
339                    filteredAnnotations.clear();
340                    visibleFilteredAnnotations.clear();
341                    partiallyFilteredAnnotations.clear();
342                    selectableFilteredAnnotations.clear();
343    
344                    if (annotations != null) {
345                            SimpleInstance currentFilter = getSelectedFilter();
346                            if (currentFilter != null) {
347                                    partiallyFilteredAnnotations.addAll(filterUtil.filterAnnotations(annotations, currentFilter, true));
348                                    filteredAnnotations.addAll(filterUtil.filterAnnotations(partiallyFilteredAnnotations, currentFilter));
349                                    selectableFilteredAnnotations.addAll(filterUtil.filterAnnotations(filteredAnnotations, currentFilter,
350                                                    false, true));
351                            } else {
352                                    filteredAnnotations.addAll(annotations);
353                                    partiallyFilteredAnnotations.addAll(annotations);
354                                    selectableFilteredAnnotations.addAll(annotations);
355                            }
356                    }
357    
358                    EventHandler.getInstance().fireCurrentAnnotationsChanged();
359                    SimpleInstance selectedAnnotation = getSelectedAnnotation();
360                    if (!filteredAnnotations.contains(selectedAnnotation))
361                            setSelectedAnnotation(null);
362                    Collections.sort(filteredAnnotations, spanUtil.comparator(browserTextUtil.comparator()));
363                    updateVisibleFilteredAnnotations();
364                    refreshAnnotationsDisplay(true);
365            }
366    
367            /*
368             * this method would be faster if we did a binary search for the first
369             * visible annotation. However, this will take a bit of doing because there
370             * needs to be a comparator for the SimpleInstance's that make use of the
371             * spans
372             */
373            public void updateVisibleFilteredAnnotations() {
374                    visibleFilteredAnnotations.clear();
375                    Span visibleSpan = getVisibleSpan();
376                    if (visibleSpan == null)
377                            return;
378                    logger
379                                    .debug("visibleSpan = " + visibleSpan + "  filteredAnnotations = "
380                                                    + selectableFilteredAnnotations.size());
381    
382                    for (SimpleInstance filteredAnnotation : selectableFilteredAnnotations) {
383                            List<Span> spans = annotationUtil.getSpans(filteredAnnotation);
384                            if (spans != null && spans.size() > 0) {
385                                    if (visibleSpan.contains(spans.get(0))) {
386                                            visibleFilteredAnnotations.add(filteredAnnotation);
387                                    }
388                            }
389                    }
390                    // we sort them by span length because that is how KnowtatorTextPane
391                    // needs them
392                    // so that spans are highlighted with the correct precedence.
393                    // we do it here because then we don't have to do it as often.
394                    Collections.sort(visibleFilteredAnnotations, spanUtil.lengthComparator());
395                    logger.debug("visible filtered annotations = " + visibleFilteredAnnotations.size());
396            }
397    
398            /**
399             * This is the preferred way to obtain the currently selected cls (i.e. the
400             * cls selected in the annotation schema pane/tree.)
401             * 
402             * @return the currently selected cls.
403             */
404            public Cls getSelectedCls() {
405                    logger.debug("");
406                    return selectedCls;
407            }
408    
409            /**
410             * This is the preferred way to set the curently selected cls (i.e. the cls
411             * that is highlighted in the annotation schema pane/tree.) This method will
412             * call the appropriate event firing method in EventHandler.
413             * 
414             * @param cls
415             */
416            public void setSelectedCls(Cls cls) {
417                    logger.debug("");
418                    logger.debug("selcected cls = \"" + cls.getBrowserText() + "\"");
419                    selectedCls = cls;
420                    EventHandler.getInstance().fireSelectedClsChanged(cls);
421            }
422    
423            /**
424             * This is the preferred way to obtain the currently selected annotation
425             * 
426             * @return the currently selected annotation.
427             */
428            public SimpleInstance getSelectedAnnotation() {
429                    logger.debug("");
430                    return selectedAnnotation;
431            }
432    
433            /**
434             * @return the currently selected annotation if it is not null. Otherwise,
435             *         it returns the previously selected annotation or null if there
436             *         has never been an annotation selected.
437             */
438            public SimpleInstance getLastSelectedAnnotation() {
439                    logger.debug("");
440                    return lastSelectedAnnotation;
441            }
442    
443            /**
444             * This is the preferred way to set the curently selected annotation. This
445             * method will call the appropriate event firing method in EventHandler.
446             * 
447             * @param annotation
448             */
449            public void setSelectedAnnotation(SimpleInstance annotation) {
450                    logger.debug("selected annotation=\"" + browserTextUtil.getBrowserText(annotation, 100) + "\"");
451                    selectedAnnotation = annotation;
452                    if (annotation != null)
453                            lastSelectedAnnotation = annotation;
454                    EventHandler.getInstance().fireSelectedAnnotationChanged(annotation);
455                    refreshAnnotationsDisplay(true);
456                    setNotifyText(getBrowserTextUtil().getBrowserText(annotation));
457            }
458    
459            /**
460             * The "next" annotation is defined as the annotation (if it exists) that is
461             * of the same type as the currently selected annotation that is 'next'
462             * according to the sort order defined by the currently selected comparator.
463             */
464            public void selectNextAnnotation() {
465                    logger.debug("");
466                    setSelectedAnnotation(getNextAnnotation());
467            }
468    
469            /**
470             * The "previous" annotation is defined as the annotation (if it exists)
471             * that is of the same type as the currently selected annotation that is
472             * 'next' according to the sort order defined by the currently selected
473             * comparator.
474             */
475            public void selectPreviousAnnotation() {
476                    logger.debug("");
477                    setSelectedAnnotation(getPreviousAnnotation());
478            }
479    
480            /**
481             * This is the preferred way to obtain the currently selected spans (i.e.
482             * the spans selected in the text pane.)
483             * 
484             * @return a list of currenly selected spans highlighted by the user
485             */
486            public List<Span> getSelectedSpans() {
487                    logger.debug("");
488                    if (selectedSpans == null)
489                            return Collections.emptyList();
490                    return Collections.unmodifiableList(selectedSpans);
491            }
492    
493            /**
494             * This is the preferred way to set the currently selected spans. This
495             * method will call the appropriate event firing method in EventHandler.
496             * 
497             * @param selectedSpans
498             * @see EventHandler#fireSelectedSpanChanged(List)
499             */
500            public void setSelectedSpans(List<Span> selectedSpans) {
501                    if (selectedSpans == null) {
502                            this.selectedSpans.clear();
503                    } else {
504                            logger.debug("");
505                            if (logger.isDebugEnabled()) {
506                                    StringBuffer sb = new StringBuffer();
507                                    for (Span span : selectedSpans)
508                                            sb.append("  " + span.getStart() + "|" + span.getEnd());
509                                    logger.debug(sb.toString());
510                            }
511                            this.selectedSpans = new ArrayList<Span>(selectedSpans);
512                    }
513                    EventHandler.getInstance().fireSelectedSpanChanged(selectedSpans);
514            }
515    
516            public List<SimpleInstance> getColorAssignments() {
517                    logger.debug("");
518                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
519                    Collection<SimpleInstance> colorAssignments = (Collection<SimpleInstance>) configuration
520                                    .getDirectOwnSlotValues(kpu.getColorAssignmentsSlot());
521                    List<SimpleInstance> returnValues = new ArrayList<SimpleInstance>();
522                    if (colorAssignments != null) {
523                            returnValues.addAll(colorAssignments);
524                    }
525                    return returnValues;
526            }
527    
528            public List<Cls> getRootClses() {
529                    logger.debug("");
530                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
531                    Collection<Cls> rootClses = (Collection<Cls>) configuration.getDirectOwnSlotValues(kpu.getRootClsesSlot());
532                    List<Cls> returnValues = new ArrayList<Cls>();
533                    if (rootClses == null || rootClses.size() == 0) {
534                            returnValues.add(kb.getRootCls());
535                    } else {
536                            returnValues.addAll(rootClses);
537                    }
538                    return returnValues;
539            }
540    
541            public SimpleInstance getSelectedAnnotator() {
542                    logger.debug("");
543                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
544                    return (SimpleInstance) configuration.getDirectOwnSlotValue(kpu.getSelectedAnnotatorSlot());
545            }
546    
547            public void setSelectedAnnotator(SimpleInstance annotator) {
548                    logger.debug("selected annotator = \"" + annotator.getBrowserText() + "\"");
549                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
550                    configuration.setDirectOwnSlotValue(kpu.getSelectedAnnotatorSlot(), annotator);
551            }
552    
553            public SimpleInstance getSelectedAnnotationSet() {
554                    logger.debug("");
555                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
556                    return (SimpleInstance) configuration.getDirectOwnSlotValue(kpu.getSelectedAnnotationSetSlot());
557            }
558    
559            public void setSelectedAnnotationSet(SimpleInstance annotationSet) {
560                    logger.debug("selected annotation set = \"" + annotationSet.getBrowserText() + "\"");
561                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
562                    configuration.setDirectOwnSlotValue(kpu.getSelectedAnnotationSetSlot(), annotationSet);
563            }
564    
565            public String getTokenRegex() {
566                    logger.debug("");
567                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kb.getProject());
568                    return (String) configuration.getDirectOwnSlotValue(kpu.getTokenRegexSlot());
569            }
570    
571            public void currentAnnotationsChanged(CurrentAnnotationsChangeEvent cace) {
572                    logger.debug("");
573                    frameAnnotationsMap.clear();
574                    List<SimpleInstance> currentFilteredAnnotations = cace.getFilteredAnnotations();
575                    logger.debug("currentFilteredAnnotations.size()=" + currentFilteredAnnotations.size());
576                    for (SimpleInstance annotation : currentFilteredAnnotations) {
577                            SimpleInstance mention = annotationUtil.getMention(annotation);
578                            Frame annotatedFrame = mentionUtil.getMentionFrame(mention);
579                            if (!frameAnnotationsMap.containsKey(annotatedFrame))
580                                    frameAnnotationsMap.put(annotatedFrame, new ArrayList<SimpleInstance>());
581                            frameAnnotationsMap.get(annotatedFrame).add(annotation);
582                    }
583            }
584    
585            public void annotationCreated(AnnotationCreatedEvent event) {
586                    logger.debug("");
587                    SimpleInstance annotation = event.getCreatedAnnotation();
588                    SimpleInstance mention = annotationUtil.getMention(annotation);
589                    Frame annotatedFrame = mentionUtil.getMentionFrame(mention);
590                    if (!frameAnnotationsMap.containsKey(annotatedFrame))
591                            frameAnnotationsMap.put(annotatedFrame, new ArrayList<SimpleInstance>());
592                    frameAnnotationsMap.get(annotatedFrame).add(annotation);
593    
594                    int insertionPoint = Collections.binarySearch(filteredAnnotations, annotation, spanUtil
595                                    .comparator(browserTextUtil.comparator()));
596                    if (insertionPoint < 0) {
597                            insertionPoint = -(insertionPoint + 1);
598                    }
599                    filteredAnnotations.add(insertionPoint, annotation);
600                    partiallyFilteredAnnotations.add(annotation);
601                    selectableFilteredAnnotations.add(annotation);
602                    visibleFilteredAnnotations.add(annotation);
603            }
604    
605            /**
606             * @param scrollToSelection please see javadoc for RefreshAnnotationsDisplayListener
607             * @see RefreshAnnotationsDisplayListener#refreshAnnotationsDisplay(boolean)
608             */
609            public void refreshAnnotationsDisplay(boolean scrollToSelection) {
610                    logger.debug("");
611                    EventHandler.getInstance().fireRefreshAnnotationsDisplay(scrollToSelection);
612            }
613    
614            public void deleteSelectedAnnotation() {
615                    logger.debug("");
616                    SimpleInstance nextAnnotation = getNextAnnotation();
617                    if (nextAnnotation.equals(getSelectedAnnotation())) {
618                            nextAnnotation = null;
619                    }
620                    deleteAnnotation(getSelectedAnnotation());
621                    setSelectedAnnotation(nextAnnotation);
622            }
623    
624            public void deleteAnnotation(SimpleInstance annotation) {
625                    logger.debug("");
626                    if (annotation != null) {
627                            annotationUtil.deleteMention((SimpleInstance) annotation);
628                            kb.deleteInstance(annotation);
629                            filteredAnnotations.remove(annotation);
630                            partiallyFilteredAnnotations.remove(annotation);
631                            selectableFilteredAnnotations.remove(annotation);
632                            visibleFilteredAnnotations.remove(annotation);
633                            EventHandler.getInstance().fireCurrentAnnotationsChanged();
634                    }
635            }
636    
637            // TODO what should the cls of the new annotation be?
638            public void duplicateSelectedAnnotation() {
639                    logger.debug("");
640                    SimpleInstance annotation = getSelectedAnnotation();
641                    if (annotation != null) {
642                            logger.debug("creating duplicate annotation");
643                            SimpleInstance duplicateAnnotation;
644                            try {
645                                    SimpleInstance mention = mentionUtil.createMention(null);
646                                    List<Span> spans = annotationUtil.getSpans(annotation);
647    
648                                    duplicateAnnotation = annotationUtil.createAnnotation(mention, getSelectedAnnotator(), spans,
649                                                    textSourceUtil.getCurrentTextSource(), getSelectedAnnotationSet());
650                                    if (duplicateAnnotation == null)
651                                            return;
652    
653                                    EventHandler.getInstance().fireAnnotationCreated(duplicateAnnotation);
654                                    setSelectedAnnotation(duplicateAnnotation);
655                                    refreshAnnotationsDisplay(true);
656                            }
657                            // TODO why is the parent component of this dialog null?
658                            catch (TextSourceAccessException tsae) {
659                                    JOptionPane.showMessageDialog(null, "There was a problem retrieving the text from the text source: "
660                                                    + tsae.getMessage(), "Text Source Error", JOptionPane.ERROR_MESSAGE);
661                            }
662                    }
663            }
664    
665            /**
666             * calls createAnnotation(selectedClsOrInstance, true)
667             * 
668             * @param selectedClsOrInstance
669             *            - a Cls or Instance frame in Protege that specifies the type
670             *            of annotation to create. all other settings for the new
671             *            annotation will be derived from the current settings and
672             *            selections.
673             * @return the created annotation
674             */
675            public SimpleInstance createAnnotation(Instance selectedClsOrInstance) {
676                    logger.debug("");
677                    return createAnnotation(selectedClsOrInstance, true);
678            }
679    
680            /**
681             * @param selectedClsOrInstance
682             * @param selectCreatedAnnotation
683             *            determines whether the newly created annotation should be
684             *            selected. If the new annotation is a slot filler, then you
685             *            might not want it to be selected as the annotation of focus
686             *            for Knowtator.
687             * @return the created annotation or null if something went wrong.
688             */
689            public SimpleInstance createAnnotation(Instance selectedClsOrInstance, boolean selectCreatedAnnotation) {
690                    logger.debug("creating annotation for \"" + selectedClsOrInstance.getBrowserText() + "\"");
691                    SimpleInstance createdAnnotation;
692                    try {
693                            SimpleInstance mention = mentionUtil.createMention(selectedClsOrInstance);
694                            List<Span> selectedSpans = getSelectedSpans();
695    
696                            createdAnnotation = annotationUtil.createAnnotation(mention, getSelectedAnnotator(), selectedSpans,
697                                            textSourceUtil.getCurrentTextSource(), getSelectedAnnotationSet());
698                            if (createdAnnotation == null)
699                                    return null;
700    
701                            EventHandler.getInstance().fireAnnotationCreated(createdAnnotation);
702                            if (selectCreatedAnnotation)
703                                    setSelectedAnnotation(createdAnnotation);
704    
705                            refreshAnnotationsDisplay(true);
706                            return createdAnnotation;
707                    }
708                    // TODO why is the parent component of this dialog null?
709                    catch (TextSourceAccessException tsae) {
710                            JOptionPane.showMessageDialog(null, "There was a problem retrieving the text from the text source: "
711                                            + tsae.getMessage(), "Text Source Error", JOptionPane.ERROR_MESSAGE);
712                            return null;
713                    }
714            }
715    
716            public AnnotationUtil getAnnotationUtil() {
717                    logger.debug("");
718                    return annotationUtil;
719            }
720    
721            public BrowserTextUtil getBrowserTextUtil() {
722                    logger.debug("");
723                    return browserTextUtil;
724            }
725    
726            public DisplayColors getDisplayColors() {
727                    logger.debug("");
728                    return displayColors;
729            }
730    
731            public FilterUtil getFilterUtil() {
732                    logger.debug("");
733                    return filterUtil;
734            }
735    
736            public MentionUtil getMentionUtil() {
737                    logger.debug("");
738                    return mentionUtil;
739            }
740    
741            public SpanUtil getSpanUtil() {
742                    logger.debug("");
743                    return spanUtil;
744            }
745    
746            public TextSourceUtil getTextSourceUtil() {
747                    logger.debug("");
748                    return textSourceUtil;
749            }
750    
751            public KnowtatorProjectUtil getKnowtatorProjectUtil() {
752                    logger.debug("");
753                    return kpu;
754            }
755    
756            public ColorFrameRenderer getRenderer() {
757                    logger.debug("");
758                    return renderer;
759            }
760    
761            public KnowtatorTextPane getTextPane() {
762                    logger.debug("");
763                    return textPane;
764            }
765    
766            public void setTextPane(KnowtatorTextPane textPane) {
767                    logger.debug("");
768                    this.textPane = textPane;
769            }
770    
771            public Comparator<SimpleInstance> getAnnotationComparator() {
772                    logger.debug("");
773                    return annotationComparator;
774            }
775    
776            public Comparator<SimpleInstance> getPositionComparator(SimpleInstance annotation) {
777                    logger.debug("");
778                    return textViewer.comparator(annotation);
779            }
780    
781            public void setAnnotationComparator(Comparator<SimpleInstance> annotationComparator) {
782                    logger.debug("");
783                    this.annotationComparator = annotationComparator;
784            }
785    
786            /**
787             * 
788             * @return the fast annotation cls. May return null if it has not been set
789             *         yet (or set to null).
790             */
791            public Frame getFastAnnotateFrame() {
792                    logger.debug("");
793                    return fastAnnotateFrame;
794            }
795    
796            public void setFastAnnotateFrame(Frame fastAnnotateFrame) {
797                    logger.debug("");
798                    this.fastAnnotateFrame = fastAnnotateFrame;
799                    if (fastAnnotateFrame != null) {
800                            fastAnnotateFrameSet.add(fastAnnotateFrame.getFrameID());
801                    }
802    
803                    EventHandler.getInstance().fireFastAnnotateAddCls(fastAnnotateFrame);
804                    EventHandler.getInstance().fireFastAnnotateClsChange();
805            }
806    
807            public void startFastAnnotate() {
808                    logger.debug("");
809                    fastAnnotateMode = true;
810                    EventHandler.getInstance().fireFastAnnotateStart();
811            }
812    
813            /**
814             * This method allows you to start fast annotation mode and set the fast
815             * annotation class in one convenient method. If this method is called when
816             * KnowtatorManager.isFastAnnotateMode() is true, then this method has the
817             * same effect as calling setFastAnnotateCls.
818             * 
819             * @param fastAnnotateFrame
820             *            the class with which to annotate with
821             * @see #isFastAnnotateMode()
822             * @see #setFastAnnotateFrame(Frame)
823             */
824            public void startFastAnnotate(Frame fastAnnotateFrame) {
825                    logger.debug("");
826                    setFastAnnotateFrame(fastAnnotateFrame);
827    
828                    if (!fastAnnotateMode) {
829                            fastAnnotateMode = true;
830                            EventHandler.getInstance().fireFastAnnotateStart();
831                    }
832            }
833    
834            /**
835             * Removes the given frame (class) from the fast annotate tool bar.
836             * 
837             * @param frame
838             *            The frame (or Cls) to be removed.
839             */
840            public void removeFastAnnotateFrame(Frame frame) {
841                    if (frame != null) {
842                            fastAnnotateFrameSet.remove(frame.getFrameID());
843                    }
844                    EventHandler.getInstance().fireFastAnnotateRemoveCls(frame);
845            }
846    
847            /**
848             * Finds out if the given frame is being shown in the fast annotate tool
849             * bar.
850             * 
851             * @param frame
852             *            The frame to lookup
853             * @return True if the frame (Cls) is being shown in the tool bar, returns
854             *         false otherwise.
855             */
856            public boolean fastAnnotateToolBarContains(Frame frame) {
857                    if (frame != null) {
858                            return fastAnnotateFrameSet.contains(frame.getFrameID());
859                    } else {
860                            return false;
861                    }
862            }
863    
864            public void quitFastAnnotate() {
865                    logger.debug("");
866                    fastAnnotateMode = false;
867                    EventHandler.getInstance().fireFastAnnotateQuit();
868            }
869    
870            public boolean isFastAnnotateMode() {
871                    logger.debug("");
872                    return fastAnnotateMode;
873            }
874    
875            public void setSelectedFilter(SimpleInstance filter) throws ConsensusException {
876                    logger.debug("");
877                    if (filter == null)
878                            logger.debug("filter == null");
879                    selectedFilter = filter;
880                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
881                    configuration.setDirectOwnSlotValue(kpu.getSelectedFilterSlot(), selectedFilter);
882                    updateCurrentAnnotations();
883    
884                    if (!getActiveFilters().contains(filter))
885                            addActiveFilter(filter);
886    
887                    if (FilterUtil.isConsensusFilter(filter)) {
888                            Set<SimpleInstance> consensusAnnotations = new HashSet<SimpleInstance>(getCurrentFilteredAnnotations());
889                            SimpleInstance consensusSetInstance = (SimpleInstance) getSelectedFilter().getOwnSlotValue(
890                                            kpu.getFilterSetSlot());
891                            consensusSet = new ConsensusSet(consensusAnnotations, consensusSetInstance, this);
892                            consensusMode = true;
893                            kpu.displayAnnotationAuthor();
894                    } else {
895                            consensusMode = false;
896                    }
897                    EventHandler.getInstance().fireFilterChanged(filter, consensusMode);
898    
899                    // List<SimpleInstance> filterAnnotators =
900                    // filterUtil.getAnnotators(filter);
901                    // if(filterAnnotators != null && filterAnnotators.size() > 0)
902                    // {
903                    // setSelectedAnnotator(filterAnnotators.get(0));
904                    // }
905    
906            }
907    
908            public SimpleInstance getSelectedFilter() {
909                    logger.debug("");
910                    if (selectedFilter != null)
911                            return selectedFilter;
912    
913                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
914                    SimpleInstance configurationFilter = (SimpleInstance) configuration.getDirectOwnSlotValue(kpu
915                                    .getSelectedFilterSlot());
916                    if (configurationFilter == null) {
917                            SimpleInstance showAllFilter = kpu.getShowAllFilter();
918                            try {
919                                    setSelectedFilter(showAllFilter);
920                            } catch (ConsensusException ce) {
921                                    ce.printStackTrace();
922                            } // should never throw a consensus exception here
923                            return showAllFilter;
924                    } else {
925                            try {
926                                    setSelectedFilter(configurationFilter);
927                                    return configurationFilter;
928                            } catch (ConsensusException ce) {
929                                    SimpleInstance showAllFilter = kpu.getShowAllFilter();
930                                    try {
931                                            setSelectedFilter(showAllFilter);
932                                    } catch (ConsensusException ex) {
933                                            ex.printStackTrace();
934                                    } // should never throw a consensus exception here
935                                    return showAllFilter;
936                            }
937                    }
938            }
939    
940            public List<SimpleInstance> getActiveFilters() {
941                    logger.debug("");
942                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
943                    Collection<SimpleInstance> activeFilters = (Collection<SimpleInstance>) configuration
944                                    .getDirectOwnSlotValues(kpu.getActiveFiltersSlot());
945                    List<SimpleInstance> returnValues = new ArrayList<SimpleInstance>();
946                    if (activeFilters != null && activeFilters.size() > 0) {
947                            returnValues.addAll(activeFilters);
948                    } else {
949                            SimpleInstance selectedFilter = null;
950                            selectedFilter = getSelectedFilter();
951                            returnValues.add(getSelectedFilter());
952                            addActiveFilter(selectedFilter);
953                    }
954                    if (returnValues.size() == 0) {
955                            returnValues.add(kpu.getShowAllFilter());
956                            returnValues.add(kpu.getShowNoneFilter());
957                    }
958                    return returnValues;
959            }
960    
961            public void setActiveFilters(Collection<SimpleInstance> activeFilters) {
962                    logger.debug("");
963                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
964                    configuration.setDirectOwnSlotValues(kpu.getActiveFiltersSlot(), activeFilters);
965            }
966    
967            public void addActiveFilter(SimpleInstance filter) {
968                    logger.debug("");
969                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
970                    configuration.addOwnSlotValue(kpu.getActiveFiltersSlot(), filter);
971            }
972    
973            public boolean getFadeUnselectedAnnotations() {
974                    logger.debug("");
975                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
976                    Boolean fadeUnselectedAnnotations = (Boolean) configuration.getDirectOwnSlotValue(kpu
977                                    .getFadeUnselectedAnnotationsSlot());
978                    if (fadeUnselectedAnnotations == null)
979                            return false;
980                    return fadeUnselectedAnnotations;
981            }
982    
983            public Slot getSubtextSlot() {
984                    logger.debug("");
985                    SimpleInstance configuration = ProjectSettings.getActiveConfiguration(kpu.kb.getProject());
986                    return (Slot) configuration.getDirectOwnSlotValue(kpu.getSubtextSlotSlot());
987            }
988    
989            public void restartConsensusMode() throws ConsensusException {
990                    logger.debug("");
991                    if (isConsensusMode()) {
992                            consensusSet = ConsensusAnnotations.recreateConsensusAnnotations(textSourceUtil.getTextSourceInstance(
993                                            textSourceUtil.getCurrentTextSource(), false), getSelectedAnnotationSet(), this);
994                    }
995            }
996    
997            public boolean isConsensusMode() {
998                    logger.debug("");
999                    return consensusMode;
1000            }
1001    
1002            public int getConsensusModeProgress() {
1003                    logger.debug("");
1004                    if (isConsensusMode()) {
1005                            List<SimpleInstance> annotations = getCurrentFilteredAnnotations();
1006                            SimpleInstance teamAnnotator = consensusSet.getTeamAnnotator();
1007                            int progress = 0;
1008                            for (SimpleInstance annotation : annotations) {
1009                                    if (teamAnnotator.equals(annotationUtil.getAnnotator(annotation)))
1010                                            progress++;
1011                            }
1012                            return progress;
1013                    } else
1014                            return -1;
1015            }
1016    
1017            public void textSourceChanged(TextSourceChangeEvent event) {
1018                    logger.debug("");
1019                    updateCurrentAnnotations();
1020                    setSelectedAnnotation(null);
1021    
1022                    if (isConsensusMode()) {
1023                            try {
1024                                    Set<SimpleInstance> consensusAnnotations = new HashSet<SimpleInstance>(getCurrentFilteredAnnotations());
1025                                    SimpleInstance consensusSetInstance = (SimpleInstance) getSelectedFilter().getOwnSlotValue(
1026                                                    kpu.getFilterSetSlot());
1027                                    consensusSet = new ConsensusSet(consensusAnnotations, consensusSetInstance, this);
1028                            } catch (ConsensusException ce) {
1029                                    ce.printStackTrace();
1030                            }
1031                    }
1032                    refreshAnnotationsDisplay(true);
1033            }
1034    
1035            public void consolidateAnnotation(SimpleInstance deleteAnnotation, SimpleInstance consolidateAnnotation) {
1036                    logger.debug("");
1037                    consensusSet.consolidateAnnotations(consolidateAnnotation, deleteAnnotation);
1038                    setSelectedAnnotation(consolidateAnnotation);
1039            }
1040    
1041            public void setTextViewer(TextViewer textViewer) {
1042                    logger.debug("");
1043                    this.textViewer = textViewer;
1044            }
1045    
1046            public boolean isAnnotationVisible(SimpleInstance annotation) {
1047                    logger.debug("");
1048                    return textViewer.isVisible(annotation);
1049            }
1050    
1051            public Span getVisibleSpan() {
1052                    logger.debug("");
1053                    if (textViewer == null)
1054                            return null;
1055                    return textViewer.getVisibleSpan();
1056            }
1057    
1058            public int getVerticalDistance(SimpleInstance annotation1, SimpleInstance annotation2) {
1059                    logger.debug("");
1060                    return textViewer.getVerticalDistance(annotation1, annotation2);
1061            }
1062    
1063    }