001/* 002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon) 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.golo.doc; 011 012import gololang.FunctionReference; 013import gololang.TemplateEngine; 014import gololang.Predefined; 015 016import java.io.IOException; 017import java.io.InputStream; 018import java.io.InputStreamReader; 019import java.nio.file.Path; 020import java.nio.charset.StandardCharsets; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Set; 024import java.util.TreeSet; 025 026public abstract class AbstractProcessor { 027 028 public abstract String render(ModuleDocumentation module) throws Throwable; 029 030 public abstract void process(Map<String, ModuleDocumentation> modules, Path targetFolder) throws Throwable; 031 032 private TemplateEngine templateEngine = new TemplateEngine(); 033 private HashMap<String, FunctionReference> templateCache = new HashMap<>(); 034 035 private Path targetFolder; 036 private Set<ModuleDocumentation> modules = new TreeSet<>(); 037 038 public void setTargetFolder(Path target) { 039 this.targetFolder = target.toAbsolutePath(); 040 } 041 042 public Path getTargetFolder() { 043 return this.targetFolder; 044 } 045 046 public Set<ModuleDocumentation> modules() { 047 return modules; 048 } 049 050 protected void addModule(ModuleDocumentation module) { 051 modules.add(module); 052 } 053 054 protected String fileExtension() { 055 return ""; 056 } 057 058 protected FunctionReference template(String name, String format) throws IOException { 059 String key = name + "-" + format; 060 if (templateCache.containsKey(key)) { 061 return templateCache.get(key); 062 } 063 InputStream in = AbstractProcessor.class.getResourceAsStream("/org/eclipse/golo/doc/" + key); 064 if (in == null) { 065 throw new IllegalArgumentException("There is no template " + name + " for format: " + format); 066 } 067 try (InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { 068 StringBuilder builder = new StringBuilder(); 069 char[] buffer = new char[1024]; 070 int nread; 071 while ((nread = reader.read(buffer)) > 0) { 072 builder.append(buffer, 0, nread); 073 } 074 FunctionReference compiledTemplate = templateEngine.compile(builder.toString()); 075 templateCache.put(key, compiledTemplate); 076 return compiledTemplate; 077 } 078 } 079 080 public Path outputFile(String name) { 081 if (targetFolder == null) { 082 throw new IllegalStateException("no target folder defined"); 083 } 084 return targetFolder.resolve(name.replace('.', '/') 085 + (fileExtension().isEmpty() ? "" : ("." + fileExtension()))); 086 } 087 088 /** 089 * Return the absolute path of the file containing the given element. 090 */ 091 public Path docFile(DocumentationElement doc) { 092 DocumentationElement parent = doc; 093 while (parent.parent() != parent) { 094 parent = parent.parent(); 095 } 096 return outputFile(parent.name()); 097 } 098 099 /** 100 * Returns the link to the given filename from the given document. 101 */ 102 public String linkToFile(DocumentationElement src, String dst) { 103 Path doc = docFile(src); 104 if (doc.getParent() != null) { 105 doc = doc.getParent(); 106 } 107 return doc.relativize(outputFile(dst)).toString(); 108 } 109 110 /** 111 * Returns the link to the given filename from the given filename. 112 */ 113 public String linkToFile(String src, String dst) { 114 Path out = outputFile(src); 115 if (out.getParent() != null) { 116 out = out.getParent(); 117 } 118 return out.relativize(outputFile(dst)).toString(); 119 } 120 121 protected void renderIndex(String templateName) throws Throwable { 122 FunctionReference indexTemplate = template(templateName, fileExtension()); 123 String index = (String) indexTemplate.invoke(this); 124 Predefined.textToFile(index, outputFile(templateName)); 125 } 126 127 /** 128 * Change the section level of the given markdown line. 129 * <p> 130 * For instance, {@code subsection("# Title", 1)} gives {@code "## Title"} 131 * <p> 132 * Used when displaying a markdown Golo documentation inside the golodoc, to avoid the user to 133 * remember at which level to start subsections for each documentation element. 134 */ 135 private static String subsection(String line, int level) { 136 if (!line.trim().startsWith("#") || line.startsWith(" ")) { 137 return line; 138 } 139 StringBuilder output = new StringBuilder(); 140 for (int i = 0; i < level; i++) { 141 output.append('#'); 142 } 143 output.append(line.trim()); 144 return output.toString(); 145 } 146 147 public static String adaptSections(String documentation, int rootLevel) { 148 StringBuilder output = new StringBuilder(); 149 for (String line: documentation.split("\n")) { 150 output.append(subsection(line, rootLevel)); 151 output.append("\n"); 152 } 153 return output.toString(); 154 } 155}