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     *   Angus Roberts
028     */
029    
030    /**
031     * Changes:
032     *  2/7/2007    ar         updated handleAddAction to handle inverse slots
033     *  2/7/2007    ar         added handleRemoveAction
034     *  9/7/2005    pvo    added handleCreateAction which allows one to highlight a span of text and 
035     *                     click the 'Create Instance' button for an annotations slot value and create
036     *                     an annotation for that slot at the selected span.
037     *  9/7/2005    pvo    updated the SelectAnnotationsFromCollectionPanel such that mousing over an annotation
038     *                     in the panel causes the corresponding text in the text viewer to be highlighted.
039     *  8/10/2005   pvo    list now uses ListCellRenderer obtained from the KB.getClientInformation()
040     *                     this was done in conjunction with creation of the BrowserTextUtil code
041     *  8/11/2005   pvo    the handleAddAction calls pickAnnotationsFromCollection to ask for an annotation instead of the
042     *                     previously used DisplayUtilities.pickInstancesFromCollection.
043     *                     the method was copied from the DisplayUtilities and modified so that the 
044     *                     displayed list uses the desired ListCellRenderer.
045     */
046    
047    package edu.uchsc.ccp.knowtator;
048    
049    import java.util.Collection;
050    import java.util.Collections;
051    import java.util.Comparator;
052    import java.util.HashSet;
053    import java.util.List;
054    import java.util.Set;
055    
056    import javax.swing.JOptionPane;
057    
058    import org.apache.log4j.Logger;
059    
060    import edu.stanford.smi.protege.model.Cls;
061    import edu.stanford.smi.protege.model.Instance;
062    import edu.stanford.smi.protege.model.KnowledgeBase;
063    import edu.stanford.smi.protege.model.SimpleInstance;
064    import edu.stanford.smi.protege.model.Slot;
065    import edu.stanford.smi.protege.model.ValueType;
066    import edu.stanford.smi.protege.util.CollectionUtilities;
067    import edu.stanford.smi.protege.util.DoubleClickListener;
068    import edu.stanford.smi.protege.widget.InstanceListWidget;
069    import edu.uchsc.ccp.knowtator.ui.AnnotationPicker;
070    import edu.uchsc.ccp.knowtator.util.ProtegeUtil;
071    
072    public class ComplexSlotMentionValueWidget extends InstanceListWidget implements DoubleClickListener {
073    
074            static final long serialVersionUID = 0;
075    
076            KnowtatorManager manager;
077    
078            MentionUtil mentionUtil;
079    
080            AnnotationUtil annotationUtil;
081    
082            KnowtatorProjectUtil kpu;
083    
084            TextSourceUtil textSourceUtil;
085    
086            BrowserTextUtil browserTextUtil;
087    
088            KnowledgeBase kb;
089    
090            DisplayColors displayColors;
091    
092            Logger logger = Logger.getLogger(ComplexSlotMentionValueWidget.class);
093    
094            public void initialize() {
095                    super.initialize();
096                    manager = (KnowtatorManager) getKnowledgeBase().getClientInformation(Knowtator.KNOWTATOR_MANAGER);
097                    mentionUtil = manager.getMentionUtil();
098                    annotationUtil = manager.getAnnotationUtil();
099                    kpu = manager.getKnowtatorProjectUtil();
100                    textSourceUtil = manager.getTextSourceUtil();
101                    browserTextUtil = manager.getBrowserTextUtil();
102                    displayColors = manager.getDisplayColors();
103    
104                    kb = kpu.getKnowledgeBase();
105    
106                    setDoubleClickListener(this);
107                    // we want the list to use our cell renderer
108                    setRenderer(manager.getRenderer()); // this does nothing because it is
109                                                                                            // reset in the super class
110                                                                                            // AbstractSlotWidget.setInstance
111                                                                                            // method.
112            }
113    
114            public void setValues(Collection values) {
115                    super.setValues(values);
116                    try {
117                            SimpleInstance slotMention = (SimpleInstance) getInstance();
118                            Slot mentionSlot = mentionUtil.getSlotMentionSlot(slotMention);
119                            if (mentionSlot == null)
120                                    return;
121                            SimpleInstance mentionedByMention = mentionUtil.getMentionedBy(slotMention);
122                            Cls mentionCls = mentionUtil.getMentionCls(mentionedByMention);
123                            if (mentionCls == null)
124                                    return;
125    
126                            // this code doesn't work for some reason. It is supposed to paint
127                            // the red border
128                            // around this slot
129                            // int minCardinality =
130                            // mentionCls.getTemplateSlotMinimumCardinality(mentionSlot);
131                            // int maxCardinality =
132                            // mentionCls.getTemplateSlotMaximumCardinality(mentionSlot);
133                            //            
134                            // if(values.size() < minCardinality || values.size() >
135                            // maxCardinality)
136                            // setInvalidValueBorder();
137                            // else
138                            // setNormalBorder();
139    
140                            getLabeledComponent().setHeaderLabel(ProtegeUtil.getSlotLabel(mentionCls, mentionSlot, getProject()));
141                    } catch (NullPointerException npe) {
142                            getLabeledComponent().setHeaderLabel("");
143                    }
144            }
145    
146            public void setInstance(Instance instance) {
147                    super.setInstance(instance);
148                    // the renderer is set in the super class AbstractSlotWidget - we must
149                    // set it to our renderer to get it to show up.
150                    setRenderer(manager.getRenderer());
151            }
152    
153            @Override
154            protected void handleViewAction(Instance instance) {
155                    logger.debug(browserTextUtil.getBrowserText((SimpleInstance) instance, 100));
156                    SimpleInstance annotation = mentionUtil.getMentionAnnotation((SimpleInstance) instance);
157                    manager.setSelectedAnnotation(annotation);
158            }
159    
160            public void onDoubleClick(Object item) {
161                    if (item instanceof SimpleInstance)
162                            handleViewAction((SimpleInstance) item);
163            }
164    
165            protected void handleCreateAction() {
166                    // slotMention is the mention that corresponds to the instance handled
167                    // by this widget
168                    SimpleInstance slotMention = (SimpleInstance) getInstance();
169                    // mentionSlot is the slot corresponding to the slotMention
170                    Slot mentionSlot = mentionUtil.getSlotMentionSlot(slotMention);
171                    if (mentionSlot == null) {
172                            showSlotMissingMessage();
173                            return;
174                    }
175    
176                    // mention has slotMention as a value of one of its
177                    // knowtator_slot_mention
178                    SimpleInstance mention = mentionUtil.getMentionedBy(slotMention);
179                    // mentionCls is the cls of the mention
180                    Cls mentionCls = mentionUtil.getMentionCls(mention);
181                    if (mentionCls == null) {
182                            showTypeMissingMessage(mention);
183                            return;
184                    }
185    
186                    if (!checkValueTypeConstraint(mentionCls, mentionSlot))
187                            return;
188    
189                    // Get the classes that are allowed by this class at this slot.
190                    // This assumes the slot is constrained to be an instance of a cls and
191                    // does not account for
192                    // the possibility that the slot may be constrained to be a cls.
193                    Set<Cls> allowedClses = new HashSet<Cls>((Collection<Cls>) mentionCls.getTemplateSlotAllowedClses(mentionSlot));
194    
195                    // this code needs to be expanded in two ways - if the slot of the cls
196                    // is of type cls then
197                    // we need to call mentionCls.getTemplateSlotAllowedParents(slot) and
198                    // make sure that instances are not displayed for selection.
199                    // if the slot of the cls is of type instance then we should display the
200                    // annotations corresponding to instances for selection.
201                    if (allowedClses == null || allowedClses.size() < 1) {
202                            allowedClses = new HashSet<Cls>(manager.getRootClses());
203                    }
204    
205                    Set<Cls> descendants = new HashSet<Cls>();
206                    for (Cls allowedCls : allowedClses) {
207                            descendants.addAll(allowedCls.getSubclasses());
208                    }
209    
210                    descendants.addAll(allowedClses);
211    
212                    Cls newMentionCls;
213                    if (descendants.size() > 1) {
214                            newMentionCls = edu.stanford.smi.protege.ui.DisplayUtilities.pickCls(this, kb, allowedClses,
215                                            "Choose type for new annotation filling in the slot");
216                    } else {
217                            newMentionCls = (Cls) CollectionUtilities.getFirstItem(descendants);
218                    }
219    
220                    if (newMentionCls == null)
221                            return;
222    
223                    SimpleInstance newAnnotation = manager.createAnnotation(newMentionCls, false);
224    
225                    SimpleInstance newMention = annotationUtil.getMention(newAnnotation);
226    
227                    addItem(newMention);
228                    mentionUtil.addInverse(mention, mentionSlot, newMention);
229                    mentionUtil.adjustSlotMentionForCardinality(mentionCls, mentionSlot, mention);
230                    manager.updateCurrentAnnotations();
231            }
232    
233            private boolean checkValueTypeConstraint(Cls cls, Slot slot) {
234                    ValueType type = cls.getTemplateSlotValueType(slot);
235                    if (type == ValueType.INSTANCE || type == ValueType.CLS)
236                            return true;
237                    else {
238                            JOptionPane.showMessageDialog(this, "It appears that the value type constraint\n"
239                                            + "has changed since the slot values for this\n" + "slot were entered.\n"
240                                            + "Please remove the slot values for this slot,\n"
241                                            + "select a different annotation, and re-select\n " + "the currently selected annotation.",
242                                            "value type constraint inconsistency", JOptionPane.WARNING_MESSAGE);
243                            return false;
244                    }
245            }
246    
247            private void showTypeMissingMessage(SimpleInstance mention) {
248                    if (mentionUtil.isClassMention(mention))
249                            JOptionPane.showMessageDialog(this, "There is no class assigned to this annotation.\n"
250                                            + "You may not add a value to this slot until a \n" + "class is assigned.", "No class assigned",
251                                            JOptionPane.WARNING_MESSAGE);
252                    else if (mentionUtil.isInstanceMention(mention))
253                            JOptionPane.showMessageDialog(this, "There is no instance assigned to this annotation.\n"
254                                            + "You may not add a value to this slot until an \n" + "instance is assigned.",
255                                            "No instance assigned", JOptionPane.WARNING_MESSAGE);
256    
257            }
258    
259            private void showSlotMissingMessage() {
260                    JOptionPane.showMessageDialog(this, "There is not a slot specified for this slot value.\n"
261                                    + "This is most likely a result of deleting a slot \n"
262                                    + "from the annotation schema after this annotation \n"
263                                    + "was created.  Please remove this slot value, select\n"
264                                    + "another annotation, and re-select this annotation.", "Slot missing", JOptionPane.WARNING_MESSAGE);
265            }
266    
267            /**
268             * At the moment the only value you can add is a mention from another
269             * annotation. First, loop through all of the available annotations, then
270             * choose only those that make sense for the slot.
271             * 
272             * why isn't the allowedClses filled with getTemplateSlotAllowedParents too?
273             */
274            protected void handleAddAction() {
275                    SimpleInstance slotMention = (SimpleInstance) getInstance();
276                    Slot mentionSlot = mentionUtil.getSlotMentionSlot(slotMention);
277                    if (mentionSlot == null) {
278                            showSlotMissingMessage();
279                            return;
280                    }
281                    SimpleInstance mention = mentionUtil.getMentionedBy(slotMention);
282                    Cls mentionCls = mentionUtil.getMentionCls(mention);
283                    if (mentionCls == null) {
284                            showTypeMissingMessage(mention);
285                            return;
286                    }
287    
288                    if (!checkValueTypeConstraint(mentionCls, mentionSlot))
289                            return;
290    
291                    List<SimpleInstance> partiallyFilteredAnnotations = manager.getCurrentPartiallyFilteredAnnotations();
292                    List<SimpleInstance> annotations = mentionUtil.getSlotFillerCandidates(mention, mentionSlot,
293                                    partiallyFilteredAnnotations);
294    
295                    if (annotations.size() == 0) {
296                            if (getList().getModel().getSize() == 0)
297                                    JOptionPane
298                                                    .showMessageDialog(
299                                                                    this,
300                                                                    "There are no annotations available that satisfy the constraints for this slot on this annotation.",
301                                                                    "No appropriate annotations", JOptionPane.INFORMATION_MESSAGE);
302                            else
303                                    JOptionPane
304                                                    .showMessageDialog(
305                                                                    this,
306                                                                    "There are no other annotations available that satisfy the constraints for this slot on this annotation.",
307                                                                    "No appropriate annotations", JOptionPane.INFORMATION_MESSAGE);
308    
309                            return;
310                    } else if (annotations.size() == 1) {
311                            SimpleInstance annotationInstance = annotations.get(0);
312                            SimpleInstance annotationMention = annotationUtil.getMention(annotationInstance);
313                            addItem(annotationMention);
314                            mentionUtil.addInverse(mention, mentionSlot, annotationMention);
315                    } else {
316                            Comparator comparator = manager.getAnnotationComparator();
317                            Collections.sort(annotations, comparator);
318                            List<SimpleInstance> pickedAnnotations = AnnotationPicker.pickAnnotationsFromCollection(this, manager,
319                                            annotations, "select annotation for '" + mentionSlot.getBrowserText() + "'");
320    
321                            for (SimpleInstance annotation : pickedAnnotations) {
322                                    SimpleInstance annotationMention = annotationUtil.getMention(annotation);
323                                    addItem(annotationMention);
324                                    mentionUtil.addInverse(mention, mentionSlot, annotationMention);
325                            }
326                    }
327    
328                    mentionUtil.adjustSlotMentionForCardinality(mentionCls, mentionSlot, mention);
329    
330                    // int maxCardinality =
331                    // mentionCls.getTemplateSlotMaximumCardinality(mentionSlot);
332                    // if(maxCardinality > 0)
333                    // {
334                    // ArrayList values = new ArrayList(getValues());
335                    // removeAllItems();
336                    // for(int i=values.size()-1, j=0; i >=0 && j<maxCardinality; i--)
337                    // {
338                    // addItem(values.get(i));
339                    // j++;
340                    // }
341                    // }
342                    manager.refreshAnnotationsDisplay(true);
343    
344            }
345    
346            protected void handleRemoveAction(Collection instances) {
347                    super.handleRemoveAction(instances);
348    
349                    SimpleInstance slotMention = (SimpleInstance) getInstance();
350                    Slot mentionSlot = mentionUtil.getSlotMentionSlot(slotMention);
351                    if (mentionSlot != null) {
352                            SimpleInstance mentionedByMention = mentionUtil.getMentionedBy(slotMention);
353    
354                            for (Object annotationMentionObj : instances)
355                                    mentionUtil.removeInverse(mentionedByMention, mentionSlot, (SimpleInstance) annotationMentionObj);
356                    }
357                    manager.refreshAnnotationsDisplay(true);
358            }
359    }