001/** 002 * Copyright (C) 2007 - 2016, 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 */ 019package org.dllearner.core; 020 021import com.google.common.collect.Sets; 022import org.apache.commons.collections4.BidiMap; 023import org.apache.commons.collections4.bidimap.DualHashBidiMap; 024import org.apache.log4j.Level; 025import org.dllearner.core.config.ConfigOption; 026import org.dllearner.accuracymethods.AccMethod; 027import org.dllearner.refinementoperators.RefinementOperator; 028import org.reflections.Reflections; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import java.lang.reflect.Field; 033import java.lang.reflect.InvocationTargetException; 034import java.lang.reflect.Method; 035import java.lang.reflect.Modifier; 036import java.util.*; 037import java.util.stream.Collectors; 038 039/** 040 * Component manager for the new (as of 2011) annotation based configuration 041 * system. 042 * 043 * In the future, this may replace the previous implementation of component 044 * manager. 045 * 046 * @author Jens Lehmann 047 * 048 */ 049public class AnnComponentManager { 050 private static Logger logger = LoggerFactory.getLogger(AnnComponentManager.class); 051 052 // the list of annotation based components (note that we save them as string here 053 // instead of class objects in order not to have dependencies on the implementation 054 // of components, which are not required to be in the core module - the class 055 // objects are only created on invocation of the component manager); 056 // components must be listed here if they should be supported in interfaces 057 // (CLI, GUI, Web Service) and scripts (HTML documentation generator) 058 private static List<String> componentClassNames; 059// ;= new ArrayList<String> ( Arrays.asList(new String[]{ 060// "org.dllearner.algorithms.NaiveALLearner", 061// "org.dllearner.algorithms.celoe.CELOE", 062//// "org.dllearner.algorithms.celoe.PCELOE", 063// "org.dllearner.algorithms.el.ELLearningAlgorithm", 064// "org.dllearner.algorithms.el.ELLearningAlgorithmDisjunctive", 065//// "org.dllearner.algorithms.fuzzydll.FuzzyCELOE", 066//// "org.dllearner.algorithms.BruteForceLearner", 067//// "org.dllearner.algorithms.RandomGuesser", 068// "org.dllearner.algorithms.properties.DisjointObjectPropertyAxiomLearner", 069// "org.dllearner.algorithms.properties.EquivalentObjectPropertyAxiomLearner", 070// "org.dllearner.algorithms.properties.FunctionalObjectPropertyAxiomLearner", 071// "org.dllearner.algorithms.properties.InverseFunctionalObjectPropertyAxiomLearner", 072// "org.dllearner.algorithms.properties.ObjectPropertyDomainAxiomLearner", 073// "org.dllearner.algorithms.properties.ObjectPropertyRangeAxiomLearner", 074// "org.dllearner.algorithms.properties.SubObjectPropertyOfAxiomLearner", 075// "org.dllearner.algorithms.properties.SymmetricObjectPropertyAxiomLearner", 076// "org.dllearner.algorithms.properties.TransitiveObjectPropertyAxiomLearner", 077// "org.dllearner.algorithms.properties.DisjointDataPropertyAxiomLearner", 078// "org.dllearner.algorithms.properties.EquivalentDataPropertyAxiomLearner", 079// "org.dllearner.algorithms.properties.FunctionalDataPropertyAxiomLearner", 080// "org.dllearner.algorithms.properties.DataPropertyDomainAxiomLearner", 081// "org.dllearner.algorithms.properties.DataPropertyRangeAxiomLearner", 082// "org.dllearner.algorithms.properties.SubDataPropertyOfAxiomLearner", 083// "org.dllearner.algorithms.DisjointClassesLearner", 084// "org.dllearner.algorithms.SimpleSubclassLearner", 085// "org.dllearner.algorithms.qtl.QTL2Disjunctive", 086// "org.dllearner.kb.KBFile", 087// "org.dllearner.kb.OWLFile", 088// "org.dllearner.kb.SparqlEndpointKS", 089// "org.dllearner.kb.LocalModelBasedSparqlEndpointKS", 090// "org.dllearner.kb.sparql.SparqlKnowledgeSource", 091// "org.dllearner.kb.sparql.simple.SparqlSimpleExtractor", 092// "org.dllearner.learningproblems.PosNegLPStandard", 093//// "org.dllearner.learningproblems.PosNegLPStrict", 094//// "org.dllearner.learningproblems.FuzzyPosNegLPStandard", 095// "org.dllearner.learningproblems.PosOnlyLP", 096// "org.dllearner.learningproblems.ClassLearningProblem", 097// "org.dllearner.learningproblems.PropertyAxiomLearningProblem", 098// "org.dllearner.reasoning.ClosedWorldReasoner", 099// "org.dllearner.reasoning.OWLAPIReasoner", 100// "org.dllearner.reasoning.SPARQLReasoner", 101//// "org.dllearner.reasoning.fuzzydll.FuzzyOWLAPIReasoner", 102// "org.dllearner.algorithms.ocel.OCEL", 103// "org.dllearner.algorithms.ocel.MultiHeuristic", 104// "org.dllearner.algorithms.celoe.OEHeuristicRuntime", 105// "org.dllearner.algorithms.isle.NLPHeuristic", 106// "org.dllearner.algorithms.qtl.heuristics.QueryTreeHeuristicComplex", 107// "org.dllearner.algorithms.qtl.heuristics.QueryTreeHeuristicSimple", 108// "org.dllearner.algorithms.el.DisjunctiveHeuristic", 109// "org.dllearner.algorithms.isle.metrics.RelevanceWeightedStableHeuristic", 110// "org.dllearner.algorithms.el.StableHeuristic", 111// "org.dllearner.refinementoperators.RhoDRDown", 112//// "org.dllearner.refinementoperators.SynchronizedRhoDRDown", 113// // just for testing 114// // "org.dllearner.refinementoperators.ExampleOperator", 115// "org.dllearner.utilities.semkernel.SemKernelWorkflow", 116// "org.dllearner.utilities.semkernel.MPSemKernelWorkflow", 117// } )); 118 private static Collection<Class<? extends Component>> components; 119 private static BidiMap<Class<? extends Component>, String> componentNames; 120 private static BidiMap<Class<? extends Component>, String> componentNamesShort; 121 122 private static AnnComponentManager cm = null; 123 private static Reflections reflectionScanner = null; 124 125 private AnnComponentManager() { 126 if (componentClassNames == null) { 127 componentClassNames = new ArrayList<>(); 128 if (reflectionScanner == null) { 129 org.apache.log4j.Logger.getLogger(Reflections.class).setLevel(Level.OFF); 130 reflectionScanner = new Reflections("org.dllearner"); 131 } 132 Set<Class<? extends Component>> componentClasses = reflectionScanner.getSubTypesOf(Component.class); 133 Set<Class<?>> componentAnnClasses = reflectionScanner.getTypesAnnotatedWith(ComponentAnn.class, true); 134 for (Class<?> clazz 135 : Sets.intersection( 136 componentClasses, 137 componentAnnClasses 138 ) 139 ) { 140 if (!Modifier.isAbstract( clazz.getModifiers() )) 141 componentClassNames.add(clazz.getCanonicalName()); 142 } 143 for (Class<?> clazz 144 : Sets.difference(componentClasses, componentAnnClasses) 145 ) { 146 if (!Modifier.isAbstract( clazz.getModifiers() )) 147 logger.debug("Warning: " + clazz.getCanonicalName() + " implements Component but is not annotated, ignored"); 148 } 149 } 150 // conversion of class strings to objects 151 components = new TreeSet<>((Comparator<Class<? extends Component>>) (o1, o2) -> { 152 return o1.getName().compareTo(o2.getName()); 153 }); 154 componentNames = new DualHashBidiMap<>(); 155 componentNamesShort = new DualHashBidiMap<>(); 156 for (String componentClassName : componentClassNames) { 157 try { 158 Class<? extends Component> component = Class.forName(componentClassName).asSubclass(Component.class); 159 components.add(component); 160 componentNames.put(component, getName(component)); 161 componentNamesShort.put(component, getShortName(component)); 162 } catch (ClassNotFoundException e) { 163 e.printStackTrace(); 164 } 165 } 166 } 167 168 /** 169 * Explicitly sets the list of components to use. This will (re-)initialise the 170 * component manager the next time the singleton instance is retrieved. 171 */ 172 public static void setComponentClassNames(List<String> componentClassNames) { 173 AnnComponentManager.componentClassNames = componentClassNames; 174 cm = null; 175 } 176 177 public static void setReflectionScanner(Reflections ref) { 178 AnnComponentManager.reflectionScanner = ref; 179 setComponentClassNames(null); 180 } 181 182 /** 183 * Gets the singleton instance of <code>ComponentManager</code>. 184 * @return The singleton <code>ComponentManager</code> instance. 185 */ 186 public static AnnComponentManager getInstance() { 187 if(cm == null) { 188 cm = new AnnComponentManager(); 189 } 190 return cm; 191 } 192 193 /** 194 * Returns a list of all available components in this instance 195 * of <code>ComponentManager</code>. 196 * @return the components A list of component classes available in this 197 * instance of <code>ComponentManager</code>. 198 */ 199 public Collection<Class<? extends Component>> getComponents() { 200 return components; 201 } 202 203 /** 204 * Returns a list of all available components in this instance 205 * of <code>ComponentManager</code>. 206 * @return the components A list of component classes available in this 207 * instance of <code>ComponentManager</code>. 208 */ 209 public SortedSet<String> getComponentStrings() { 210 SortedSet<String> result = getComponents().stream() 211 .map(AnnComponentManager::getShortName) 212 .collect(Collectors.toCollection(TreeSet::new)); 213 return result; 214 } 215 216 /** 217 * Get registered components which are of the specified type. 218 * 219 * @param type The super type. 220 * @return All sub classes of type. 221 */ 222 public SortedSet<String> getComponentStringsOfType(Class type) { 223 224 SortedSet<String> result = getComponentsOfType(type).stream() 225 .map(AnnComponentManager::getShortName) 226 .collect(Collectors.toCollection(TreeSet::new)); 227 228 return result; 229 } 230 231 /** 232 * Get the corresponding component class given the long or short name. 233 * 234 * @param componentName The long or short name of the component. 235 * @return The class of the component. 236 */ 237 public Class<? extends Component> getComponentClass(String componentName) { 238 // lookup by long name 239 Class<? extends Component> componentClass = componentNames.getKey(componentName); 240 241 // lookup by short name 242 if(componentClass == null) { 243 componentClass = componentNamesShort.getKey(componentName); 244 } 245 246 return componentClass; 247 } 248 249 /** 250 * Get registered components which are of the specified type. 251 * 252 * @param type The super type. 253 * @return All sub classes of type. 254 */ 255 public Collection<Class<? extends Component>> getComponentsOfType(Class type) { 256 257 Collection<Class<? extends Component>> result = components.stream() 258 .filter(component -> type.isAssignableFrom(component)) 259 .collect(Collectors.toCollection(ArrayList::new)); 260 261 return result; 262 } 263 264 /** 265 * Convenience method, which returns a list of components along with 266 * their name. 267 * 268 * @return A map where the key is the class of the component and the 269 * value is its name. 270 */ 271 public BidiMap<Class<? extends Component>, String> getComponentsNamed() { 272 return componentNames; 273 } 274 275 /** 276 * Convenience method, which returns a list of components along with 277 * their name. 278 * 279 * @return A map where the key is the class of the component and the 280 * value is its name. 281 */ 282 public BidiMap<Class<? extends Component>, String> getComponentsNamedShort() { 283 return componentNamesShort; 284 } 285 286 /** 287 * Applies a config entry to a component. If the entry is not valid, the method 288 * prints an exception and returns false. 289 * @param <T> Type of the config option. 290 * @param component A component object. 291 * @param optionName the option name 292 * @param value the value to set 293 * @return True if the config entry could be applied succesfully, otherwise false. 294 */ 295 @Deprecated 296 public static <T> boolean applyConfigEntry(AbstractComponent component, String optionName, T value) { 297 List<AbstractComponent> childComponents = new LinkedList<>(); 298 for (Method m : component.getClass().getMethods()) { 299 if (m.getName().equals("set" + optionName.substring(0, 1).toUpperCase() + optionName.substring(1))) { 300 try { 301 m.invoke(component, value); 302 } catch (IllegalAccessException | IllegalArgumentException 303 | InvocationTargetException e) { 304 logger.debug("Error setting " + optionName + " to " + value + " on " + component + ": ", e); 305 return false; 306 } 307 return true; 308 } else if (m.getName().startsWith("get") 309 && AbstractComponent.class.isAssignableFrom(m.getReturnType())) { 310 Object cc; 311 try { 312 cc = m.invoke(component); 313 childComponents.add((AbstractComponent) cc); 314 } catch (IllegalAccessException | IllegalArgumentException 315 | InvocationTargetException e) { 316 logger.trace("Error querying " + m.getName() + " for subcomponent in " + component, e); 317 } 318 } 319 } 320 for (AbstractComponent cc : childComponents) { 321 if (cc != null) { 322 boolean try_inv = applyConfigEntry(cc, optionName, value); 323 if (try_inv) { 324 return true; 325 } 326 } 327 } 328 return false; 329 } 330 331 public final static Class[] coreComponentClasses = { 332 KnowledgeSource.class, 333 LearningAlgorithm.class, 334 AxiomLearningAlgorithm.class, 335 ClassExpressionLearningAlgorithm.class, 336 LearningProblem.class, 337 ReasonerComponent.class, 338 RefinementOperator.class, 339 Heuristic.class, 340 AccMethod.class 341 }; 342 343 /** 344 * Convenience method to retrieve core types of a component. The main use case for this 345 * is for automatic documentation generation. 346 * 347 * @param component A component. 348 * @return The list of core interfaces the component implemnets. 349 */ 350 public static List<Class<? extends Component>> getCoreComponentTypes(Class<? extends Component> component) { 351 List<Class<? extends Component>> types = new LinkedList<>(); 352 for(Class c : coreComponentClasses) { 353 if(c.isAssignableFrom(component)) { 354 types.add(c); 355 } 356 } 357 return types; 358 } 359 360 /** 361 * Returns the name of a DL-Learner component. 362 * @param component the component 363 * @return Name of the component. 364 */ 365 public static String getName(Class<? extends Component> component){ 366 ComponentAnn ann = component.getAnnotation(ComponentAnn.class); 367 if(ann == null) { 368 throw new Error("Component " + component + " does not use component annotation."); 369 } 370 return ann.name(); 371 } 372 373 /** 374 * Returns the name of a DL-Learner component. 375 * @param component the component 376 * @return Name of the component. 377 */ 378 public static String getName(Component component){ 379 return getName(component.getClass()); 380 } 381 382 /** 383 * Returns the name of a DL-Learner component. 384 * @param component the component 385 * @return Name of the component. 386 */ 387 public static String getShortName(Class<? extends Component> component){ 388 ComponentAnn ann = component.getAnnotation(ComponentAnn.class); 389 if(ann == null) { 390 throw new Error("Component " + component + " does not use component annotation."); 391 } 392 return ann.shortName(); 393 } 394 395 /** 396 * Returns the short name of a DL-Learner component. 397 * @param component the component 398 * @return Short name of the component. 399 */ 400 public static String getShortName(Component component){ 401 return getShortName(component.getClass()); 402 } 403 404 /** 405 * Returns the name of a DL-Learner component. 406 * @param component the component 407 * @return Name of the component. 408 */ 409 public static String getDescription(Class<? extends Component> component){ 410 ComponentAnn ann = component.getAnnotation(ComponentAnn.class); 411 return ann.description(); 412 } 413 414 /** 415 * Returns the description of a DL-Learner component. 416 * @param component the component 417 * @return OWLClassExpression of the component. 418 */ 419 public static String getDescription(Component component){ 420 return getDescription(component.getClass()); 421 } 422 423 /** 424 * Returns the config options of a DL-Learner component. 425 * @param component the component 426 * @return OWLClassExpression of the component. 427 */ 428 public static Set<Field> getConfigOptions(Class<? extends Component> component){ 429 Set<Field> set = new HashSet<>(); 430 Class<?> c = component; 431 while (c != null) { 432 for (Field field : c.getDeclaredFields()) { 433 if (field.isAnnotationPresent(ConfigOption.class)) { 434 set.add(field); 435 } 436 } 437 c = c.getSuperclass(); 438 } 439 return set; 440 } 441 442 /** 443 * Returns the version of a DL-Learner component. 444 * @param component the component 445 * @return Version of the component. 446 */ 447 public static double getVersion(Class<? extends Component> component){ 448 ComponentAnn ann = component.getAnnotation(ComponentAnn.class); 449 return ann.version(); 450 } 451 452 /** 453 * Returns the version of a DL-Learner component. 454 * @param component the component 455 * @return Version of the component. 456 */ 457 public static double getVersion(Component component){ 458 return getVersion(component.getClass()); 459 } 460 461 public static boolean addComponentClassName(String e) { 462 return componentClassNames.add(e); 463 } 464 465 /** 466 * Returns the name of a config option 467 * @param f the Reflection field of the option 468 * @return name of the option 469 */ 470 public static String getName(Field f) { 471 f.getAnnotation(ConfigOption.class); 472 return f.getName(); 473 } 474}