/* This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. */ import java.io.*; import java.util.*; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import com.sun.javadoc.*; import com.sun.tools.javadoc.Main; /** * Java Doclet for generating .NET XML API documentation. *
* The current implementation may has not been tested
* with (and thus may not support) the following features:
* {@code}; should be converted to
* Other issues:
*
*
* HTML tag parsing is not forgiving; should be made more fault tolerant
*
* Javadoc HTML -> .NET tag conversions that should be considered/evaluated:
*
* NOTE: The Java Runtime API contains invalid HTML which requires this
* option to be set to to
* Usage reports "javadoc" instead of "ikvmdoc":
*
* usage: javadoc [options] [packagenames] [sourcefiles] [@files]
*
* should be:
*
* usage: ikvmdoc [options] [packagenames] [sourcefiles] [@files]
*
* true
-> false
-> null
-> true
to reflect the standard doclet behavior.
* false
when generating its
* .NET XML documentation.
*/
private static boolean OUTPUT_HTML = true;
/**
* Indicates if the author information should be included in the .NET XML documentation.
* Default is false
to reflect the standard doclet behavior.
*/
private static boolean OUTPUT_AUTHOR = false;
/**
* Indicates if the deprecated information should be included in the .NET XML documentation.
* Default is true
to reflect the standard doclet behavior.
*/
private static boolean OUTPUT_DEPRECATED = true;
/**
* Indicates if the since information should be included in the .NET XML documentation.
* Default is true
to reflect the standard doclet behavior.
*/
private static boolean OUTPUT_SINCE = true;
/**
* Indicates if the version information should be included in the .NET XML documentation.
* Default is false
to reflect the standard doclet behavior.
*/
private static boolean OUTPUT_VERSION = false;
/**
* The reported used to report failures to.
*/
private static DocErrorReporter ERROR_REPORTER;
static {
// Populate the Java->.NET data type mappings
DATA_TYPE_MAPPING.put("boolean", "System.Boolean");
DATA_TYPE_MAPPING.put("byte", "System.Byte");
DATA_TYPE_MAPPING.put("char", "System.Char");
DATA_TYPE_MAPPING.put("short", "System.Int16");
DATA_TYPE_MAPPING.put("int", "System.Int32");
DATA_TYPE_MAPPING.put("long", "System.Int64");
DATA_TYPE_MAPPING.put("float", "System.Single");
DATA_TYPE_MAPPING.put("double", "System.Double");
DATA_TYPE_MAPPING.put("java.lang.Object", "System.Object");
DATA_TYPE_MAPPING.put("java.lang.String", "System.String");
DATA_TYPE_MAPPING.put("java.lang.Throwable", "System.Exception");
}
/**
* Generate the .NET XML Documentation.
*
* @param root represents the root of the program structure information for one run of javadoc
* @return true
on success; false
on failure
*/
public static boolean start(RootDoc root) {
String assemblyName = ASSEMBLY_FILE.getName();
int extensionIndex = assemblyName.lastIndexOf('.');
if (extensionIndex != -1) {
assemblyName = assemblyName.substring(0, extensionIndex);
}
File documentationFile = new File(ASSEMBLY_FILE.getParent(), assemblyName + ".xml");
PrintWriter pw = null;
try {
FileOutputStream fos = new FileOutputStream(documentationFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
pw = new PrintWriter(bw);
// Write the header
pw.println("");
pw.println("true
if a "seealso" tag should be printed;
* false
if a "see" tag should be printed
*/
private static void printSeeTag(PrintWriter pw, ProgramElementDoc memberDoc, SeeTag seeTag, boolean asSeeAlso) {
String label = seeTag.label();
String text = seeTag.text();
boolean isAnchor = (text.startsWith(""));
int endIndex = -1;
ProgramElementDoc referencedMemberDoc = seeTag.referencedMember();
if (isAnchor) {
endIndex = text.indexOf('>') + 1;
if (endIndex == text.length()) {
ERROR_REPORTER.printError("Invalid anchor '" + text + "' for '" + memberDoc.qualifiedName() + "'");
printText(pw, text);
return;
}
} else {
// If the member reference is null, attempt to use the referenced class
if (referencedMemberDoc == null) {
referencedMemberDoc = seeTag.referencedClass();
}
if (referencedMemberDoc == null) {
ERROR_REPORTER.printError("Unable to locate reference '" + text + "' for '" + memberDoc.qualifiedName() + "'");
if (label == null || label.trim().length() == 0) {
printText(pw, text);
} else {
printText(pw, label);
}
return;
}
}
String type = (asSeeAlso) ? "seealso" : "see";
if (asSeeAlso) {
printIndent(pw, 3);
}
pw.print("<");
pw.print(type);
if (isAnchor) {
pw.print(text.substring(2, endIndex));
printText(pw, text.substring(endIndex, text.length() - 4));
} else {
pw.print(" cref=\"");
printReference(pw, referencedMemberDoc, true);
pw.print("\">");
if (label == null || label.trim().length() == 0) {
printReference(pw, referencedMemberDoc, false);
} else {
printText(pw, label);
}
}
pw.print("");
pw.print(type);
pw.print(">");
if (asSeeAlso) {
pw.println();
}
}
/**
* Prints the documentation for the specified tag.
*
* @param pw the writer to print the tag documentation to
* @param referenceDoc the member to document the tags for
* @param label the label to print for the tag documentation
* @param tagName the name of the tags to print the documentation for
*/
private static void printTags(PrintWriter pw, ProgramElementDoc referenceDoc, String label, String tagName) {
Tag[] tags = referenceDoc.tags(tagName);
for (Tag tag : tags) {
pw.print("true
if the type identifier should be included;
* false
if the type identifier should be omitted
*/
private static void printReference(PrintWriter pw, ProgramElementDoc referenceDoc, boolean includeType) {
ClassDoc classDoc = (referenceDoc.isClass() || referenceDoc.isInterface()) ? (ClassDoc) referenceDoc : referenceDoc.containingClass();
if (includeType) {
if (referenceDoc.isField()) {
if (referenceDoc.isFinal() && !classDoc.isInterface()) {
pw.print("P:");
} else {
pw.print("F:");
}
} else if (referenceDoc.isConstructor() || referenceDoc.isMethod()) {
pw.print("M:");
} else {
pw.print("T:");
}
}
pw.print(classDoc.qualifiedName());
if (referenceDoc.isField()) {
if (classDoc.isInterface()) {
pw.print(".__Fields.");
} else {
pw.print(".");
}
pw.print(referenceDoc.name());
} else if (referenceDoc.isConstructor()) {
pw.print(".#ctor");
printParameters(pw, (ConstructorDoc) referenceDoc);
} else if (referenceDoc.isMethod()){
pw.print(".");
pw.print(referenceDoc.name());
printParameters(pw, (MethodDoc) referenceDoc);
}
}
/**
* Prints comment tags.
*
* @param pw the writer to print the comment tags to
* @param memberDoc the member to print the comment tags for
* @param commentTags the comment tags to print
*/
private static void printComment(PrintWriter pw, ProgramElementDoc memberDoc, Tag[] commentTags) {
for (Tag tag : commentTags) {
if (tag instanceof SeeTag) {
SeeTag seeTag = (SeeTag) tag;
printSeeTag(pw, memberDoc, seeTag, false);
} else {
String text = tag.text();
printText(pw, memberDoc, text);
}
}
}
/**
* Prints the specified javadoc text in a .NET XML documentation format.
*
* @param pw the writer to print the comment tags to
* @param memberDoc the member to print the comment tags for
* @param text the text to print
*/
private static void printText(PrintWriter pw, ProgramElementDoc memberDoc, String text) {
char[] characters = text.toCharArray();
for (int index = 0; index < characters.length; index++) {
char character = characters[index];
switch (character) {
case '<':
int x = Character.offsetByCodePoints(text, 0, index);
if (x != index) {
System.out.println("x = " + x);
}
int endIndex = text.indexOf('>', index);
// Handle invalid HTML (use of "<" or "<>" in text)
if (endIndex == -1 || endIndex - index < 2) {
pw.print("<");
continue;
}
String tag = text.substring(index + 1, endIndex).trim().toLowerCase();
boolean isEndTag = false;
boolean isStandAloneTag = false;
if (tag.length() > 1) {
if (tag.startsWith("/")) {
tag = tag.substring(1);
isEndTag = true;
} else if (tag.endsWith("/")) {
tag = tag.substring(0, tag.length() - 1);
isStandAloneTag = true;
}
}
/*
* Process/convert HTML tags to .NET XML
*/
if ("p".equals(tag)) {
// Translate true
if the options are valid;
* false
if the options are invalid
*/
public static boolean validOptions(String[][] options, DocErrorReporter reporter) {
ERROR_REPORTER = reporter;
for (String[] option : options) {
if (ASSEMBLY_PARAMETER.equals(option[0])) {
ASSEMBLY_FILE = new File(option[1]);
if (!ASSEMBLY_FILE.isFile() || !ASSEMBLY_FILE.exists()) {
reporter.printError("The assembly file specified '" + ASSEMBLY_FILE.getAbsolutePath() + "' is invalid.");
return false;
}
} else if (HTML_PARAMETER.equals(option[0])) {
OUTPUT_HTML = false;
} else if (AUTHOR_PARAMETER.equals(option[0])) {
OUTPUT_AUTHOR = true;
} else if (DEPRECATED_PARAMETER.equals(option[0])) {
OUTPUT_DEPRECATED = false;
} else if (SINCE_PARAMETER.equals(option[0])) {
OUTPUT_SINCE = false;
} else if (VERSION_PARAMETER.equals(option[0])) {
OUTPUT_VERSION = true;
}
}
return true;
}
}