001/**
002 * Copyright (C) 2007-2010, 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 */
020package org.dllearner.cli;
021
022import com.google.common.base.CaseFormat;
023import com.google.common.base.Strings;
024import com.google.common.collect.Sets;
025import org.apache.commons.lang.StringUtils;
026import org.apache.log4j.Level;
027import org.dllearner.cli.DocumentationGeneratorMeta.GlobalDoc;
028import org.dllearner.configuration.spring.editors.ConfigHelper;
029import org.dllearner.core.*;
030import org.dllearner.core.config.ConfigOption;
031import org.dllearner.utilities.Files;
032import org.reflections.Reflections;
033import org.semanticweb.owlapi.model.OWLClass;
034
035import java.io.File;
036import java.lang.reflect.Field;
037import java.util.*;
038import java.util.Map.Entry;
039
040/**
041 * 
042 * @author Jens Lehmann
043 *
044 */
045@SuppressWarnings("StringConcatenationInsideStringBufferAppend")
046public class DocumentationGenerator {
047        static {
048                if (System.getProperty("log4j.configuration") == null)
049                        System.setProperty("log4j.configuration", "log4j.properties");
050                //AnnComponentManager.setReflectionScanner(new Reflections());
051                org.apache.log4j.Logger.getLogger(AnnComponentManager.class).setLevel(Level.DEBUG);
052        }
053        private AnnComponentManager cm = AnnComponentManager.getInstance();
054        
055        private static final Map<Class, String> varNameMapping = new HashMap<>();
056        
057        static {
058                varNameMapping.put(LearningAlgorithm.class, "la");
059                varNameMapping.put(LearningProblem.class, "lp");
060                varNameMapping.put(KnowledgeSource.class, "ks");
061                varNameMapping.put(AbstractReasonerComponent.class, "reasoner");
062                varNameMapping.put(org.dllearner.core.Heuristic.class, "h");
063                varNameMapping.put(org.dllearner.refinementoperators.RefinementOperator.class, "op");
064        }
065
066        public String getConfigDocumentationString() {
067                String doc = "";
068                doc += "This file contains an automatically generated files of all components and their config options.\n\n";
069                
070                Set<Class<?>> componentsDone = new HashSet<>();
071                Class<?>[] nonComponentClasses = {
072                                CLI.class,
073                                GlobalDoc.class
074                };
075                
076                // go through all types of components and write down their documentation
077                List<Class> coreComps = Arrays.asList(AnnComponentManager.coreComponentClasses);
078                Collections.reverse(coreComps);
079                LinkedList<String> cc = new LinkedList<>();
080                doc += "*************************\n"
081                         + "* Non-component classes *\n"
082                         + "*************************\n\n";
083                for (Class<?> comp : nonComponentClasses) {
084                        if (componentsDone.contains(comp)) continue;
085                        doc += getComponentConfigString(comp, Class.class);
086                        componentsDone.add(comp);
087                }
088                
089                for (Class<?> cats : coreComps) {
090                        ComponentAnn ann = cats.getAnnotation(ComponentAnn.class);
091                        String name = cats.getSimpleName();
092                        if (ann != null) {
093                                name = ann.name();
094                        }
095                        StringBuilder sc = new StringBuilder();
096                        sc.append("\n" + Strings.repeat("*", name.length() + 4) + "\n"
097                                        + "* " + name + " *\n"
098                                        + Strings.repeat("*", name.length() + 4) + "\n\n");
099                        
100                        for(Class<? extends Component> comp : cm.getComponentsOfType(cats)) {
101                                if (componentsDone.contains(comp)) continue;
102                                sc.append(getComponentConfigString(comp, cats));
103                                componentsDone.add(comp);
104                        }
105                        cc.addFirst(sc.toString());
106                }
107                doc += StringUtils.join(cc, "\n");
108                for (Class<?> comp : cm.getComponents()) {
109                        StringBuilder sc = new StringBuilder();
110                        if (!componentsDone.contains(comp)) {
111                                if (componentsDone.contains(comp)) continue;
112                                sc.append(getComponentConfigString(comp, Component.class));
113                                componentsDone.add(comp);
114                        }
115                        if (sc.length() != 0) {
116                                doc += "\n********************\n"
117                                         + "* Other Components *\n"
118                                         + "********************\n\n"
119                                         + sc.toString();
120                        }
121                }
122                
123                return doc;
124        }
125        
126        /**
127         * Writes documentation for all components available in this
128         * <code>ComponentManager</code> instance. It goes through
129         * all components (sorted by their type) and all the configuration
130         * options of the components. Explanations, default values, allowed
131         * values for the options are collected and the obtained string is
132         * written in a file.
133         * @param file The documentation file.
134         */
135        public void writeConfigDocumentation(File file) {
136                Files.createFile(file, getConfigDocumentationString());
137        }
138        
139        private String getComponentConfigString(
140                        Class<?> component, Class<?> category) {
141                // heading + anchor
142                StringBuilder sb = new StringBuilder();
143                String klass = component.getName();
144
145                String header = "component: " + klass;
146                String description = "";
147                String catString = "component";
148                String usage = null;
149                
150                // some information about the component
151                if (Component.class.isAssignableFrom(component)) {
152                        Class<? extends Component> ccomp = (Class<? extends Component>) component;
153                        header = "component: " + AnnComponentManager.getName(ccomp) + " (" + klass + ") v" + AnnComponentManager.getVersion(ccomp);
154                        description = AnnComponentManager.getDescription(ccomp);
155                        catString = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, category.getSimpleName());
156                        ComponentAnn catAnn = category.getAnnotation(ComponentAnn.class);
157                        if (catAnn != null) {
158                                catString = catAnn.shortName();
159                        }
160                        
161                        for (Entry<Class, String> entry : varNameMapping.entrySet()) {
162                                Class cls = entry.getKey();
163                                if(cls.isAssignableFrom(component)) {
164                                        catString = entry.getValue();
165                                }
166                        }
167                        
168                        
169                        usage = "conf file usage: " + catString + ".type = \"" + AnnComponentManager.getShortName(ccomp) + "\"\n";
170                        
171                } else {
172                        ComponentAnn ann = component.getAnnotation(ComponentAnn.class);
173                        if (ann != null) {
174                                header =  "component: " + ann.name() + " (" + klass + ") v" + ann.version();
175                        }
176                        description = ann.description();
177                        if (component.equals(GlobalDoc.class)) {
178                                catString="";
179                                usage = "";
180                        } else {
181                                catString = "cli";
182
183                                usage = "conf file usage: " + catString + ".type = \"" + klass + "\"\n";
184                        }
185                }
186                header += "\n" + Strings.repeat("=", header.length()) + "\n";
187                sb.append(header);
188                if(description.length() > 0) {
189                        sb.append(description + "\n");
190                }
191                sb.append("\n");
192                sb.append(usage + "\n");
193                optionsTable(sb, component, catString);
194                return sb.toString();
195        }
196
197        private void optionsTable(StringBuilder sb, Class<?> component, String catString) {
198                // generate table for configuration options
199                Map<Field,Class<?>> options = ConfigHelper.getConfigOptionTypes(component);
200                for(Entry<Field,Class<?>> entry : options.entrySet()) {
201                        String optionName = AnnComponentManager.getName(entry.getKey());
202                        ConfigOption option = entry.getKey().getAnnotation(ConfigOption.class);
203                        String type = entry.getValue().getSimpleName();
204                        if(entry.getValue().equals(OWLClass.class)) {
205                                type = "IRI";
206                        }
207                        String exampleValue = !option.exampleValue().isEmpty() ? option.exampleValue() : option.defaultValue();
208                        Set<Class> noQuoteClasses = Sets.<Class>newHashSet(boolean.class, int.class, long.class, float.class, double.class, short.class, List.class, Set.class, Map.class);
209                        Set<Class> collectionClasses = Sets.<Class>newHashSet(List.class, Set.class);
210                        Set<Class> mapClasses = Sets.<Class>newHashSet(Map.class);
211                        
212                        boolean needsQuotes = !noQuoteClasses.contains(entry.getValue());
213                        boolean isCollection = collectionClasses.contains(entry.getValue());
214                        boolean isMap = mapClasses.contains(entry.getValue());
215                        
216                        if(needsQuotes) {
217                                exampleValue = "\"" + exampleValue + "\"";
218                        } else if(isCollection && exampleValue.isEmpty()){
219                                exampleValue = "{" + exampleValue + "}";
220                        } else if (isMap && exampleValue.isEmpty()) {
221                                exampleValue = "[]";
222                        }
223                        
224                        sb.append("option name: " + optionName + "\n"
225                                        + "description: " + option.description() + "\n"
226                                        + "type: " + type + "\n"
227                                        + "required: " + option.required() + "\n"
228                                        + "default value: " + option.defaultValue() + "\n"
229                                        + "conf file usage: " + (catString.isEmpty()? "" : catString + ".") + optionName + " = " + exampleValue +"\n");
230                        
231                        sb.append("\n");
232                }
233        }
234
235        /**
236         * @param args
237         */
238        public static void main(String[] args) {
239                File file = new File("doc/configOptions.txt");
240                DocumentationGenerator dg = new DocumentationGenerator();
241                dg.writeConfigDocumentation(file);
242                System.out.println("Done");
243        }
244        
245}