
info.leadinglight.umljavadoclet.UmlJavaDoclet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of uml-java-doclet Show documentation
Show all versions of uml-java-doclet Show documentation
Add UML diagrams (using PlantUML) to Javadocs. Extends the standard Java doclet.
The newest version!
package info.leadinglight.umljavadoclet;
import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.RootDoc;
import com.sun.tools.doclets.standard.Standard;
import info.leadinglight.umljavadoclet.model.Model;
import info.leadinglight.umljavadoclet.model.ModelClass;
import info.leadinglight.umljavadoclet.model.ModelPackage;
import info.leadinglight.umljavadoclet.printer.ContextDiagramPrinter;
import info.leadinglight.umljavadoclet.printer.DiagramOptions;
import info.leadinglight.umljavadoclet.printer.OverviewDiagramPrinter;
import info.leadinglight.umljavadoclet.printer.PackageDiagramPrinter;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.version.Version;
public class UmlJavaDoclet extends Standard {
public static boolean start(RootDoc root) {
// Generate Javadocs using standard doclet.
System.out.println("Generating Javadocs...");
System.out.println("Using arguments:");
int idx = -1;
for (int i = 0; i < root.options().length; i++){
for (int j = 0; j < root.options()[i].length; j++){
String s = root.options()[i][j];
System.out.print(s + " ");
if (s.trim().equalsIgnoreCase("-d")){
idx = i;
}
}
System.out.println("");
}
javaDocDir = idx != -1 && root.options()[idx].length == 2 ? root.options()[idx][1] : ".";
System.out.println("Using java doc dir: " + javaDocDir);
generateJavadoc(root);
// Set the options.
DiagramOptions options = new DiagramOptions();
options.set(root.options());
// Extract the Model.
Model model = new Model(root);
model.map();
// Print the model.
// ModelPrinter printer = new ModelPrinter(model);
// printer.generate();
// System.out.println("=======================================================================================");
// System.out.println(printer.toString());
// System.out.println("=======================================================================================");
// Generate the diagrams.
System.out.println("Using PlantUML version " + Version.versionString());
System.out.println("Generating diagrams...");
generateContextDiagrams(model, options);
generatePackageDiagrams(model, options);
generateOverviewDiagram(model, options);
return true;
}
/**
* Specify the language version.
* This is EXTREMELY important. It it is not set, none of the generic parameters
* are properly returned. Grrrr. Thanks Java.
* @return Version of the language.
*/
public static LanguageVersion languageVersion() {
return Standard.languageVersion();
}
public static int optionLength(String option) {
if (DiagramOptions.isValidOption(option)) {
return DiagramOptions.getOptionLength(option);
} else {
return Standard.optionLength(option);
}
}
public static boolean validOptions(String[][] options, DocErrorReporter reporter) {
// Iterate through all of the options, checking to see if an option is valid.
List standardOptions = new ArrayList<>();
for (String[] option: options) {
String name = option[0];
if (DiagramOptions.isValidOption(name)) {
String error = DiagramOptions.checkOption(option);
if (error != null && error.length() > 0) {
reporter.printError(error);
}
} else {
standardOptions.add(option);
}
}
return Standard.validOptions(standardOptions.toArray(new String[][]{}), reporter);
}
private static void generateJavadoc(RootDoc rootDoc) {
Standard.start(rootDoc);
}
private static void generateContextDiagrams(Model model, DiagramOptions options) {
for (ModelClass modelClass: model.classes()) {
if (modelClass.isInternal()) {
generateContextDiagram(model, modelClass, options);
}
}
}
private static void generateContextDiagram(Model model, ModelClass modelClass, DiagramOptions options) {
ContextDiagramPrinter generator = new ContextDiagramPrinter(model, modelClass, options);
generator.generate();
File file = createFile(modelClass.packageName(), modelClass.shortNameWithoutParameters(), "puml");
boolean success = generator.toFile(file);
if (success && executePlantUML(modelClass.packageName(), modelClass.shortNameWithoutParameters(), generator.stringBuilder())) {
if (updateHtml(
fileForName(modelClass.packageName()),
modelClass.shortNameWithoutParameters(),
Pattern.compile(".*(Class|Interface|Enum) " + modelClass.shortNameWithoutParameters() + ".*"))) {
System.out.println("Generated diagram for class " + modelClass.fullName());
} else {
System.out.println("ERROR: Could not update html page for class " + modelClass.fullName());
}
} else {
System.out.println("ERROR: Could not generate diagram for class " + modelClass.fullName());
}
}
private static void generatePackageDiagrams(Model model, DiagramOptions options) {
for (ModelPackage modelPackage: model.packages()) {
generatePackageDiagram(model, modelPackage, options);
}
}
private static void generatePackageDiagram(Model model, ModelPackage modelPackage, DiagramOptions options) {
PackageDiagramPrinter generator = new PackageDiagramPrinter(model, modelPackage, options);
generator.generate();
File file = createFile(modelPackage.fullName(), "package-summary", "puml");
boolean success = generator.toFile(file);
if (success && executePlantUML(modelPackage.fullName(), "package-summary", generator.stringBuilder())) {
if (updateHtml(
fileForName(modelPackage.fullName()),
"package-summary",
Pattern.compile("([Hh]2>)|("))) {
System.out.println("Generated overview diagram");
} else {
System.out.println("ERROR: Could not update html page for overview diagram");
}
} else {
System.out.println("ERROR: Could not generate overview diagram");
}
}
private static boolean executePlantUML(String name, String baseName, StringBuilder content) {
File file = createFile(name, baseName, "svg");
try {
OutputStream imageOutput = new BufferedOutputStream(new FileOutputStream(file));
SourceStringReader reader = new SourceStringReader(content.toString());
// http://plantuml.sourceforge.net/qa/?qa=4969/skinparam-svglinktarget-not-working-for-api
reader.generateImage(imageOutput, new FileFormatOption(FileFormat.SVG).withSvgLinkTarget("_parent"));
return true;
} catch (IOException e) {
//e.printStackTrace();
return false;
}
}
private static File createFile(String name, String baseName, String extension) {
try {
File dir = fileForName(name);
if (dir.exists() || dir.mkdirs()) {
File file = new File(dir, baseName + "." + extension);
if (file.exists() || file.createNewFile()) {
return file;
}
}
return null;
} catch (IOException e) {
//e.printStackTrace();
return null;
}
}
private static File fileForName(String name) {
File file = new File(javaDocDir);
for (String part : name.split("\\.")) {
if (part.trim().length() > 0) {
file = new File(file, part);
}
}
return file;
}
private static boolean updateHtml(File directory, String baseName, Pattern insertPointPattern) {
File htmlFile = new File(directory, baseName + ".html");
if (!htmlFile.exists()) {
System.out.println("ERROR: Could not find html file " + htmlFile.getName());
return false;
}
File svgFile = new File(directory, baseName + ".svg");
if (!svgFile.exists()) {
System.out.println("ERROR: Could not find svg file " + svgFile.getName());
return false;
}
File updatedHtml = new File(directory, baseName + ".uml");
boolean matched = false;
BufferedWriter writer = null;
BufferedReader reader = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(updatedHtml), "UTF-8"));
reader = new BufferedReader(new InputStreamReader(new FileInputStream(htmlFile), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
if (!matched && insertPointPattern.matcher(line).matches()) {
matched = true;
String tag = String.format(UML_DIV_TAG, baseName);
writer.newLine();
writer.write(tag);
writer.newLine();
}
}
} catch (IOException e) {
//e.printStackTrace();
return false;
} finally {
try {
if (writer != null)
writer.close();
if (reader != null)
reader.close();
} catch(IOException e) {
//e.printStackTrace();
return false;
}
}
// if altered, delete old file and rename new one to the old file name
if (matched) {
htmlFile.delete();
updatedHtml.renameTo(htmlFile);
return true;
} else {
System.out.println("ERROR: Could not insert diagram into HTML file " + htmlFile.getName());
htmlFile.delete();
return false;
}
}
// TODO Not specifying the width and height of diagrams means that they may be bigger than the page.
// However, if I specify a width and height, the diagrams do not resize if I zoom in and out.
// Need to investigate how to do this to keep the diagrams consistent and the browser happy.
private static final String UML_DIV_TAG =
"" +
"" +
"";
private static String javaDocDir;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy