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.scripts;
021    
022    import java.io.File;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Collection;
026    import java.util.LinkedHashMap;
027    import java.util.LinkedList;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    import java.util.SortedSet;
032    import java.util.TreeSet;
033    
034    import org.dllearner.core.Component;
035    import org.dllearner.core.ComponentManager;
036    import org.dllearner.core.KnowledgeSource;
037    import org.dllearner.core.LearningAlgorithm;
038    import org.dllearner.core.LearningProblem;
039    import org.dllearner.core.LearningProblemUnsupportedException;
040    import org.dllearner.core.ReasonerComponent;
041    import org.dllearner.core.configurators.CELOEConfigurator;
042    import org.dllearner.core.configurators.ROLearnerConfigurator;
043    import org.dllearner.core.configurators.RefinementOperatorConfigurator;
044    import org.dllearner.core.options.ConfigOption;
045    import org.dllearner.utilities.Files;
046    
047    /**
048     * Collects information about all used configuration options and writes them
049     * into a file. This way the documentation is always in sync with the source
050     * code.
051     * 
052     * @author Jens Lehmann
053     * @author Sebastian Hellmann
054     * 
055     */
056    public final class ConfigJavaGenerator {
057            
058            private static final SortedSet<String> DONOTDELETE = 
059                    new TreeSet<String>(Arrays.asList(new String[]{
060                                    ".svn",
061                                    "RefinementOperatorConfigurator.java",
062                                    }));
063            
064            // currently it targets the configurators for 
065            private static final SortedSet<String> EXTENDSREFINEMENTOPERATOR = 
066                    new TreeSet<String>(Arrays.asList(new String[]{
067                                    ROLearnerConfigurator.class.getSimpleName(),
068                                    CELOEConfigurator.class.getSimpleName(),
069                                    }));
070            
071            @SuppressWarnings("unchecked")
072            private static final Class EXTENDSREFINEMENTOPERATORCLASS = RefinementOperatorConfigurator.class;
073    
074            private static final boolean INCLUDE_UNUSED = false;
075    
076            private static final String UNUSED = "@SuppressWarnings(\"unused\")\n";
077            private static final String OVERRIDE = "@SuppressWarnings(\"all\")\n";
078    
079            private static final String TARGET_DIR = "src/dl-learner/org/dllearner/core/configurators";
080    
081            private static final String TARGET_PACKAGE = "org.dllearner.core.configurators";
082    
083            private static final String HEADER_FILE = "doc/header.txt";
084    
085            private static final String HEADER = getHeader();
086    
087            private static final String REINITVAR = "reinitNecessary";
088    
089            private static final String REINITGETTER = "/**\n* true, if this component needs reinitializsation.\n"
090                            + "* @return boolean\n**/\n"
091                            + "public boolean is"
092                            + capitalize(REINITVAR)
093                            + "(){\nreturn "
094                            + REINITVAR + ";\n}\n";
095    
096            // private static final String TEMPLATE_DIR = "doc/template/";
097    
098            private static final String CONFIGURATOR = "Configurator";
099    
100            private static final String COMPONENT_FACTORY = "ComponentFactory";
101            private static final String CLASS_COMMENT = "* automatically generated, do not edit manually.\n"
102                    + "* run " + ConfigJavaGenerator.class.getCanonicalName()+ " to update\n";
103    
104            private static final SortedSet<String> COMPONENT_FACTORY_IMPORTS = new TreeSet<String>();
105    
106            private static final List<String> COMPONENT_FACTORY_METHODS = new ArrayList<String>();
107    
108            private Class<? extends Component> component;
109    
110            private String className;
111    
112            private String componentType;
113            
114            private String extendS = "";
115            
116            //private String implementS = "";
117    
118            private List<String> body = new ArrayList<String>();
119    
120            private List<String> vars = new ArrayList<String>();
121    
122            private List<String> setters = new ArrayList<String>();
123    
124            private List<String> getters = new ArrayList<String>();
125    
126            private SortedSet<String> imports = new TreeSet<String>();
127    
128            private SortedSet<String> getinstanceExceptions = new TreeSet<String>();
129    
130            private Map<String, String> additionalMandatoryVars = new LinkedHashMap<String, String>();
131    
132            private Map<String, String> mandatoryVars = new LinkedHashMap<String, String>();
133    
134            // private Map<String,String> varMap = new LinkedHashMap<String, String>();
135    
136            private List<ConfigOption<?>> mandatoryOptions = new LinkedList<ConfigOption<?>>();
137    
138            /**
139             * @param args
140             *            none
141             */
142            public static void main(String[] args) {
143    
144                    Files.backupDirectory(TARGET_DIR);
145                    System.out.println("previous classes were backupped to tmp/+System.currentTimeMillis()");
146                    String[] files = Files.listDir(TARGET_DIR); 
147                    
148                    for (String file : files){
149                            //System.out.println(DONOTDELETE);
150                            
151                            if(DONOTDELETE.contains(file)){
152                                    continue;
153                            }
154                            //System.out.println(file);
155                            String todelete = TARGET_DIR + File.separator + file;
156                            Files.deleteFile(todelete);
157                            
158                    }
159                    //System.exit(0);
160                    //Files.deleteDir(TARGET_DIR);
161    
162                    ComponentManager cm = ComponentManager.getInstance();
163                    COMPONENT_FACTORY_IMPORTS.add(KnowledgeSource.class.getCanonicalName());
164                    COMPONENT_FACTORY_IMPORTS
165                                    .add(ReasonerComponent.class.getCanonicalName());
166                    COMPONENT_FACTORY_IMPORTS.add(LearningProblem.class.getCanonicalName());
167                    COMPONENT_FACTORY_IMPORTS.add(LearningProblemUnsupportedException.class
168                                    .getCanonicalName());
169                    COMPONENT_FACTORY_METHODS.add("private "+COMPONENT_FACTORY+"(){}\n");
170    
171                    for (Class<? extends KnowledgeSource> component : cm
172                                    .getKnowledgeSources()) {
173                            String componentType = "knowledgeSource";
174    
175                            COMPONENT_FACTORY_IMPORTS.add(component.getCanonicalName());
176                            // configuratorImports.add(TARGET_PACKAGE+"."+component.getSimpleName()+CONFIGURATOR);
177                            ConfigJavaGenerator c = new ConfigJavaGenerator(component,
178                                            componentType);
179                            c.makeConfigurator();
180    
181                    }
182                    for (Class<? extends ReasonerComponent> component : cm
183                                    .getReasonerComponents()) {
184    
185                            COMPONENT_FACTORY_IMPORTS.add(component.getCanonicalName());
186                            // configuratorImports.add(TARGET_PACKAGE+"."+component.getSimpleName()+CONFIGURATOR);
187    
188                            ConfigJavaGenerator c = new ConfigJavaGenerator(component,
189                                            "reasoner");
190                            c.imports.add("org.dllearner.core.KnowledgeSource");
191                            c.imports.add(Set.class.getCanonicalName());
192                            c.additionalMandatoryVars.put("Set<KnowledgeSource> knowledgeSource",
193                                            "knowledgeSource");
194                            c.makeConfigurator();
195    
196                    }
197                    for (Class<? extends LearningProblem> component : cm
198                                    .getLearningProblems()) {
199    
200                            COMPONENT_FACTORY_IMPORTS.add(component.getCanonicalName());
201                            // configuratorImports.add(TARGET_PACKAGE + "." +
202                            // component.getSimpleName()+CONFIGURATOR);
203    
204                            ConfigJavaGenerator c = new ConfigJavaGenerator(component,
205                                            "learningProblem");
206                            c.imports.add("org.dllearner.core.ReasonerComponent");
207                            c.additionalMandatoryVars.put("ReasonerComponent reasoningService",
208                                            "reasoningService");
209                            c.makeConfigurator();
210    
211                    }
212    
213                    for (Class<? extends LearningAlgorithm> component : cm
214                                    .getLearningAlgorithms()) {
215    
216                            COMPONENT_FACTORY_IMPORTS.add(component.getCanonicalName());
217                            // configuratorImports.add(TARGET_PACKAGE+"."+
218                            // component.getSimpleName()+CONFIGURATOR);
219    
220                            ConfigJavaGenerator c = new ConfigJavaGenerator(component,
221                                            "learningAlgorithm");
222                            c.imports.add("org.dllearner.core.LearningProblem");
223                            c.imports.add("org.dllearner.core.ReasonerComponent");
224                            c.imports.add(LearningProblemUnsupportedException.class
225                                            .getCanonicalName());
226    
227                            c.additionalMandatoryVars.put("LearningProblem learningProblem",
228                                            "learningProblem");
229                            c.additionalMandatoryVars.put("ReasonerComponent reasoningService",
230                                            "reasoningService");
231                            c.getinstanceExceptions.add("LearningProblemUnsupportedException");
232                            c.makeConfigurator();
233    
234                    }
235    
236                    makeComponentFactory();
237                    makeInterface();
238                    writePackageHTML();
239    
240                    System.out.println("Done");
241            }
242            
243            
244    
245            private ConfigJavaGenerator(Class<? extends Component> component,
246                            String componentType) {
247                    this.className = component.getSimpleName();
248                    this.component = component;
249                    this.componentType = componentType;
250                    imports.add(component.getCanonicalName());
251                    imports.add(ComponentManager.class.getCanonicalName());
252                    // imports.add(Configurator.class.getCanonicalName());
253                    // imports.add(ConfigEntry.class.getCanonicalName());
254    
255                    vars.add("private " + className + " " + deCapitalize(className)
256                                                    + ";\n");
257                    
258                    if(EXTENDSREFINEMENTOPERATOR.contains(this.className+CONFIGURATOR)){
259                                    this.extendS = EXTENDSREFINEMENTOPERATORCLASS.getSimpleName();
260                                    this.imports.add(EXTENDSREFINEMENTOPERATORCLASS.getCanonicalName());
261                            }
262                            
263    
264            }
265    
266            private void makeConfigurator() {
267    
268                    for (ConfigOption<?> option : ComponentManager
269                                    .getConfigOptions(component)) {
270    
271                            String type = option.getValueTypeAsJavaString();
272                            String optionName = option.getName();
273                            // String defaultValue = option.getDefaultValueInJava();
274                            String comment = option.getJavaDocString();
275    
276                            if (option.isMandatory()) {
277                                    mandatoryVars.put(type + " " + optionName, optionName);
278                                    mandatoryOptions.add(option);
279                                    COMPONENT_FACTORY_IMPORTS.addAll(option.getJavaImports());
280                            }
281    
282                            imports.addAll(option.getJavaImports());
283                            // vars.add(fillVariableDefTemplate(optionName, type,
284                            // defaultValue));
285                            setters.add(fillSetterTemplate(className, comment, optionName,
286                                            type, option.requiresInit()));
287                            getters
288                                            .add(fillGetterTemplate(className, comment, optionName,
289                                                            type));
290                            // System.out.println(option);
291                            // componentOptions.get(component)) {
292    
293                    }
294    
295                    body.add("private boolean " + REINITVAR + " = false;");
296                    body.add(UNUSED);
297                    body.add(expandCollection(vars, "", "", 0));
298                    body.add(fillConstructorTemplate(className));
299    
300                    body
301                                    .add(makeGetInstanceForConfigurators(getAllCommentsForOptionList(mandatoryOptions)));
302    
303                    // body.add(makeApplyConfigEntryForOptionList(ComponentManager.getConfigOptions(component)));
304     
305                    body.add(expandCollection(getters, "", "", 0));
306                    body.add(expandCollection(setters, "", "", 0));
307                    body.add(REINITGETTER);
308                    String bodytmp = expandCollection(body, "", "\n",0);
309                    String importtmp =  expandCollection(imports, "import ", ";\n", 0);
310                    String ret = fillClassTemplate(
311                                    TARGET_PACKAGE,
312                                    importtmp, 
313                                    className + CONFIGURATOR, 
314                                    extendS,
315                                    bodytmp ,
316                                    "", 
317                                    CONFIGURATOR); 
318    
319                    // configuratorMethods.add((className, componentType,
320                    // mandatoryOptions));
321    
322                    Files.createFile(new File(TARGET_DIR + "/" + className + CONFIGURATOR
323                                    + ".java"), ret);
324    
325                    COMPONENT_FACTORY_METHODS
326                                    .add(makeComponentFactoryMethods(getAllCommentsForOptionList(mandatoryOptions)));
327            }
328    
329            private static void makeInterface(){
330                    String ret ="";
331                    ret+= HEADER+"\n";
332                    ret+= "package "+TARGET_PACKAGE+";\n\n";
333                    ret+= fillJavaDocComment(CLASS_COMMENT);
334                    ret+="public interface Configurator{\n}\n";
335                    Files.createFile(new File(TARGET_DIR+File.separator+CONFIGURATOR+".java"), ret);
336            }
337            
338            @SuppressWarnings("unused")
339            private static String makeApplyConfigEntryForOptionList(
340                            List<ConfigOption<?>> options) {
341                    String ret = "@SuppressWarnings({ \"unchecked\" })\n"
342                                    + "public <T> void applyConfigEntry(ConfigEntry<T> entry){\n";
343                    ret += "String optionName = entry.getOptionName();\n";
344                    // ret+="ConfigOption<T> option = entry.getOption();\n";
345                    ret += "if(false){//empty block \n}";
346    
347                    for (ConfigOption<?> option : options) {
348                            ret += "else if (optionName.equals(\"" + option.getName()
349                                            + "\")){\n";
350                            ret += "" + option.getName() + " = ("
351                                            + rightType(option.getValueTypeAsJavaString()) + ") "
352                                            + " entry.getValue();\n}";
353    
354                    }
355                    ret += "\n}\n";
356                    return ret;
357            }
358    
359            private static String getAllCommentsForOptionList(
360                            List<ConfigOption<?>> options) {
361                    String ret = "";
362                    for (ConfigOption<?> option : options) {
363                            ret += "* @param " + option.getName() + " "
364                                            + option.getDescription() + "\n";
365                    }
366    
367                    return ret;
368            }
369    
370            private String makeGetInstanceForConfigurators(String comments) {
371    
372                    Map<String, String> parametersWithType = new LinkedHashMap<String, String>();
373                    Map<String, String> parametersNoType = new LinkedHashMap<String, String>();
374                    String applyConf = "";
375                    // parametersWithType.put("ComponentManager cm", "cm");
376                    parametersNoType.put(className + ".class", className + ".class");
377                    for (String s : additionalMandatoryVars.keySet()) {
378                            parametersWithType.put(s, additionalMandatoryVars.get(s));
379                            parametersNoType.put(s, additionalMandatoryVars.get(s));
380                            comments ="* @param "+additionalMandatoryVars.get(s)+" see "+additionalMandatoryVars.get(s)+"\n"+comments;
381                    }
382                    for (String s : mandatoryVars.keySet()) {
383                            parametersWithType.put(s, mandatoryVars.get(s));
384                            applyConf += fillApplyConfigEntry("component", mandatoryVars.get(s));
385                    }
386    
387                    String parWithType = expandCollection(parametersWithType.keySet(), "",
388                                    ", ", 2);
389    
390                    String par = expandCollection(parametersNoType.values(), "", ", ", 2);
391    
392                    String exceptions = "";
393                    if (!getinstanceExceptions.isEmpty()) {
394                            exceptions += "throws ";
395                            exceptions += expandCollection(getinstanceExceptions, "", ", ", 2);
396                            comments+=expandCollection(getinstanceExceptions, "* @throws ", " see \n", 0);
397                    }
398                    comments = fillJavaDocComment(comments + "* @return " + className
399                                    + "\n");
400                    String ret = comments;
401                    ret += "public static " + className + " get" + className + "("
402                                    + parWithType + ") " + exceptions + "{\n";
403                    ret += className + " component = ComponentManager.getInstance()."
404                                    + componentType + "(" + par + ");\n";
405                    ret += applyConf;
406    
407                    ret += "return component;\n}\n";
408                                    
409                    return ret;
410            }
411            
412            
413    
414    
415            private String makeComponentFactoryMethods(String comments) {
416    
417                    Map<String, String> parametersWithType = new LinkedHashMap<String, String>();
418                    Map<String, String> parametersNoType = new LinkedHashMap<String, String>();
419                    String applyConf = "";
420                    // parametersWithType.put("ComponentManager cm", "cm");
421                    parametersNoType.put(className + ".class", className + ".class");
422                    for (String s : additionalMandatoryVars.keySet()) {
423                            parametersWithType.put(s, additionalMandatoryVars.get(s));
424                            parametersNoType.put(s, additionalMandatoryVars.get(s));
425                            comments += "* @param " + additionalMandatoryVars.get(s) + " see "
426                                            + capitalize(additionalMandatoryVars.get(s)) + "\n";
427                    }
428                    for (String s : mandatoryVars.keySet()) {
429                            parametersWithType.put(s, mandatoryVars.get(s));
430                            applyConf += fillApplyConfigEntry("component", mandatoryVars.get(s))
431                                            + "";
432                    }
433    
434                    String parWithType = expandCollection(parametersWithType.keySet(), "",
435                                    ", ", 2);
436    
437                    String par = expandCollection(parametersWithType.values(), "", ", ", 2);
438    
439                    String exceptions = "";
440                    if (!getinstanceExceptions.isEmpty()) {
441                            exceptions += "throws ";
442                            exceptions += expandCollection(getinstanceExceptions, "", ", ", 2);
443                            comments += expandCollection(getinstanceExceptions, "* @throws ",
444                                            " see\n", 0);
445                    }
446                    comments += "* @return a component ready for initialization "
447                                    + className + "\n";
448                    String ret = fillJavaDocComment(comments);
449                    ret += "public static " + className + " get" + className + "("
450                                    + parWithType + ") " + exceptions + " {\n" + "return "
451                                    + className + CONFIGURATOR + ".get" + className + "(" + par
452                                    + ");\n}\n";
453                    return ret;
454            }
455    
456            private static void makeComponentFactory() {
457                    String ret = fillClassTemplate(TARGET_PACKAGE, expandCollection(
458                                    COMPONENT_FACTORY_IMPORTS, "import ", ";\n", 0),
459                                    COMPONENT_FACTORY, "", expandCollection(
460                                                    COMPONENT_FACTORY_METHODS, "", "\n", 0), "final","");
461    
462                    Files.createFile(new File(TARGET_DIR + "/" + COMPONENT_FACTORY
463                                    + ".java"), ret);
464            }
465    
466            private static String fillApplyConfigEntry(String className,
467                            String optionName) {
468                    return "ComponentManager.getInstance().applyConfigEntry("
469                                    + deCapitalize(className) + ", \"" + optionName + "\", "
470                                    + optionName + ");\n";
471            }
472    
473            private static String fillConstructorTemplate(String className) {
474                    return fillJavaDocComment("* @param " + deCapitalize(className)
475                                    + " see " + className + "\n")
476                                    + "public "
477                                    + className
478                                    + CONFIGURATOR
479                                    + "("
480                                    + className
481                                    + " "
482                                    + deCapitalize(className)
483                                    + "){\n"
484                                    + "this."
485                                    + deCapitalize(className)
486                                    + " = "
487                                    + deCapitalize(className)
488                                    + ";\n" + "}\n";
489            }
490    
491            @SuppressWarnings("unused")
492            private static String fillVariableDefTemplate(String optionName,
493                            String type, String defaultValue) {
494                    return "private " + type + " " + optionName + " = " + defaultValue
495                                    + ";\n";
496    
497            }
498    
499            private static String fillJavaDocComment(String lines) {
500                    return "/**\n" + lines + "**/\n";
501            }
502    
503            private static String fillGetterTemplate(String className, String comment,
504                            String optionName, String type) {
505                    comment = comment.replaceAll("@param ", "");
506                    comment += "* @return " + checkstyleAdjust(type) + " \n";
507                    String ret = fillJavaDocComment(comment);
508                    ret += (type.contains("<String>") || type.contains("<StringTuple>")) ? "@SuppressWarnings(\"unchecked\")\n"
509                                    : "";
510                    ret += "public " + type + " get" + capitalize(optionName) + "() {\n";
511                    ret += "return (" + rightType(type)
512                                    + ") ComponentManager.getInstance().";
513                    ret += "getConfigOptionValue(" + deCapitalize(className) + ",  \""
514                                    + optionName + "\") ;\n";
515                    ret += "}\n";
516                    return ret;
517            }
518    
519            private static String fillSetterTemplate(String className, String comment,
520                            String optionName, String type, boolean reinit) {
521                    String ret = fillJavaDocComment(comment);
522                    ret += "public void set" + capitalize(optionName);
523                    ret += "(" + type + " " + optionName + ") {\n";
524                    ret += fillApplyConfigEntry(className, optionName);
525                    ret += (reinit) ? REINITVAR + " = true;\n" : "";
526                    ret += "}\n";
527                    return ret;
528            }
529    
530            
531            
532            
533            private static String fillClassTemplate(String packagE, String imports,
534                            String className, String extendS,  String body, String classModifier, String implementS) {
535                    
536    
537                    String ret = HEADER + "\n";
538                    ret += "package " + packagE + ";\n\n";
539                    ret += imports + "\n";
540                    ret += fillJavaDocComment(CLASS_COMMENT);
541                    ret += (INCLUDE_UNUSED) ? UNUSED : "";
542                    ret += (!extendS.isEmpty()) ? OVERRIDE : "";
543                    ret += "public "+classModifier+" class " + className + " "
544                                    + ((extendS.length() > 0) ? " extends " + extendS : "")
545                                    + ((implementS.length() > 0) ? " implements " + implementS : "")
546                                    + " {\n\n";
547                    ret += body + "\n";
548                    ret += "}\n";
549                    return ret;
550    
551            }
552            
553            private static void writePackageHTML(){
554                    String c = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">"+
555                    "<html>"+
556                    "<head></head>"+
557                    "<body bgcolor=\"white\">"+
558                    "<p>Automatically generated classes, which enable programmatically setting"+
559                    "and getting configuration options of components.</p>"+
560                    "</body>"+
561                    "</html>";
562                    try{
563                    Files.createFile(new File(TARGET_DIR+File.separator+"package.html"), c);
564                    }catch (Exception e) {
565                            e.printStackTrace();
566                    }
567            }
568            
569    
570            private static String expandCollection(Collection<String> col,
571                            String before, String after, int removeChars) {
572                    if (col.isEmpty()){
573                            return "";
574                    }
575                    String ret = "";
576                    for (String string : col) {
577                            ret += before + string + after;
578                    }
579    
580                    if (removeChars == 0) {
581                            return ret;
582                    } else {
583                            return ret.substring(0, ret.length() - removeChars);
584                    }
585            }
586    
587            private static String capitalize(String s) {
588                    String tmp = s.substring(0, 1);
589                    return tmp.toUpperCase() + s.substring(1);
590            }
591    
592            private static String deCapitalize(String s) {
593                    String tmp = s.substring(0, 1);
594                    return tmp.toLowerCase() + s.substring(1);
595            }
596    
597            private static String rightType(String type) {
598                    if (type.equals("int")){
599                            return "Integer";
600                    }else if (type.equals("boolean")){
601                            return "Boolean";
602                    }else if (type.equals("double")){
603                            return "Double";
604                    }else{
605                            return type;
606                    }
607    
608            }
609    
610    
611            private static String checkstyleAdjust(String type) {
612                    type = type.replaceAll("<", "(");
613                    type = type.replaceAll(">", ")");
614                    return type;
615            }
616    
617            private static String getHeader() {
618                    try {
619                            return Files.readFile(new File(HEADER_FILE));
620                    } catch (Exception e) {
621                            e.printStackTrace();
622                    }
623                    return "";
624            }
625    
626    }