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 }