001    /**
002     * Copyright (C) 2007-2008, Jens Lehmann
003     *
004     * This file is part of DL-Learner.
005     * 
006     * DL-Learner is free software; you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation; either version 3 of the License, or
009     * (at your option) any later version.
010     *
011     * DL-Learner is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014     * GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License
017     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018     *
019     */
020    package org.dllearner.gui;
021    
022    import java.io.File;
023    import java.io.FileNotFoundException;
024    import java.util.HashSet;
025    import java.util.LinkedList;
026    import java.util.List;
027    import java.util.Set;
028    
029    import org.apache.log4j.Logger;
030    import org.dllearner.cli.Start;
031    import org.dllearner.core.Component;
032    import org.dllearner.core.ComponentInitException;
033    import org.dllearner.core.ComponentManager;
034    import org.dllearner.core.KnowledgeSource;
035    import org.dllearner.core.LearningAlgorithm;
036    import org.dllearner.core.LearningProblem;
037    import org.dllearner.core.LearningProblemUnsupportedException;
038    import org.dllearner.core.ReasonerComponent;
039    import org.dllearner.core.options.ConfigEntry;
040    import org.dllearner.core.options.ConfigOption;
041    import org.dllearner.kb.KBFile;
042    import org.dllearner.kb.OWLFile;
043    import org.dllearner.kb.sparql.SparqlKnowledgeSource;
044    import org.dllearner.learningproblems.PosNegLP;
045    import org.dllearner.parser.ParseException;
046    
047    /**
048     * Config save all together used variables: ComponentManager, KnowledgeSource,
049     * Reasoner, ReasonerComponent, LearningProblem, LearningAlgorithm; also inits of
050     * these components.
051     * 
052     * @author Jens Lehmann
053     * @author Tilo Hielscher
054     */
055    public class Config {
056    
057            private ComponentManager cm = ComponentManager.getInstance();
058            private static Logger logger = Logger.getLogger(Config.class);
059    
060            // the components currently active
061            private KnowledgeSource source;
062            private ReasonerComponent reasoner;
063    //      private ReasonerComponent rs;
064            private LearningProblem lp;
065            private LearningAlgorithm la;
066    
067            // stores which components need to be initialised (either
068            // because they have not been initialiased or previous components
069            // have changed configuration options, which require initialisation)
070            private boolean[] needsInit = new boolean[4];
071    
072            // specifies whether the panel is enabled, i.e. the user
073            // can select it (all mandatory variables in selected components have been
074            // choosen)
075            private boolean[] isEnabled = new boolean[4];
076    
077            private StartGUI gui;
078    
079            /**
080             * This constructor can be used systemwide to save configurations in conf files.
081             * Of course it should not really belong here, but either in core or utilities.
082             * Consider refactoring using a subclass of Config for the GUI.
083             * Nevertheless it still works.
084             * 
085             *  
086             * @param cm
087             * @param source
088             * @param reasoner
089             * @param rs
090             * @param lp
091             * @param la
092             */
093            public Config(ComponentManager cm, KnowledgeSource source, ReasonerComponent reasoner, LearningProblem lp, LearningAlgorithm la) {
094                    super();
095                    this.cm = cm;
096                    this.source = source;
097                    this.reasoner = reasoner;
098                    this.lp = lp;
099                    this.la = la;
100            }
101    
102            /**
103             * Create central configuration object.
104             * 
105             * @param gui
106             *            The main gui object. It is passed as parameter, such that the
107             *            configuration handler can update the GUI appropriately.
108             */
109            public Config(StartGUI gui) {
110                    this.gui = gui;
111                    // none of the components is initialised
112                    for (int i = 0; i < 4; i++) {
113                            needsInit[i] = true;
114                            // TODO there might be knowledge source without mandatory options
115                            isEnabled[i] = false;
116                    }
117            }
118    
119            /**
120             * Loads a file using the commandline interface and asks it for loaded
121             * components and config options. This way, we ensure that the loading
122             * process is always compatible with the command line and we do not need to
123             * implement the process again for the GUI. Afte loading, the GUI is updates
124             * appropriately.
125             * 
126             * @param file
127             *            The file to load.
128             */
129            public void loadFile(File file) {
130                    // use CLI to load file
131                    try {
132                            // set all loaded components as active components
133                            Start start = null;
134                            try {
135                                    start = new Start(file);
136                            } catch (FileNotFoundException e) {
137                                    // TODO Auto-generated catch block
138                                    e.printStackTrace();
139                            } catch (ParseException e) {
140                                    // TODO Auto-generated catch block
141                                    e.printStackTrace();
142                            }
143                            Set<KnowledgeSource> sources = start.getSources();
144                            if (sources.size() != 1) {
145                                    gui.getStatusPanel().setExceptionMessage(
146                                                    "Warning: GUI supports only one knowledge source.");
147                            }
148                            source = sources.iterator().next();
149                            reasoner = start.getReasonerComponent();
150    //                      rs = start.getReasonerComponent();
151                            lp = start.getLearningProblem();
152                            la = start.getLearningAlgorithm();
153    
154                            // all components initialised and enabled
155                            for (int i = 0; i < 4; i++) {
156                                    needsInit[i] = false;
157                                    isEnabled[i] = true;
158                            }
159    
160                            // update tabs in GUI such that algorithm can be run
161                            gui.updateTabs();
162                            gui.updateStatusPanel();
163                            for (int i = 0; i < 4; i++) {
164                                    gui.panels[i].update();
165                            }
166    
167                    } catch (ComponentInitException e) {
168                            gui.getStatusPanel().setExceptionMessage(e.getMessage());
169                            e.printStackTrace();
170                    }
171            }
172    
173            /**
174             * Get ComponentManager.
175             * 
176             * @return ComponentManager
177             */
178            public ComponentManager getComponentManager() {
179                    return cm;
180            }
181    
182            /**
183             * Get KnowledgeSource.
184             * 
185             * @return KnowledgeSource
186             */
187            public KnowledgeSource getKnowledgeSource() {
188                    return source;
189            }
190    
191            /**
192             * Creates a knowledge source and makes it the active source.
193             * 
194             * @param clazz
195             *            knowledge source class
196             * @return knowledge source instance
197             */
198            public KnowledgeSource newKnowledgeSource(Class<? extends KnowledgeSource> clazz) {
199                    source = cm.knowledgeSource(clazz);
200                    // logger.debug("new knowledge source " + clazz + " created");
201                    return source;
202            }
203    
204            /**
205             * Changes active knowledge source. This method does not not only create a
206             * knowledge source, but also updates the active reasoner to use the new
207             * knowledge source.
208             * 
209             * @param clazz
210             *            knowledge source class
211             * @return knowledge source instance
212             */
213            public KnowledgeSource changeKnowledgeSource(Class<? extends KnowledgeSource> clazz) {
214                    source = cm.knowledgeSource(clazz);
215                    Set<KnowledgeSource> sources = new HashSet<KnowledgeSource>();
216                    sources.add(source);
217                    reasoner.changeSources(sources);
218                    // logger.debug("knowledge source " + clazz + " changed");
219                    // create a new reasoner object using the current class and the selected
220                    // source
221                    // reasoner = cm.reasoner(reasoner.getClass(), source);
222                    // rs = cm.reasoningService(reasoner);
223                    // lp = cm.learningProblem(lp.getClass(), rs);
224                    // try {
225                    // la = cm.learningAlgorithm(la.getClass(), lp, rs);
226                    // } catch (LearningProblemUnsupportedException e) {
227                    // // TODO Auto-generated catch block
228                    // e.printStackTrace();
229                    // }
230                    needsInit[0] = true;
231                    needsInit[1] = true;
232                    needsInit[2] = true;
233                    needsInit[3] = true;            
234                    return source;
235            }
236    
237            /**
238             * Set Reasoner.
239             * 
240             * @param reasoner
241             */
242            // public void setReasoner(ReasonerComponent reasoner) {
243            // this.reasoner = reasoner;
244            // }
245            /**
246             * Get Reasoner.
247             * 
248             * @return reasoner
249             */
250            public ReasonerComponent getReasoner() {
251                    return this.reasoner;
252            }
253    
254            /**
255             * Creates reasoner + reasoning service and makes it active.
256             * @param clazz The class of the reasoner.
257             * @return A reasoner instance.
258             */
259            public ReasonerComponent newReasoner(Class<? extends ReasonerComponent> clazz) {
260                    reasoner = cm.reasoner(clazz, source);
261    //              rs = cm.reasoningService(reasoner);
262                    return reasoner;
263            }
264    
265            /**
266             * Change the reasoner and notify the depending components
267             * (learning problem, learning algorithm).
268             * @param clazz The reasoner class.
269             * @return A reasoner instance.
270             */
271            public ReasonerComponent changeReasoner(Class<? extends ReasonerComponent> clazz) {
272                    reasoner = cm.reasoner(clazz, source);
273    //              rs = cm.reasoningService(reasoner);
274                    lp.changeReasonerComponent(reasoner);
275                    la.changeReasonerComponent(reasoner);
276                    needsInit[1] = true;
277                    needsInit[2] = true;
278                    needsInit[3] = true;
279                    return reasoner;
280            }
281    
282            /**
283             * Get LearningProblem.
284             * 
285             * @return learningProblem
286             */
287            public LearningProblem getLearningProblem() {
288                    return this.lp;
289            }
290    
291            /**
292             * Creates learning problem and makes it active.
293             * @param clazz The class of the learning problem.
294             * @return A learning problem instance.
295             */     
296            public LearningProblem newLearningProblem(Class<? extends LearningProblem> clazz) {
297                    lp = cm.learningProblem(clazz, reasoner);
298                    return lp;
299            }
300    
301            /**
302             * Change the learning problem and notify the depending components
303             * (learning algorithm).
304             * @param clazz The learning problem class.
305             * @return A learning problem instance.
306             */     
307            public LearningProblem changeLearningProblem(Class<? extends LearningProblem> clazz) {
308                    lp = cm.learningProblem(clazz, reasoner);
309                    la.changeLearningProblem(lp);
310                    needsInit[2] = true;
311                    needsInit[3] = true;            
312                    return lp;
313            }
314    
315            /**
316             * Get LearningAlgorithm.
317             * 
318             * @return LearningAlgorithm
319             */
320            public LearningAlgorithm getLearningAlgorithm() {
321                    return this.la;
322            }
323    
324            /**
325             * Creates learning algorithm and makes it active.
326             * @param clazz The class of the learning algorithm.
327             * @throws LearningProblemUnsupportedException If the learning algorithm
328             * does not support the learning problem (TODO should be handled intelligently
329             * by GUI).
330             * @return A learning algorithm instance.
331             */
332            public LearningAlgorithm newLearningAlgorithm(Class<? extends LearningAlgorithm> clazz)
333                            throws LearningProblemUnsupportedException {
334                    la = cm.learningAlgorithm(clazz, lp, reasoner);
335                    return la;
336            }
337    
338            /**
339             * Change the learning algorithm.
340             * @param clazz The learning algorithm class.
341             * @throws LearningProblemUnsupportedException If the learning algorithm
342             * does not support the learning problem (TODO should be handled intelligently
343             * by GUI).
344             * @return A learning algorithm instance.
345             */
346            public LearningAlgorithm changeLearningAlgorithm(Class<? extends LearningAlgorithm> clazz)
347                            throws LearningProblemUnsupportedException {
348                    la = cm.learningAlgorithm(clazz, lp, reasoner);
349                    needsInit[3] = true;            
350                    return la;
351            }
352    
353            /**
354             * Returns whether the corresponding tab needs to be initialised.
355             * @param tabIndex Index of the tab (0 to 3).
356             * @return True if the tab needs to be initialised for the learning
357             * algorithm to run. False, otherwise.
358             */
359            public boolean tabNeedsInit(int tabIndex) {
360                    return needsInit[tabIndex];
361            }
362    
363            /**
364             * KnowledgeSource.init has run?
365             * 
366             * @return true, if init was made, false if not
367             */
368            public boolean needsInitKnowledgeSource() {
369                    return needsInit[0];
370            }
371    
372            /**
373             * Reasoner.init has run?
374             * 
375             * @return true, if init was made, false if not
376             */
377            public boolean needsInitReasoner() {
378                    return needsInit[1];
379            }
380    
381            /**
382             * LearningProblem.init has run?
383             * 
384             * @return true, if init was made, false if not
385             */
386            public boolean needsInitLearningProblem() {
387                    return needsInit[2];
388            }
389    
390            /**
391             * LearningAlgorithm.init() has run?
392             * 
393             * @return true, if init was made, false if not
394             */
395            public boolean needsInitLearningAlgorithm() {
396                    return needsInit[3];
397            }
398    
399            /**
400             * Returns whether the corresponding tab is enabled (e.g.
401             * we can start to configure it, because all mandatory options
402             * of underlying components have been specified).
403             * @param tabIndex Index of the tab (0 to 3).
404             * @return True if the tab is enabled and false otherwise.
405             */     
406            public boolean isEnabled(int tabIndex) {
407                    return isEnabled[tabIndex];
408            }
409    
410            /**
411             * Initialises the specified components and records which ones where initialised.
412             * The init process is done in a different thread, so this method
413             * returns immediately.
414             * @param tabIndex A list of components (0 = knowledge source, 1 = reasoner, ...).
415             */ 
416            public void init(List<Integer> tabIndex) {
417                    List<Component> components = new LinkedList<Component>();
418                    for (int i : tabIndex) {
419                            switch (i) {
420                            case 0:
421                                    components.add(source);
422                                    needsInit[i] = false;
423                                    break;
424                            case 1:
425                                    components.add(reasoner);
426                                    needsInit[i] = false;
427                                    break;
428                            case 2:
429                                    components.add(lp);
430                                    needsInit[i] = false;
431                                    break;
432                            case 3:
433                                    components.add(la);
434                                    needsInit[i] = false;
435                                    break;
436                            default:
437                                    throw new Error("Illegal tab number " + i + " (needs to be 0-3).");
438                            }
439                    }
440                    InitWorker worker = new InitWorker(components, gui);
441                    worker.execute();
442    
443                    if (tabIndex.size() == 1) {
444                            logger.info("Component " + tabIndex.get(0) + " initialised.");
445                    } else if (tabIndex.size() > 1) {
446                            logger.info("Components " + tabIndex + " initialised.");
447                    }
448    
449            }
450    
451            /**
452             * Applies a configuration option and cares for all consequences
453             * the GUI needs to take.
454             * @see ComponentManager#applyConfigEntry(Component, ConfigEntry)
455             * @param <T> The type of config entry.
456             * @param component The component to apply the entry to.
457             * @param entry The config entry to apply.
458             */
459            public <T> void applyConfigEntry(Component component, ConfigEntry<T> entry) {
460                    System.out.println("Applying " + entry + " to " + component.getClass().getName() + ".");
461    
462                    cm.applyConfigEntry(component, entry);
463                    // enable tabs if setting the value completed mandatory settings
464                    enableComponentsIfPossible();
465                    // invalidate components
466                    if (component instanceof KnowledgeSource) {
467                            needsInit[0] = true;
468                            needsInit[1] = true;
469                            needsInit[2] = true;
470                            needsInit[3] = true;
471                            if (isEnabled[0]) {
472                                    gui
473                                                    .setStatusMessage("All mandatory options filled in. You can continue to the reasoner tab.");
474                            }
475                    } else if (component instanceof ReasonerComponent) {
476                            needsInit[1] = true;
477                            needsInit[2] = true;
478                            needsInit[3] = true;
479                            if (isEnabled[1]) {
480                                    gui
481                                                    .setStatusMessage("All mandatory options filled in. You can continue to the learning problem tab.");
482                            }
483                    } else if (component instanceof LearningProblem) {
484                            needsInit[2] = true;
485                            needsInit[3] = true;
486                            if (isEnabled[2]) {
487                                    gui
488                                                    .setStatusMessage("All mandatory options filled in. You can continue to the learning algorithm tab.");
489                            }
490                    } else if (component instanceof LearningAlgorithm) {
491                            needsInit[3] = true;
492                            if (isEnabled[3]) {
493                                    gui
494                                                    .setStatusMessage("All mandatory options filled in. You can now run the algorithm.");
495                            }
496                    }
497    
498                    gui.updateTabs();
499            }
500    
501            /**
502             * Tests whether components can be enabled. A component is enabled,
503             * if it fulfills the following conditions:
504             * - the "previous" component has all mandatory options specified
505             * - all components before the previous one are enabled
506             * Otherwise, the component is disabled.
507             * Note it can also happend that enabled components become
508             * disabled if mandatory fields are cleared.
509             */
510            public void enableComponentsIfPossible() {
511                    // 0: reasoner
512                    // 1: problem
513                    // 2: algorithm
514                    // 3: run
515    
516                    isEnabled[0] = mandatoryOptionsSpecified(source);
517                    isEnabled[1] = isEnabled[0] && mandatoryOptionsSpecified(reasoner);
518                    isEnabled[2] = isEnabled[0] && isEnabled[1] && mandatoryOptionsSpecified(lp);
519                    isEnabled[3] = isEnabled[0] && isEnabled[1] && isEnabled[2]
520                                    && mandatoryOptionsSpecified(la);
521            }
522    
523            /**
524             * Checks whether all mandatory options have been set for 
525             * a component. 
526             * TODO Use specification of mandatory variables.
527             * @param component The component to test.
528             * @return True if all mandatory options are set and false otherwise.
529             */
530            @SuppressWarnings("unchecked")
531            public boolean mandatoryOptionsSpecified(Component component) {
532                    // System.out.println("check mandatory options for " +
533                    // component.getClass().getName());
534                    if (component instanceof OWLFile) {
535                            if (cm.getConfigOptionValue(source, "url") == null) {
536                                    return false;
537                            }
538                    } else if (component instanceof KBFile) {
539                            if (cm.getConfigOptionValue(source, "url") == null) {
540                                    return false;
541                            }
542                    } else if (component instanceof PosNegLP) {
543                            if (cm.getConfigOptionValue(component, "positiveExamples") == null
544                                            || cm.getConfigOptionValue(component, "negativeExamples") == null
545                                            || ((Set<String>) cm.getConfigOptionValue(component, "positiveExamples"))
546                                                            .size() == 0
547                                            || ((Set<String>) cm.getConfigOptionValue(component, "negativeExamples"))
548                                                            .size() == 0) {
549                                    return false;
550                            }
551                    } else if (component instanceof SparqlKnowledgeSource) {
552                            if (cm.getConfigOptionValue(component, "instances") == null
553                                            || ((Set<String>) cm.getConfigOptionValue(component, "instances")).size() == 0) {
554                                    return false;
555                            }
556                    }
557    
558                    return true;
559            }
560    
561            /**
562             * Delegate method for getting config option values.
563             * @see ComponentManager#getConfigOptionValue(Component, ConfigOption)
564             * @param <T> Type of option.
565             * @param component Component, which has the option.
566             * @param option The option for which we want to know the value.
567             * @return The value of the specified option.
568             */
569            public <T> T getConfigOptionValue(Component component, ConfigOption<T> option) {
570                    return cm.getConfigOptionValue(component, option);
571            }
572    
573    }