org.opentripplanner.graph_builder.AnnotationsToHTML Maven / Gradle / Ivy
package org.opentripplanner.graph_builder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.*;
import java.util.logging.Level;
import org.apache.commons.io.FileUtils;
import org.opentripplanner.graph_builder.annotation.GraphBuilderAnnotation;
import org.opentripplanner.graph_builder.services.GraphBuilderModule;
import org.opentripplanner.routing.graph.Graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class generates nice HTML graph annotations reports
*
* They are created with the help of getHTMLMessage function in {@link GraphBuilderAnnotation} derived classes.
* @author mabu
*/
public class AnnotationsToHTML implements GraphBuilderModule {
private static Logger LOG = LoggerFactory.getLogger(AnnotationsToHTML.class);
//Path to output folder
private File outPath;
//If there are more then this number annotations are split into multiple files
//This is because browsers aren't made for giant HTML files which can be made with 500k annotations
private int maxNumberOfAnnotationsPerFile;
//This counts all occurrences of HTML annotations classes
//If one annotation class is split into two files it has two entries in this Multiset
//IT is used to show numbers in HTML files name and links
Multiset annotationClassOccurences;
//List of writers which are used for actual writing annotations to HTML
List writers;
//Key is classname, value is annotation message
//Multimap because there are multiple annotations for each classname
private Multimap annotations;
public AnnotationsToHTML (File outpath, int maxNumberOfAnnotationsPerFile) {
this.outPath = outpath;
annotations = ArrayListMultimap.create();
this.maxNumberOfAnnotationsPerFile = maxNumberOfAnnotationsPerFile;
this.writers = new ArrayList<>();
this.annotationClassOccurences = HashMultiset.create();
}
@Override
public void buildGraph(Graph graph, HashMap, Object> extra) {
if (outPath == null) {
LOG.error("Saving folder is empty!");
return;
}
outPath = new File(outPath, "report");
if (outPath.exists()) {
//Removes all files from report directory
try {
FileUtils.cleanDirectory(outPath);
} catch (IOException e) {
LOG.error("Failed to clean HTML report directory: " + outPath.toString() + ". HTML report won't be generated!", e);
return;
}
} else {
//Creates report directory if it doesn't exist yet
try {
FileUtils.forceMkdir(outPath);
} catch (IOException e) {
e.printStackTrace();
LOG.error("Failed to create HTML report directory: " + outPath.toString() + ". HTML report won't be generated!", e);
return;
}
}
//Groups annotations in multimap according to annotation class
for (GraphBuilderAnnotation annotation : graph.getBuilderAnnotations()) {
//writer.println("" + annotation.getHTMLMessage() + "
");
// writer.println("" + annotation.getClass().getSimpleName()+"");
addAnnotation(annotation);
}
LOG.info("Creating Annotations log");
//Creates list of HTML writers. Each writer has whole class of HTML annotations
//Or multiple HTML writers can have parts of one class of HTML annotations if number
// of annotations is larger than maxNumberOfAnnotationsPerFile
for (Map.Entry> entry: annotations.asMap().entrySet()) {
List annotationsList;
if (entry.getValue() instanceof List) {
annotationsList = (List) entry.getValue();
} else {
annotationsList = new ArrayList<>(entry.getValue());
}
addAnnotations(entry.getKey(), annotationsList);
}
//Actual writing to the file is made here since
// this is the first place where actual number of files is known (because it depends on annotations count)
for (HTMLWriter writer : writers) {
writer.writeFile(annotationClassOccurences, false);
}
try {
HTMLWriter indexFileWriter = new HTMLWriter("index", (Multimap)null);
indexFileWriter.writeFile(annotationClassOccurences, true);
} catch (FileNotFoundException e) {
LOG.error("Index file coudn't be created:{}", e);
}
LOG.info("Annotated logs are in {}", outPath);
}
/**
* Creates file with given class of annotations
*
* If number of annotations is larger then maxNumberOfAnnotationsPerFile multiple files are generated.
* And named annotationClassName1,2,3 etc.
*
* @param annotationClassName name of annotation class and then also filename
* @param annotations list of all annotations with that class
*/
private void addAnnotations(String annotationClassName, List annotations) {
try {
HTMLWriter file_writer;
if (annotations.size() > 1.2*maxNumberOfAnnotationsPerFile) {
LOG.debug("Number of annotations is very large. Splitting: {}", annotationClassName);
List> partitions = Lists.partition(annotations, maxNumberOfAnnotationsPerFile);
for (List partition: partitions) {
annotationClassOccurences.add(annotationClassName);
int labelCount = annotationClassOccurences.count(annotationClassName);
file_writer =new HTMLWriter(annotationClassName+Integer.toString(labelCount), partition);
writers.add(file_writer);
}
} else {
annotationClassOccurences.add(annotationClassName);
int labelCount = annotationClassOccurences.count(annotationClassName);
file_writer = new HTMLWriter(annotationClassName + Integer.toString(labelCount),
annotations);
writers.add(file_writer);
}
} catch (FileNotFoundException ex) {
LOG.error("Output folder not found:{} {}", outPath, ex);
}
}
@Override
public void checkInputs() {
}
/**
* Groups annotations according to annotation class name
*
* All annotations are saved together in multimap where key is annotation classname
* and values are list of annotations with that class
* @param annotation
*/
private void addAnnotation(GraphBuilderAnnotation annotation) {
String className = annotation.getClass().getSimpleName();
annotations.put(className, annotation.getHTMLMessage());
}
class HTMLWriter {
private PrintStream out;
private Multimap writerAnnotations;
private String annotationClassName;
public HTMLWriter(String key, Collection annotations) throws FileNotFoundException {
LOG.debug("Making file: {}", key);
File newFile = new File(outPath, key +".html");
FileOutputStream fileOutputStream = new FileOutputStream(newFile);
this.out = new PrintStream(fileOutputStream);
writerAnnotations = ArrayListMultimap.create();
writerAnnotations.putAll(key, annotations);
annotationClassName = key;
}
public HTMLWriter(String filename, Multimap curMap)
throws FileNotFoundException {
LOG.debug("Making file: {}", filename);
File newFile = new File(outPath, filename +".html");
FileOutputStream fileOutputStream = new FileOutputStream(newFile);
this.out = new PrintStream(fileOutputStream);
writerAnnotations = curMap;
annotationClassName = filename;
}
private void writeFile(Multiset classes, boolean isIndexFile) {
println("Graph report for " + outPath.getParentFile()
+ "Graph.obj ");
println("\t");
println("");
println("");
println(
"");
String css = "\t\t\n"
+ "";
println(css);
println("");
println(String.format("OpenTripPlanner annotations log for %s
", annotationClassName));
println("Graph report for " + outPath.getParentFile() + "Graph.obj
");
println("");
//adds links to the other HTML files
for (Multiset.Entry htmlAnnotationClass : classes.entrySet()) {
String label_name = htmlAnnotationClass.getElement();
String label;
int currentCount = 1;
//it needs to add link to every file even if they are split
while (currentCount <= htmlAnnotationClass.getCount()) {
label = label_name + currentCount;
if (label.equals(annotationClassName)) {
println(String.format("",
label_name.toLowerCase(), label));
} else {
println(String.format("%s",
label_name.toLowerCase(), label, label));
}
currentCount++;
}
}
println("
");
if (!isIndexFile) {
println("");
writeAnnotations();
println("
");
}
println("");
close();
}
/**
* Writes annotations as LI html elements
*/
private void writeAnnotations() {
String annotationFMT = "%s ";
for (Map.Entry annotation: writerAnnotations.entries()) {
print(String.format(annotationFMT, annotation.getValue()));
}
}
private void println(String bodyhtml) {
out.println(bodyhtml);
}
private void print(String bodyhtml) {
out.print(bodyhtml);
}
private void close() {
out.close();
}
/**
* Generates JSON from annotations variable which is used by Javascript
* to display HTML report
*/
private void writeJson() {
try {
out.print("\tvar data=");
ObjectMapper mapper = new ObjectMapper();
JsonGenerator jsonGenerator = mapper.getJsonFactory().createJsonGenerator(out);
mapper.writeValue(jsonGenerator, writerAnnotations.asMap());
out.println(";");
} catch (IOException ex) {
java.util.logging.Logger.getLogger(AnnotationsToHTML.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}