|
package rendering.source; |
|
|
|
import com.github.javaparser.JavaParser; |
|
import com.github.javaparser.ParserConfiguration; |
|
import com.github.javaparser.ast.CompilationUnit; |
|
import indexing.ImportVisitor; |
|
import indexing.Index; |
|
import options.OdinOptions; |
|
import org.apache.commons.text.StringEscapeUtils; |
|
|
|
import javax.annotation.Nonnull; |
|
import java.io.IOException; |
|
import java.nio.file.Files; |
|
import java.nio.file.Path; |
|
import java.nio.file.Paths; |
|
import java.util.*; |
|
import java.util.stream.Collectors; |
|
|
|
public record SourceHtmlRenderer( |
|
Index index, |
|
OdinOptions odinOptions, |
|
String header, |
|
String footer |
|
){ |
|
|
|
public SourceHtmlRenderer(Index index, |
|
OdinOptions odinOptions) { |
|
this( |
|
index, |
|
odinOptions, |
|
String.format( |
|
""" |
|
<html> |
|
<head> |
|
<link rel="stylesheet" type="text/css" href="%s"/> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
</head> |
|
<body> |
|
<a class="index-link" href="%s">Back to index...</a> |
|
<table> |
|
""", |
|
odinOptions.webPathToCssFile, |
|
odinOptions.webPathToSourceHtmlFiles |
|
), |
|
String.format( |
|
""" |
|
</table> |
|
</body> |
|
<a class="index-link" href="%s">Back to index...</a> |
|
</html> |
|
""", |
|
odinOptions.webPathToSourceHtmlFiles |
|
) |
|
); |
|
} |
|
|
|
public void proccessFiles( |
|
String inputDirectory, |
|
Collection<Path> files |
|
) throws IOException { |
|
for (Path path : files) { |
|
processFile(inputDirectory, path); |
|
} |
|
} |
|
|
|
private void processFile( |
|
String inputDirectory, |
|
Path path |
|
) throws IOException { |
|
System.out.println("Rendering " + path); |
|
final String destinationStr = path.toString() |
|
.replace('\\', '/') |
|
.substring(0, path.toString().length()-5) |
|
.replace(inputDirectory, odinOptions.outputDirectory) |
|
+ ".html"; |
|
renderFile(path, destinationStr); |
|
} |
|
|
|
private void renderFile( |
|
Path inputFile, |
|
String outputFile |
|
) throws IOException { |
|
JavaParser javaParser = new JavaParser(new ParserConfiguration().setLanguageLevel( |
|
odinOptions.languageLevel)); |
|
// Get the parse tree from GitHub Java Parser, so we can navigate the parse tree |
|
|
|
CompilationUnit compilationUnit = javaParser.parse(inputFile).getResult().get(); |
|
|
|
// Use a visitor to collect all the imports. The imports are used to detemine what |
|
|
|
ImportVisitor importVisitor = new ImportVisitor(index); |
|
importVisitor.visit(compilationUnit, null); |
|
|
|
// Accumulate the changes from source code to html in a rendering queue that we apply at the end. |
|
|
|
RenderingQueue renderingQueue = new RenderingQueue(); |
|
|
|
|
|
ApplyIndexVisitor applyIndexVisitor = new ApplyIndexVisitor( |
|
outputFile, |
|
index, |
|
importVisitor.imports, |
|
renderingQueue |
|
); |
|
applyIndexVisitor.visit(compilationUnit, null); |
|
|
|
// Use the parse tree to figure out what css styles need to be applied. This is separates |
|
// linking from styling since methods, classes, variables, other linkables will have |
|
|
|
ApplySyntaxHighlightingVisitor syntaxHighlightingVisitor = new ApplySyntaxHighlightingVisitor( |
|
renderingQueue |
|
); |
|
syntaxHighlightingVisitor.visit(compilationUnit, null); |
|
|
|
|
|
Path destinationPath = Paths.get(outputFile); |
|
destinationPath.toFile().getParentFile().mkdirs(); |
|
String code = Files.readString(inputFile); |
|
Files.writeString( |
|
destinationPath, |
|
codeToHtml(code, renderingQueue) |
|
); |
|
} |
|
|
|
private String codeToHtml( |
|
String code, |
|
RenderingQueue renderingQueue |
|
) { |
|
StringBuilder sb = new StringBuilder(); |
|
sb.append(header); |
|
|
|
int lineNumber = 1; |
|
Iterable<String> lines = () -> code.lines().iterator(); |
|
for (String line : lines) { |
|
linePrefix(sb, lineNumber); |
|
|
|
Collection<String> beforeTasks = renderingQueue.beginningOfLineRenderingQueue.get(lineNumber); |
|
if (beforeTasks != null) { |
|
for (String content : beforeTasks) { |
|
sb.append(content); |
|
} |
|
} |
|
|
|
renderLine(sb, line, renderingQueue.withinLineRenderingQueue.getOrDefault( |
|
lineNumber, |
|
new RenderQueueLine() |
|
)); |
|
|
|
Collection<String> afterTasksRaw = renderingQueue.beginningOfLineRenderingQueue.get(lineNumber); |
|
if (afterTasksRaw != null) { |
|
List<String> afterTasks = new ArrayList<>(afterTasksRaw); |
|
Collections.reverse(afterTasks); |
|
for (String content : afterTasks) { |
|
sb.append(content); |
|
} |
|
} |
|
|
|
lineSuffix(sb); |
|
lineNumber++; |
|
} |
|
|
|
sb.append(footer); |
|
return sb.toString(); |
|
} |
|
|
|
private void linePrefix(StringBuilder sb, int lineNumber) { |
|
sb.append(String.format( |
|
""" |
|
<tr> |
|
<td><a id="linenum%d" class="linenum-cell" data-linenum="%d" href="#linenum%d"></a></td> |
|
<td><pre>""", |
|
lineNumber, |
|
lineNumber, |
|
lineNumber |
|
)); |
|
} |
|
|
|
private void lineSuffix(StringBuilder sb) { |
|
sb.append(String.format( |
|
""" |
|
</pre></td> |
|
</tr> |
|
""" |
|
)); |
|
} |
|
|
|
private void renderLine( |
|
StringBuilder sb, |
|
String line, |
|
@Nonnull RenderQueueLine renderQueueLine |
|
) { |
|
int currentCol = 1; |
|
for (char c : line.toCharArray()) { |
|
List<String> beforeContentRecords = renderQueueLine.renderQueueLine.getOrDefault(currentCol, Collections.emptyList()).stream() |
|
.filter(contentRecord -> contentRecord.positionType() == PositionType.BEFORE) |
|
.map(ContentRecord::content) |
|
.collect(Collectors.toList()); |
|
for (String before : beforeContentRecords) { |
|
sb.append(before); |
|
} |
|
|
|
sb.append(StringEscapeUtils.escapeHtml4(String.valueOf(c))); |
|
|
|
List<String> afterContentRecords = renderQueueLine.renderQueueLine.getOrDefault(currentCol, Collections.emptyList()).stream() |
|
.filter(contentRecord -> contentRecord.positionType() == PositionType.AFTER) |
|
.map(ContentRecord::content) |
|
// need to reverse so that the <a><div> </a></div> |
|
|
|
.collect(Collectors.toList()); |
|
Collections.reverse(afterContentRecords); |
|
for (String after : afterContentRecords) { |
|
sb.append(after); |
|
} |
|
currentCol++; |
|
} |
|
} |
|
} |
|
|