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.awt.Component;
032    import java.io.PrintWriter;
033    import java.io.StringWriter;
034    import java.lang.reflect.Field;
035    import java.lang.reflect.Method;
036    import java.util.ArrayList;
037    import java.util.Collection;
038    import java.util.HashMap;
039    
040    import javax.swing.JOptionPane;
041    
042    import org.apache.log4j.Logger;
043    
044    import edu.stanford.smi.protege.model.Instance;
045    import edu.stanford.smi.protege.model.KnowledgeBase;
046    import edu.stanford.smi.protege.model.Project;
047    import edu.stanford.smi.protege.model.SimpleInstance;
048    import edu.uchsc.ccp.knowtator.textsource.TextSource;
049    import edu.uchsc.ccp.knowtator.textsource.TextSourceAccessException;
050    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeEvent;
051    import edu.uchsc.ccp.knowtator.textsource.TextSourceChangeListener;
052    import edu.uchsc.ccp.knowtator.textsource.TextSourceCollection;
053    import edu.uchsc.ccp.knowtator.textsource.TextSourceCollectionChangeEvent;
054    import edu.uchsc.ccp.knowtator.textsource.TextSourceCollectionChangeListener;
055    
056    /**
057     * This class helps one to find a "text source" instance in the knowledgebase
058     * given a TextSource object or vice versa.
059     * 
060     * In order to make this as simple to code up as possible, I made some
061     * simplifying assumptions which result in less flexibility for the user.
062     * 
063     * One is that all instances of "text source" share the same namespace - and
064     * there should be no overlap - even across different types of "text source"
065     * instances.
066     * 
067     * A user must know where a to find a TextSource given its name if the code
068     * cannot find it in the TextSouceCollection that it has open.
069     * 
070     * 
071     */
072    
073    public class TextSourceUtil {
074    
075            AnnotationUtil annotationUtil;
076    
077            KnowtatorProjectUtil kpu;
078    
079            KnowledgeBase kb;
080    
081            Project project;
082    
083            Class[] textSourceCollectionClasses;
084    
085            String[] displayNames;
086    
087            HashMap displayNames2TSC;
088    
089            String[] clsNames;
090    
091            HashMap clsNames2TSC;
092    
093            TextSource currentTextSource;
094    
095            java.util.List<TextSourceChangeListener> textSourceChangeListeners;
096    
097            TextSourceCollection currentTextSourceCollection;
098    
099            java.util.List<TextSourceCollectionChangeListener> textSourceCollectionChangeListeners;
100    
101            Logger logger = Logger.getLogger(TextSourceUtil.class);
102    
103            public TextSourceUtil(AnnotationUtil annotationUtil, KnowtatorProjectUtil kpu) {
104                    this.annotationUtil = annotationUtil;
105                    this.kpu = kpu;
106                    this.kb = annotationUtil.kb;
107                    this.project = kb.getProject();
108    
109                    textSourceChangeListeners = new ArrayList<TextSourceChangeListener>();
110                    textSourceCollectionChangeListeners = new ArrayList<TextSourceCollectionChangeListener>();
111    
112                    java.util.List<Class> classes = new ArrayList<Class>();
113    
114                    Collection<Instance> tscImplementations = (Collection<Instance>) kb
115                                    .getInstances(kpu.getTscImplementationsCls());
116    
117                    String[] textSourceCollectionClassNames = new String[tscImplementations.size()];
118    
119                    int _tsc = 0;
120                    for (Instance tscImplementation : tscImplementations) {
121                            String className = (String) tscImplementation.getOwnSlotValue(kb.getSlot("knowtator_tsc_implementation"));
122                            textSourceCollectionClassNames[_tsc++] = className;
123                    }
124    
125                    Class textSourceCollectionClass = null;
126                    try {
127                            textSourceCollectionClass = Class.forName("edu.uchsc.ccp.knowtator.textsource.TextSourceCollection");
128                    } catch (ClassNotFoundException cnfe) {
129                            cnfe.printStackTrace();
130                    }
131    
132                    for (int i = 0; i < textSourceCollectionClassNames.length; i++) {
133                            try {
134                                    Class tscClass = Class.forName(textSourceCollectionClassNames[i]);
135                                    if (isSubclass(tscClass, textSourceCollectionClass)) {
136                                            classes.add(tscClass);
137                                    } else
138                                            System.err.println("WARNING: class " + textSourceCollectionClassNames[i]
139                                                            + " is not a subclass of edu.uchsc.ccp.knowtator.textsource.TextSourceCollection");
140                            } catch (ClassNotFoundException cnfe) {
141                                    System.err.println("WARNING: class " + textSourceCollectionClassNames[i] + " not found in classpath");
142                            }
143                    }
144                    textSourceCollectionClasses = (Class[]) (classes.toArray(new Class[classes.size()]));
145                    displayNames = new String[textSourceCollectionClasses.length];
146                    displayNames2TSC = new HashMap(textSourceCollectionClasses.length);
147                    clsNames = new String[textSourceCollectionClasses.length];
148                    clsNames2TSC = new HashMap(textSourceCollectionClasses.length);
149    
150                    for (int i = 0; i < textSourceCollectionClasses.length; i++) {
151                            try {
152                                    Field displayNameField = textSourceCollectionClasses[i].getField("DISPLAY_NAME");
153                                    String displayName = (String) displayNameField.get(null);
154                                    displayNames[i] = displayName;
155                                    displayNames2TSC.put(displayName, textSourceCollectionClasses[i]);
156    
157                                    Field clsNameField = textSourceCollectionClasses[i].getField("CLS_NAME");
158                                    String clsName = (String) clsNameField.get(null);
159                                    clsNames[i] = clsName;
160                                    clsNames2TSC.put(clsName, textSourceCollectionClasses[i]);
161    
162                                    Method createClsMethod = textSourceCollectionClasses[i].getMethod("createCls",
163                                                    new Class[] { KnowledgeBase.class });
164                                    createClsMethod.invoke(null, kb);
165                            } catch (Exception exception) {
166                                    exception.printStackTrace();
167                            }
168                    }
169                    logger.debug("made it to here");
170    
171            }
172    
173            public void init() {
174                    TextSourceCollection tsc = ProjectSettings.getRecentTextSourceCollection(project);
175                    if (tsc != null) {
176                            int recentIndex = ProjectSettings.getRecentTextSourceCollectionIndex(project);
177    
178                            setCurrentTextSourceCollection(tsc);
179    
180                            TextSource recentTextSource = null;
181                            try {
182                                    recentTextSource = tsc.get(recentIndex);
183                            } catch (TextSourceAccessException tsae) {
184                                    try {
185                                            recentTextSource = tsc.get(0);
186                                    } catch (TextSourceAccessException e) {
187                                    }
188                            }
189                            if (recentTextSource != null) {
190                                    setCurrentTextSource(recentTextSource);
191                            }
192                    }
193            }
194    
195            public TextSourceCollection selectTextSourceCollection(Component parent) {
196                    Object selection = JOptionPane.showInputDialog(parent, "Please select a text source type.",
197                                    "Text source type selection", JOptionPane.PLAIN_MESSAGE, null, displayNames, displayNames[0]);
198    
199                    TextSourceCollection newTextSourceCollection;
200    
201                    try {
202                            if (selection != null) {
203                                    Class textSourceCollectionClass = (Class) displayNames2TSC.get(selection);
204                                    Method selectionMethod = textSourceCollectionClass.getMethod("open", new Class[] { Project.class,
205                                                    Component.class });
206                                    newTextSourceCollection = (TextSourceCollection) selectionMethod.invoke(null, new Object[] { project,
207                                                    parent });
208                                    return newTextSourceCollection;
209                            }
210                    } catch (Exception exception) {
211                            StringWriter stackTraceWriter = new StringWriter();
212                            exception.printStackTrace(new PrintWriter(stackTraceWriter));
213                            String stackTrace = stackTraceWriter.toString();
214                            JOptionPane.showMessageDialog(parent, "Error opening text source collection:  Stack trace = " + stackTrace,
215                                            "Error opening text source collection", JOptionPane.ERROR_MESSAGE, null);
216                            exception.printStackTrace();
217                    }
218                    return null;
219            }
220    
221            private static boolean isSubclass(Class subClass, Class superClass) {
222                    Class cls = subClass.getSuperclass();
223                    if (cls == null)
224                            return false;
225                    else if (cls.equals(superClass)) {
226                            return true;
227                    } else {
228                            return isSubclass(cls, superClass);
229                    }
230    
231            }
232    
233            /**
234             * This method finds a "text source" instance in the knowledgebase for the
235             * given TextSouce object. The "search" is very simplistic - simply return
236             * the instance in the knowledgebase that has the name textSource.getName().
237             * The namespace of your text sources - regardless of what kind they are -
238             * should not have conflicts/overlaps
239             * 
240             * @param textSource
241             * @param create
242             *            - if true: if there is not a "text source" instance for the
243             *            TextSource object then create one.
244             * @return the "text source" instance for the given TextSource object.
245             */
246    
247            public SimpleInstance getTextSourceInstance(TextSource textSource, boolean create) {
248                    SimpleInstance textSourceInstance = (SimpleInstance) kb.getInstance(textSource.getName());
249                    if (textSourceInstance == null && create) {
250                            return (SimpleInstance) textSource.createTextSourceInstance(kb);
251                    }
252                    return textSourceInstance;
253            }
254    
255            public TextSource getTextSource(Instance textSourceInstance) throws TextSourceAccessException {
256                    TextSourceCollection tsc = getCurrentTextSourceCollection();
257                    String name = textSourceInstance.getName();
258                    return tsc.get(name);
259            }
260    
261            public String getTextSourceAnnotationComment(Instance textSourceInstance) {
262    
263                    String annotationComment = (String) textSourceInstance.getOwnSlotValue(kpu.annotationCommentSlot);
264                    if (annotationComment == null) {
265                            return "";
266                    }
267                    return annotationComment;
268            }
269    
270            public String getTextSourceAnnotationComment(TextSource textSource) {
271                    Instance textSourceInstance = getTextSourceInstance(textSource, false);
272                    if (textSourceInstance != null) {
273                            return getTextSourceAnnotationComment(textSourceInstance);
274                    }
275                    return "";
276            }
277    
278            public void setTextSourceAnnotationComment(Instance textSourceInstance, String comment) {
279                    textSourceInstance.setOwnSlotValue(kpu.annotationCommentSlot, comment);
280            }
281    
282            public void setTextSourceAnnotationComment(TextSource textSource, String comment) {
283                    Instance textSourceInstance = getTextSourceInstance(textSource, true);
284                    if (textSourceInstance != null) {
285                            setTextSourceAnnotationComment(textSourceInstance, comment);
286                    }
287            }
288    
289            public void addTextSourceChangeListener(TextSourceChangeListener textSourceChangeListener) {
290                    textSourceChangeListeners.add(textSourceChangeListener);
291            }
292    
293            public void removeTextSourceChangeListener(TextSourceChangeListener textSourceChangeListener) {
294                    textSourceChangeListeners.remove(textSourceChangeListener);
295            }
296    
297            public TextSource getCurrentTextSource() {
298                    return currentTextSource;
299            }
300    
301            public void setCurrentTextSource(TextSource textSource) {
302                    if (textSource != null) {
303                            this.currentTextSource = textSource;
304                            ProjectSettings.setRecentTextSourceCollectionIndex(project, currentTextSourceCollection
305                                            .getIndex(textSource));
306                            fireTextSourceChanged(textSource);
307                    }
308            }
309    
310            // invoke SwingWorker or something....
311            void fireTextSourceChanged(TextSource textSource) {
312                    TextSourceChangeEvent event = new TextSourceChangeEvent(textSource);
313                    for (TextSourceChangeListener tscl : textSourceChangeListeners) {
314                            tscl.textSourceChanged(event);
315                    }
316            }
317    
318            /**
319             * TextSourceCollection methods
320             */
321    
322            public void addTextSourceCollectionChangeListener(
323                            TextSourceCollectionChangeListener textSourceCollectionChangeListener) {
324                    textSourceCollectionChangeListeners.add(textSourceCollectionChangeListener);
325            }
326    
327            public void removeTextSourceCollectionChangeListener(
328                            TextSourceCollectionChangeListener textSourceCollectionChangeListener) {
329                    textSourceCollectionChangeListeners.remove(textSourceCollectionChangeListener);
330            }
331    
332            public TextSourceCollection getCurrentTextSourceCollection() {
333                    return currentTextSourceCollection;
334            }
335    
336            public void setCurrentTextSourceCollection(TextSourceCollection textSourceCollection) {
337                    if (textSourceCollection != null) {
338                            this.currentTextSourceCollection = textSourceCollection;
339                            ProjectSettings.setRecentTextSourceCollection(project, textSourceCollection);
340                            fireTextSourceCollectionChange(currentTextSourceCollection);
341                            try {
342                                    setCurrentTextSource(textSourceCollection.get(0));
343                            } catch (TextSourceAccessException tsae) {
344                                    tsae.printStackTrace();
345                            }
346                    }
347            }
348    
349            // invoke SwingWorker or something....
350            void fireTextSourceCollectionChange(TextSourceCollection textSourceCollection) {
351                    TextSourceCollectionChangeEvent event = new TextSourceCollectionChangeEvent(textSourceCollection);
352                    for (TextSourceCollectionChangeListener tsccl : textSourceCollectionChangeListeners) {
353                            tsccl.textSourceCollectionChanged(event);
354                    }
355            }
356    
357    }