Skip to content
Snippets Groups Projects
Commit 78b758f9 authored by Jesper's avatar Jesper
Browse files

RagDoc Builder initial commit

This is a documentation generator tool for JastAdd projects.

The tool generates a bunch of JSON files describing the structure of the
generated JastAdd classes. The generated information can be viewed by RagDoc
Viewer.

This version has been in sporadic development since 2013.
parents
Branches
No related tags found
No related merge requests found
Showing
with 1798 additions and 0 deletions
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="jastaddj/src/frontend"/>
<classpathentry kind="src" path="src/gen">
<attributes>
<attribute name="ignore_optional_problems" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src/java"/>
<classpathentry kind="lib" path="jastaddj/tools/ant-1.9.1.jar"/>
<classpathentry kind="lib" path="lib/json-1.0.0.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="ant-bin"/>
</classpath>
* text=auto
/.idea/
/*.iml
/src/gen/
/src/tmp/
/bin
*.swp
/*.tree
/ant-bin
/testdoc
/.gradle
/*.jar
# Gradle
/build/
/sourcelist
/data/
[submodule "extendj"]
path = extendj
url = https://bitbucket.org/extendj/extendj
.project 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ragdoll2</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
RagDoc Builder
==============
This is a tool for building documentation metadata about JastAdd applications.
Run RD-Builder on the generated Java code of your JastAdd project as if
compiling the code. RD-Builder outputs a bunch of JSON files describing the
structure of the input classes. The generated documentation can be viewed with
RagDoc Viewer.
RagDoll depends on the following projects:
* `extendj`
* `se.llbit:jo-json`
Usage
-----
Use RagDoc Viewer to view the generated documentation.
buildscript {
repositories.mavenCentral()
repositories.mavenLocal()
dependencies {
classpath 'org.jastadd:jastaddgradle:1.10.6'
}
}
repositories {
maven { url uri('lib') }
mavenCentral()
mavenLocal()
}
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'jastadd'
project.version = '1.3.0'
project.group = 'org.extendj'
project.archivesBaseName = 'rd-builder'
sourceSets {
main {
java {
srcDir 'src/java'
srcDir 'extendj/src/frontend'
srcDir 'extendj/java8/src'
}
}
}
jastadd {
configureModuleBuild()
modules 'jastadd_modules'
module = 'rd-builder'
astPackage = 'org.extendj.ast'
genDir = 'src/gen'
parser.name = 'JavaParser'
parser.genDir = 'src/gen/org/extendj/parser'
scanner.genDir = 'src/gen/org/extendj/scanner'
}
mainClassName = 'org.extendj.ragdoc.RagDocBuilder'
jar.manifest.attributes 'Main-Class': mainClassName
jar.destinationDir = projectDir
// Sets the -source and -target flags for javac.
sourceCompatibility = '1.7'
targetCompatibility = '1.7'
dependencies {
compile 'se.llbit:jo-json:1.3.0'
}
jar {
baseName 'rd-builder'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
extendj @ 1829cf56
Subproject commit 1829cf5620db5d9d729f6aa16f109d7ad1b59e7a
include("extendj/jastadd_modules")
module "rd-builder", {
imports "java8 frontend"
java {
basedir "src/java/"
include "**/*.java"
}
jastadd {
basedir "src/jastadd/"
include "**/*.ast"
include "**/*.jadd"
include "**/*.jrag"
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Attributes for enumerating inherited methods and fields.
*/
aspect Inheritance {
/**
* Builds a map of method names to method declarations,
* excluding method names that are declared locally.
*/
syn Map<String, Collection<MethodDecl>> TypeDecl.inheritedMethodsNameMap() =
Collections.emptyMap();
eq ClassDecl.inheritedMethodsNameMap() {
Map<String, Collection<MethodDecl>> result = new HashMap<>();
if (hasSuperclass()) {
for (SimpleSet<MethodDecl> supertypeMethods : superclass().methodsSignatureMap().values()) {
for (MethodDecl method : supertypeMethods) {
if (!method.isPrivate()) {
Collection<MethodDecl> methods = result.get(method.name());
if (methods == null) {
methods = new ArrayList<>();
result.put(method.name(), methods);
}
methods.add(method);
}
}
}
}
for (SimpleSet<MethodDecl> supertypeMethods : interfacesMethodsSignatureMap().values()) {
for (MethodDecl method : supertypeMethods) {
Collection<MethodDecl> methods = result.get(method.name());
if (methods == null) {
methods = new ArrayList<>();
result.put(method.name(), methods);
}
methods.add(method);
}
}
for (SimpleSet<MethodDecl> localMethods : localMethodsSignatureMap().values()) {
if (!localMethods.isEmpty()) {
result.remove(localMethods.iterator().next().name());
}
}
return result;
}
eq InterfaceDecl.inheritedMethodsNameMap() {
Map<String, Collection<MethodDecl>> result = new HashMap<>();
for (Iterator<TypeDecl> outerIter = interfacesIterator(); outerIter.hasNext(); ) {
TypeDecl typeDecl = outerIter.next();
for (SimpleSet<MethodDecl> supertypeMethods : typeDecl.methodsSignatureMap().values()) {
for (MethodDecl method : supertypeMethods) {
if (!method.isPrivate()) {
Collection<MethodDecl> methods = result.get(method.name());
if (methods == null) {
methods = new ArrayList<>();
result.put(method.name(), methods);
}
methods.add(method);
}
}
}
}
for (SimpleSet<MethodDecl> localMethods : localMethodsSignatureMap().values()) {
if (!localMethods.isEmpty()) {
result.remove(localMethods.iterator().next().name());
}
}
return result;
}
syn MethodDecl MethodDecl.original() = this;
eq MethodDeclSubstituted.original() = getOriginal();
eq GenericMethodDeclSubstituted.original() = getOriginal();
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import se.llbit.json.*;
import org.extendj.ragdoc.JsonBuilder;
aspect BodyDeclModifiers {
syn boolean BodyDecl.isPrivate() = false;
eq MemberClassDecl.isPrivate() = getClassDecl().getModifiers().isPrivate();
eq MemberInterfaceDecl.isPrivate() = getInterfaceDecl().getModifiers().isPrivate();
}
aspect Initializers {
syn boolean BodyDecl.isInitializer() = false;
eq InstanceInitializer.isInitializer() = true;
eq StaticInitializer.isInitializer() = true;
}
aspect AspectJson {
//TODO collect aspect information
}
aspect ToJson {
syn String TypeDecl.objectKind() {
JsonObject doc = jsonDocObject();
if (doc != null && doc.get("ast").stringValue("").equals("node")) {
return "ast-class";
} else {
return "class";
}
}
eq InterfaceDecl.objectKind() = "interface";
eq EnumDecl.objectKind() = "enum";
syn JsonValue ASTNode.toJson() {
JsonObject obj = new JsonObject();
obj.add("kind", new JsonString("unknown"));
obj.add("astnode", new JsonString(getClass().getName()));
return obj;
}
eq TypeAccess.toJson() = new JsonString(type().typeName());
eq ParTypeAccess.toJson() = new JsonString(type().typeName());
eq TypeDecl.toJson() {
JsonObject obj = new JsonObject();
obj.add("kind", new JsonString(objectKind()));
obj.add("name", new JsonString(name()));
obj.add("pkg", new JsonString(packageName()));
JsonArray modifiers = getModifiers().toJson();
if (!modifiers.isEmpty()) {
obj.add("mods", modifiers);
}
JsonObject doc = jsonDocObject();
if (doc != null) {
obj.add("doc", doc);
}
JsonArray members = new JsonArray();
for (BodyDecl bd: getBodyDeclList()) {
if (!bd.isPrivate() && !bd.isInitializer()) {
bd.addMemberJson(members);
}
}
if (!members.isEmpty()) {
obj.add("members", members);
}
return obj;
}
syn String BodyDecl.objectKind() = "none";
eq ConstructorDecl.objectKind() = "constr";
eq MethodDecl.objectKind() {
JsonObject doc = jsonDocObject();
if (doc != null && !doc.get("attribute").stringValue("").isEmpty()) {
return "attr";
} else {
return "method";
}
}
eq FieldDecl.objectKind() = "field";
/**
* Add body declaration JSON contributions to the members array.
*/
public void BodyDecl.addMemberJson(JsonArray members) {
}
public void ConstructorDecl.addMemberJson(JsonArray members) {
jsonBuilder.addConstructor(this, members);
}
public void MethodDecl.addMemberJson(JsonArray members) {
jsonBuilder.addMethod(this, members);
}
public void FieldDecl.addMemberJson(JsonArray members) {
jsonBuilder.addField(this, members);
}
eq ClassDecl.toJson() {
JsonObject obj = super.toJson().object();
if (hasSuperClass()) {
obj.add("extends", getSuperClass().toJson());
}
return obj;
}
public static JsonBuilder ASTNode.jsonBuilder;
// TODO make this not global singleton!
public static Set<String> ASTNode.aspects = new HashSet<String>();
public JsonObject TypeDecl.jsonDocObject() {
return jsonBuilder.jsonDocObject(docComment);
}
public JsonObject BodyDecl.jsonDocObject() {
return jsonBuilder.jsonDocObject(docComment);
}
syn JsonArray Modifiers.toJson() {
JsonArray array = new JsonArray();
for (Modifier modifier: getModifierList()) {
// TODO document annotations!
if (!(modifier instanceof Annotation)) {
array.add(modifier.toJson());
}
}
return array;
}
eq Modifier.toJson() = new JsonString(getID());
eq Annotation.toJson() = new JsonString(prettyPrint());
eq ParameterDeclaration.toJson() {
JsonObject obj = new JsonObject();
obj.add("type", new JsonString(type().typeName()));
obj.add("name", new JsonString(name()));
return obj;
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
aspect Typenames {
/** Qualified type name without type arguments or array subscripts. */
syn String TypeDecl.baseTypeName() = typeName();
syn String ArrayDecl.baseTypeName() = componentType().baseTypeName();
eq ParTypeDecl.baseTypeName() = super.typeName();
syn String TypeDecl.nameWithTypeArgs() = name();
syn Collection<TypeDecl> TypeDecl.typeArgs() =
Collections.emptyList();
eq ParTypeDecl.typeArgs() {
java.util.List<TypeDecl> args = getParameterization().args;
Collection<TypeDecl> result = new ArrayList<>();
for (TypeDecl arg : args) {
result.add(arg);
}
return result;
}
syn String GenericClassDecl.nameWithTypeArgs() {
StringBuilder sb = new StringBuilder();
sb.append(name());
sb.append("<");
for (int i = 0; i < getNumTypeParameter(); i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(getTypeParameter(i).typeName());
}
sb.append(">");
return sb.toString();
}
syn String GenericInterfaceDecl.nameWithTypeArgs() {
StringBuilder sb = new StringBuilder();
sb.append(name());
sb.append("<");
for (int i = 0; i < getNumTypeParameter(); i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(getTypeParameter(i).typeName());
}
sb.append(">");
return sb.toString();
}
syn String ParTypeDecl.nameWithTypeArgs() {
java.util.List<TypeDecl> args = getParameterization().args;
StringBuilder sb = new StringBuilder();
sb.append(name());
sb.append("<");
for (int i = 0; i < args.size(); i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(args.get(i).nameWithTypeArgs());
}
sb.append(">");
return sb.toString();
}
eq RawClassDecl.nameWithTypeArgs() = name();
eq RawInterfaceDecl.nameWithTypeArgs() = name();
syn String TypeDecl.nameWithEnclosingType() {
TypeDecl enclosing = enclosingType();
if (enclosing != null) {
return String.format("%s.%s", enclosing.nameWithEnclosingType(), name());
} else {
return name();
}
}
coll HashSet<TypeDecl> CompilationUnit.localTypes() root CompilationUnit;
TypeDecl contributes this to CompilationUnit.localTypes();
// Don't search for local types inside a constructor declaration.
ConstructorDecl contributes { }
to CompilationUnit.localTypes();
// Don't search for local types inside a method declaration.
MethodDecl contributes { }
to CompilationUnit.localTypes();
// Don't search for local types inside an initializer.
InstanceInitializer contributes { }
to CompilationUnit.localTypes();
// Don't search for local types inside an initializer.
StaticInitializer contributes { }
to CompilationUnit.localTypes();
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.ragdoc;
public class DocTag extends Object {
public final String tag;
public final String text;
public DocTag(String content) {
int sep = content.indexOf(' ');
if (sep == -1) {
tag = content;
text = "";
} else {
tag = content.substring(0, sep);
text = content.substring(sep + 1);
}
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.ragdoc;
interface InlineTagExpander {
/**
* Expand the inline JavaDoc tag with the given text.
* @param text text of the tag
*/
String expand(String tag, String text);
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.ragdoc;
import java.util.Collection;
import java.util.LinkedList;
public class JavaDocParser extends Object {
static final InlineTagExpander expander = new InlineTagExpander() {
@Override public String expand(String tag, String text) {
if (tag.equals("code")) {
return "<code>" + text + "</code>";
} else if (tag.equals("link")) {
return "<a href=\"" + classDocURL(text) + "\">" + text + "</a>";
} else {
return text;
}
}
private String classDocURL(String klass) {
String result = klass.replaceAll("\\.", "/");
return "#" + result;
}
};
private int i;
private char[] doc;
private Collection<DocTag> tags = new LinkedList<DocTag>();
/**
* @return normalized documentation comment. All line endings replaced by \\n.
*/
public String parse(String comments) {
if (comments == null) {
return "";
}
int start = 0;
int end = -2;
while (true) {
int newStart = comments.indexOf("/**", end + 2);
if (newStart == -1) {
break;
}
int newEnd = comments.indexOf("*/", newStart + 3);
if (newEnd == -1) {
break;
}
start = newStart;
end = newEnd;
}
if (end <= start + 3) {
return "";
}
doc = comments.substring(start + 3, end).toCharArray();
StringBuilder text = new StringBuilder(doc.length);
i = 0;
boolean first = true;
while (i < doc.length) {
if (skipNewline()) {
skipWhitespace();
if (skipAsterisk()) {
if (!first) {
text.append('\n');
}
first = false;
skipWhitespace();
}
} else {
text.append(doc[i++]);
}
}
String expanded = expandInlineTags(text.toString());
String content = extractContent(expanded);
return content;
}
public Collection<DocTag> getTags() {
return tags;
}
private static boolean isKnownTag(String tag) {
return
/* JastAdd tags: */
tag.equals("declaredat") || tag.equals("production") ||
tag.equals("astdecl") ||
tag.equals("ast") || tag.equals("aspect") ||
tag.equals("apilevel") || tag.equals("attribute") ||
/* JavaDoc tags:
* (http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags)
*/
tag.equals("author") || tag.equals("deprecated") ||
tag.equals("exception") || tag.equals("param") ||
tag.equals("return") || tag.equals("see") ||
tag.equals("serial") || tag.equals("serialData") ||
tag.equals("serialField") || tag.equals("since") ||
tag.equals("throws") || tag.equals("version");
}
private static boolean ignoreTag(String tag) {
return tag.equals("production");
}
/**
* Separate content and doc tags.
*/
private String extractContent(String javadoc) {
this.doc = javadoc.toCharArray();
StringBuilder content = new StringBuilder(doc.length);
i = 0;
while (i < doc.length) {
int rewind = i;
DocTag tag = parseTag();
if (tag != null && isKnownTag(tag.tag)) {
if (!ignoreTag(tag.tag)) {
tags.add(tag);
}
} else {
if (tag != null) {
i = rewind;
}
String html = parseHtmlTag();
if (!html.isEmpty()) {
content.append(html);
} else {
content.append(doc[i++]);
}
}
}
return content.toString();
}
private String parseHtmlTag() {
// TODO handle string literals!
if (doc[i] != '<') {
return "";
}
int j = i + 1;
StringBuilder text = new StringBuilder();
text.append('<');
while (j < doc.length) {
char c = doc[j++];
text.append(c);
if (c == '>') {
i = j;
return text.toString();
}
}
return "";
}
private DocTag parseTag() {
if (doc[i] != '@') {
return null;
}
i += 1;
StringBuilder text = new StringBuilder();
while (i < doc.length) {
char c = doc[i];
if (c == '@') {
return new DocTag(text.toString().trim());
} else {
String html = parseHtmlTag();
if (!html.isEmpty()) {
text.append(html);
} else {
text.append(c);
i += 1;
}
}
}
return new DocTag(text.toString().trim());
}
private String expandInlineTags(String javadoc) {
this.doc = javadoc.toCharArray();
StringBuilder text = new StringBuilder(doc.length);
i = 0;
while (i < doc.length) {
String inlineTag = parseInlineTag();
if (!inlineTag.isEmpty()) {
text.append(expandInlineTag(inlineTag, expander));
} else {
text.append(doc[i++]);
}
}
return text.toString();
}
private String expandInlineTag(String inlineTag, InlineTagExpander expander) {
String tag, text;
int end = inlineTag.indexOf(' ');
if (end == -1) {
tag = inlineTag.substring(0);
text = "";
} else {
tag = inlineTag.substring(0, end);
text = inlineTag.substring(end + 1);
}
return expander.expand(tag, text);
}
private String parseInlineTag() {
if (doc[i] != '{' || doc[i + 1] != '@') {
// no opening bracket
return "";
}
int j = i + 2;
StringBuilder text = new StringBuilder();
while (j < doc.length) {
char c = doc[j++];
if (c == '}') {
i = j;
return text.toString();
} else {
text.append(c);
}
}
// EOF before closing bracket
return "";
}
private boolean skipNewline() {
if (doc[i] == '\r') {
i += 1;
if (i < doc.length && doc[i] == '\n') {
i += 1;
}
return true;
} else if (doc[i] == '\n') {
i += 1;
return true;
}
return false;
}
private void skipWhitespace() {
while (i < doc.length && isWhitespace(doc[i])) {
i += 1;
}
}
private boolean isWhitespace(char c) {
return Character.isWhitespace(c) && c != '\n' && c != '\n';
}
private boolean skipAsterisk() {
if (i < doc.length && doc[i] == '*') {
i += 1;
return true;
}
return false;
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.ragdoc;
import org.extendj.ast.Access;
import org.extendj.ast.BodyDecl;
import org.extendj.ast.ClassDecl;
import org.extendj.ast.CompilationUnit;
import org.extendj.ast.ConstructorDecl;
import org.extendj.ast.Declarator;
import org.extendj.ast.FieldDecl;
import org.extendj.ast.InterfaceDecl;
import org.extendj.ast.MethodDecl;
import org.extendj.ast.ParameterDeclaration;
import org.extendj.ast.TypeDecl;
import org.extendj.ast.Variable;
import org.extendj.util.RelativePath;
import se.llbit.json.Json;
import se.llbit.json.JsonArray;
import se.llbit.json.JsonObject;
import se.llbit.json.JsonString;
import se.llbit.json.JsonValue;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JsonBuilder {
private Map<String, String> typeIndex = new HashMap<>();
protected Set<String> typenames = new HashSet<>();
protected Map<TypeDecl, JsonObject> typemap = new HashMap<>();
private Set<String> aspects = new HashSet<>();
final Map<String, JsonArray> packages = new HashMap<>();
final Map<String, Collection<TypeDecl>> packageTypeMap = new HashMap<>();
/** Ordering of the package object kinds. */
private static final String[] TYPE_KINDS = { "ast-class", "interface", "class" };
private static final String[] MEMBER_KINDS = { "constr", "attr", "field", "method" };
private final File rootDir;
private java.util.List<String> ragRoot = new java.util.LinkedList<>();
public Map<String, File> sourceFiles = new HashMap<>();
public JsonBuilder(File rootDir) {
this.rootDir = rootDir;
ragRoot = RelativePath.buildPathList(rootDir);
typenames.add("packages"); // Reserve packages as JSON filename.
}
public void addConstructor(ConstructorDecl constr, JsonArray members) {
JsonObject doc = constr.jsonDocObject();
if (shouldDocument(constr, doc)) {
JsonObject obj = new JsonObject();
obj.add("name", new JsonString(constr.name()));
JsonArray modifiers = constr.getModifiers().toJson();
if (!modifiers.isEmpty()) {
obj.add("mods", modifiers);
}
if (doc != null) {
obj.add("doc", doc);
}
addParameters(obj, constr.getParameterList());
addExceptions(obj, constr.getExceptionList());
members.add(obj);
}
}
private void addParameters(JsonObject obj, Iterable<ParameterDeclaration> params) {
Iterator<ParameterDeclaration> iter = params.iterator();
if (iter.hasNext()) {
JsonArray array = new JsonArray();
do {
ParameterDeclaration param = iter.next();
JsonObject pobj = new JsonObject();
pobj.add("t", typeRef(param.type()));
pobj.add("n", param.name());
array.add(pobj);
} while (iter.hasNext());
obj.add("params", array);
}
}
private void addExceptions(JsonObject obj, Iterable<Access> params) {
Iterator<Access> iter = params.iterator();
if (iter.hasNext()) {
JsonArray array = new JsonArray();
do {
array.add(typeRef(iter.next().type()));
} while (iter.hasNext());
obj.add("throws", array);
}
}
public void addMethod(MethodDecl method, JsonArray members) {
JsonObject doc = method.jsonDocObject();
if (shouldDocument(method, doc)) {
JsonObject obj = new JsonObject();
obj.add("name", new JsonString(method.name()));
JsonArray modifiers = method.getModifiers().toJson();
if (!modifiers.isEmpty()) {
obj.add("mods", modifiers);
}
if (doc != null) {
obj.add("doc", doc);
}
obj.add("type", typeRef(method.type()));
addParameters(obj, method.getParameterList());
addExceptions(obj, method.getExceptionList());
members.add(obj);
}
}
private JsonValue typeRef(TypeDecl type) {
// TODO: handle wildcard types.
JsonObject obj = new JsonObject();
if (shouldDocument(type)) {
// This is a user type.
obj.add("u", type.name());
String id = typeId(type);
if (!id.equals("%")) {
obj.add("i", typeId(type));
}
} else {
// This is a built-in or library type.
obj.add("n", type.name());
}
// Add type arguments (if any).
Collection<TypeDecl> typeArgs = type.typeArgs();
JsonArray args = new JsonArray();
for (TypeDecl arg : typeArgs) {
args.add(typeRef(arg));
}
if (!args.isEmpty()) {
obj.add("a", args);
}
return obj;
}
public static boolean shouldDocument(FieldDecl field) {
return shouldDocument(field, field.jsonDocObject());
}
public static boolean shouldDocument(BodyDecl member, JsonObject doc) {
return doc == null || !doc.get("apilevel").stringValue("").equals("internal");
}
public static boolean isHighLevelApi(BodyDecl member, JsonObject doc) {
if (doc == null) {
return true;
}
String apilevel = doc.get("apilevel").stringValue("");
return !(apilevel.equals("internal") || apilevel.equals("low-level"));
}
/**
* Determines if documentation should be generated for a given type.
*
* <p>Documentation should be generated if the type is user-defined
* and non-private.
*/
public static boolean shouldDocument(TypeDecl typeDecl) {
// TODO: only non-private!
return !typeDecl.isPrivate()
&& !typeDecl.isAnonymous()
&& (!typeDecl.isArrayDecl() || shouldDocument(typeDecl.elementType()))
&& !typeDecl.isTypeVariable() && typeDecl.compilationUnit().fromSource();
}
// TODO: inner class names.
String typeId(TypeDecl type) {
String typename = type.baseTypeName();
if (typeIndex.containsKey(typename)) {
return typeIndex.get(typename);
} else {
// This handles name clashes by appending a unique index.
int i = 1;
String simpleName = type.name();
while (true) {
String id = (i == 1) ? simpleName : simpleName + i;
if (!typenames.contains(id)) {
typenames.add(id);
break;
}
i += 1;
}
String id = (i == 1) ? "%" : "%" + i;
typeIndex.put(typename, id);
return id;
}
}
public void addField(FieldDecl field, JsonArray members) {
JsonObject doc = field.jsonDocObject();
if (shouldDocument(field, doc)) {
for (Declarator declarator : field.getDeclaratorList()) {
JsonObject obj = new JsonObject();
obj.add("name", new JsonString(declarator.name()));
obj.add("type", typeRef(declarator.type()));
JsonArray modifiers = field.getModifiers().toJson();
if (!modifiers.isEmpty()) {
obj.add("mods", modifiers);
}
doc = field.jsonDocObject();
if (doc != null) {
obj.add("doc", doc);
}
members.add(obj);
}
}
}
public JsonObject jsonDocObject(String docComment) {
if (!docComment.isEmpty()) {
JavaDocParser parser = new JavaDocParser();
String javadoc = parser.parse(docComment);
JsonObject doc = new JsonObject();
String aspectName = null;
JsonArray params = new JsonArray();
for (DocTag tag : parser.getTags()) {
switch (tag.tag) {
case "declaredat":
String declaredat = tag.text;
int sep = declaredat.lastIndexOf(':');
String ragFile;
String lineno;
if (sep != -1) {
ragFile = declaredat.substring(0, sep);
lineno = declaredat.substring(sep + 1);
} else {
ragFile = declaredat;
lineno = "0";
}
if (!ragFile.equals("ASTNode")) {
String relativePath = RelativePath.getRelativePath(ragFile, ragRoot);
if (!sourceFiles.containsKey(relativePath)) {
File file = new File(rootDir, relativePath);
if (file.isFile()) {
sourceFiles.put(relativePath, file);
}
}
doc.add("ragFile", Json.of(relativePath));
} else {
// Not a real source file.
doc.add("ragFile", new JsonString(ragFile));
}
doc.add("line", new JsonString(lineno));
break;
case "aspect":
aspectName = tag.text;
doc.add("aspect", new JsonString(aspectName));
break;
case "param":
params.add(tag.text);
break;
default:
doc.add(tag.tag, new JsonString(tag.text));
break;
}
}
if (!params.isEmpty()) {
doc.add("params", params);
}
doc.add("description", new JsonString(javadoc));
if (aspectName != null) {
aspects.add(aspectName);
}
return doc;
}
return null;
}
public void addCompilationUnit(CompilationUnit unit) {
String packageName = unit.packageName();
JsonArray types = packages.get(packageName);
Collection<TypeDecl> packageTypes = packageTypeMap.get(packageName);
if (types == null) {
types = new JsonArray();
packages.put(packageName, types);
packageTypes = new ArrayList<>();
packageTypeMap.put(packageName, packageTypes);
}
// Iterate all local types:
for (TypeDecl type : unit.localTypes()) {
if (JsonBuilder.shouldDocument(type)) {
JsonObject typeJson = typeJson(type);
typeJson.add("id", new JsonString(typeId(type)));
TypeDecl enclosing = type.enclosingType();
if (enclosing != null) {
typeJson.add("enclosing", typeRef(enclosing));
}
types.add(typeJson);
packageTypes.add(type);
typemap.put(type, typeJson);
}
}
}
private JsonObject typeJson(TypeDecl type) {
JsonObject obj = new JsonObject();
obj.add("kind", new JsonString(type.objectKind()));
obj.add("name", new JsonString(type.nameWithTypeArgs()));
obj.add("pkg", new JsonString(type.packageName()));
if (type instanceof ClassDecl) {
ClassDecl klass = (ClassDecl) type;
if (klass.hasSuperclass()) {
obj.add("superclass", typeRef(klass.superclass()));
}
JsonArray ifaces = new JsonArray();
for (Access access: klass.getImplementsList()) {
ifaces.add(typeRef(access.type()));
}
if (!ifaces.isEmpty()) {
obj.add("superinterfaces", ifaces);
}
} else if (type instanceof InterfaceDecl) {
InterfaceDecl iface = (InterfaceDecl) type;
JsonArray ifaces = new JsonArray();
for (Access access: iface.getSuperInterfaceList()) {
ifaces.add(typeRef(access.type()));
}
if (!ifaces.isEmpty()) {
obj.add("superinterfaces", ifaces);
}
}
JsonArray modifiers = type.getModifiers().toJson();
if (!modifiers.isEmpty()) {
obj.add("mods", modifiers);
}
JsonObject doc = type.jsonDocObject();
if (doc != null) {
obj.add("doc", doc);
}
addInheritedMembers(type, obj);
Map<String, JsonArray> groupMap = new HashMap<>();
for (BodyDecl bd: type.getBodyDeclList()) {
if (!bd.isPrivate() && !bd.isInitializer()) {
String kind = bd.objectKind();
JsonArray members = groupMap.get(kind);
if (members == null) {
members = new JsonArray();
groupMap.put(kind, members);
}
bd.addMemberJson(members);
}
}
JsonArray groups = groupify(groupMap, MEMBER_KINDS);
if (!groups.isEmpty()) {
obj.add("groups", groups);
}
return obj;
}
/**
* This adds inherited members from superclasses.
*/
private void addInheritedMembers(TypeDecl type, JsonObject obj) {
if (type instanceof ClassDecl) {
JsonArray inheritedMethods = new JsonArray();
JsonArray inheritedAttributes = new JsonArray();
JsonArray inheritedFields = new JsonArray();
// The locally declared set of methods.
Set<String> declaredMethods = new HashSet<>();
Set<String> declaredFields = new HashSet<>();
declaredMethods.addAll(type.localMethodsSignatureMap().keySet());
ClassDecl klass = (ClassDecl) type;
while (klass.hasSuperclass()) {
ClassDecl superclass = (ClassDecl) klass.superclass();
JsonArray methodArray = new JsonArray();
JsonArray attributeArray = new JsonArray();
// Set to keep track of which method names have already been listed for
// the current type.
Set<String> locallyDeclared = new HashSet<>();
for (Iterable<MethodDecl> methods : superclass.localMethodsSignatureMap().values()) {
for (MethodDecl method : methods) {
MethodDecl original = method.original();
if (!declaredMethods.contains(original.signature())
&& !locallyDeclared.contains(original.name())
&& isHighLevelApi(original, original.jsonDocObject())) {
declaredMethods.add(original.signature());
locallyDeclared.add(original.name());
if (isAttribute(original)) {
// Add to attributes inherited from the current superclass.
attributeArray.add(original.name());
} else {
// Add to methods inherited from the current superclass.
methodArray.add(original.name());
}
}
}
}
JsonArray fieldArray = new JsonArray();
for (Iterable<Variable> vars : superclass.localFieldsMap().values()) {
for (Variable var : vars) {
if (!declaredFields.contains(var.name())
&& !var.isPrivate()
&& shouldDocument(var.fieldDecl())) {
declaredFields.add(var.name());
fieldArray.add(var.name());
}
}
}
if (!methodArray.isEmpty()) {
JsonObject inherited = new JsonObject();
inherited.add("superclass", typeRef(superclass));
inherited.add("members", methodArray);
inheritedMethods.add(inherited);
}
if (!attributeArray.isEmpty()) {
JsonObject inherited = new JsonObject();
inherited.add("superclass", typeRef(superclass));
inherited.add("members", attributeArray);
inheritedAttributes.add(inherited);
}
if (!fieldArray.isEmpty()) {
JsonObject inherited = new JsonObject();
inherited.add("superclass", typeRef(superclass));
inherited.add("members", fieldArray);
inheritedFields.add(inherited);
}
klass = superclass;
}
if (!inheritedMethods.isEmpty()) {
obj.add("inherited_methods", inheritedMethods);
}
if (!inheritedAttributes.isEmpty()) {
obj.add("inherited_attributes", inheritedAttributes);
}
if (!inheritedFields.isEmpty()) {
obj.add("inherited_fields", inheritedFields);
}
}
}
/**
* @return {@code true} if the argument is an attribute,
* {@code false} if it is an ordinary method.
*/
private static boolean isAttribute(MethodDecl method) {
JsonObject doc = method.jsonDocObject();
return !(doc == null || doc.get("attribute").stringValue("").isEmpty());
}
private static JsonArray groupify(Map<String, JsonArray> groupMap, String[] kinds) {
JsonArray groups = new JsonArray();
for (String kind : kinds) {
if (groupMap.containsKey(kind)) {
JsonObject group = new JsonObject();
group.add("kind", new JsonString(kind));
JsonArray members = groupMap.get(kind);
sortArrayBy(members, "name");
group.add("members", members);
groups.add(group);
}
}
return groups;
}
public JsonArray packageIndex() {
// TODO: split into separate arrays based on object kind.
JsonArray packageIndex = new JsonArray();
for (String packageName : packages.keySet()) {
// Group members by kind.
Map<String, JsonArray> groupMap = new HashMap<>();
for (TypeDecl type : packageTypeMap.get(packageName)) {
JsonObject memberEntry = new JsonObject();
String kind = type.objectKind();
JsonArray members = groupMap.get(kind);
if (members == null) {
members = new JsonArray();
groupMap.put(kind, members);
}
memberEntry.add("name", type.nameWithEnclosingType());
memberEntry.add("id", type.name() + typeId(type).substring(1));
members.add(memberEntry);
}
JsonObject entry = new JsonObject();
entry.add("name", new JsonString(packageName));
JsonArray groups = groupify(groupMap, TYPE_KINDS);
entry.add("groups", groups);
packageIndex.add(entry);
}
sortArrayBy(packageIndex, "name");
return packageIndex;
}
/**
* Sort object elements of JSON array by a particular key.
*/
static void sortArrayBy(JsonArray array, String key) {
Map<String, JsonObject> members = new HashMap<>();
for (JsonValue value : array) {
JsonObject member = value.object();
members.put(member.get(key).stringValue(""), member);
}
List<String> names = new ArrayList<>(members.keySet());
Collections.sort(names);
int index = 0;
for (String name : names) {
JsonObject member = members.get(name);
array.set(index, member);
index += 1;
}
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.ragdoc;
import org.extendj.ast.ASTNode;
import org.extendj.ast.BytecodeReader;
import org.extendj.ast.CompilationUnit;
import org.extendj.ast.Frontend;
import org.extendj.ast.JavaParser;
import org.extendj.ast.Problem;
import org.extendj.ast.Program;
import org.extendj.ast.TypeDecl;
import se.llbit.json.JsonObject;
import se.llbit.json.JsonValue;
import se.llbit.json.PrettyPrinter;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.Map;
/**
* Generate API documentation for a JastAdd project.
*
* @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
*/
public class RagDocBuilder extends Frontend {
/** Generate pretty-printed JSON if in debug mode, otherwise minified JSON is generated. */
private static final boolean DEBUG = false;
private JsonBuilder jsonBuilder;
public static void main(String args[]) {
RagDocBuilder rd = new RagDocBuilder();
int result = rd.compile(args);
if (result != EXIT_SUCCESS) {
System.exit(result);
}
}
/**
* @return {@code true} if documentation generation succeeds
*/
public int compile(String[] args) {
// TODO use command-line arg for ragRoot!
// TODO add package exclude argument.
File userDir = new File(System.getProperty("user.dir"));
if (userDir.isDirectory()) {
jsonBuilder = new JsonBuilder(userDir);
} else {
jsonBuilder = new JsonBuilder(new File("."));
}
ASTNode.jsonBuilder = jsonBuilder;
return run(args, Program.defaultBytecodeReader(), Program.defaultJavaParser());
}
@Override public int run(String[] args, BytecodeReader reader, JavaParser parser) {
System.out.println("Analyzing source tree...");
long start = System.currentTimeMillis();
int result = super.run(args, reader, parser);
long time = System.currentTimeMillis() - start;
System.out.format("Analysis took %fs%n", time / 1000.0);
try {
File outputDir;
if (program.options().hasValueForOption("-d")) {
outputDir = new File(program.options().getValueForOption("-d"));
} else {
outputDir = new File("doc");
}
if (!outputDir.isDirectory()) {
outputDir.mkdir();
}
if (!outputDir.isDirectory()) {
System.err.format("Error: not a valid output directory: '%s'\n", outputDir.getName());
return EXIT_CONFIG_ERROR;
}
System.out.println("Writing package summary.");
outputJson(outputDir, "packages.json", jsonBuilder.packageIndex());
System.out.println("Writing classes...");
for (Map.Entry<TypeDecl, JsonObject> entry : jsonBuilder.typemap.entrySet()) {
TypeDecl type = entry.getKey();
JsonObject typeJson = entry.getValue();
JsonBuilder.sortArrayBy(typeJson.get("members").array(), "name");
String fileName = type.name() + jsonBuilder.typeId(type).substring(1) + ".json";
outputJson(outputDir, fileName, typeJson);
}
System.out.println("Writing source files...");
for (Map.Entry<String, File> entry : jsonBuilder.sourceFiles.entrySet()) {
File file = entry.getValue();
String path = entry.getKey();
path = path.replace('/', '_').replace('\\', '_');
copyFile(outputDir, path, file);
}
} catch (IOException e) {
e.printStackTrace();
return EXIT_ERROR;
}
return result;
}
private void copyFile(File outputDir, String path, File file) throws IOException {
Files.copy(file.toPath(), new File(outputDir, path).toPath(),
StandardCopyOption.REPLACE_EXISTING);
}
private void outputJson(File dir, String fileName, JsonValue value) throws IOException {
File outputFile = new File(dir, fileName);
PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
JsonObject data = new JsonObject();
data.set("data", value);
if (DEBUG) {
PrettyPrinter pp = new PrettyPrinter(" ", out);
data.prettyPrint(pp);
} else {
out.print(data.toCompactString());
}
out.close();
}
@Override protected int processCompilationUnit(CompilationUnit unit) {
if (unit.fromSource()) {
try {
Collection<Problem> parseErrors = unit.parseErrors();
if (!parseErrors.isEmpty()) {
processErrors(parseErrors, unit);
return EXIT_ERROR;
} else {
jsonBuilder.addCompilationUnit(unit);
}
} catch (Throwable t) {
System.err.println("Errors:");
System.err.println("Fatal exception while processing " +
unit.pathName() + ":");
t.printStackTrace(System.err);
return EXIT_ERROR;
}
}
return EXIT_SUCCESS;
}
@Override protected String name() {
return "RagDoll";
}
@Override protected String version() {
return "2.0.0 SNAPSHOT";
}
@Override protected void initOptions() {
super.initOptions();
}
}
/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.extendj.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Utility class for calculating relative file paths.
*
* @author Jesper Öqvist <jesper.oqvist@cs.lth.se>
*
*/
public class RelativePath {
private static final String SEPARATOR = "/";
private static final List<String> cwd;
static {
File dir = new File(System.getProperty("user.dir"));
if (dir.isDirectory()) {
cwd = buildPathList(dir);
} else {
cwd = new LinkedList<String>();
System.err.println(
"Error: Could not find current working directory");
}
}
/**
* Builds the path list used to compute relative paths.
* @return The path list for the given path
*/
public static List<String> buildPathList(File path) {
List<String> parts = new ArrayList<String>();
try {
path = path.getCanonicalFile();
while (path != null) {
parts.add(0, path.getName());
path = path.getParentFile();
}
} catch (IOException e) {
return new LinkedList<String>();
}
return parts;
}
/**
* Returns a string representation of the relative path for
* the path <code>path</code> from the current working directory.
* @param path The original path we want a relative path for
* @return The string representing the relative path that corresponds
* to the absolute path
*/
public static String getRelativePath(String path) {
return getRelativePath(path, cwd);
}
/**
* Compute the relative path to a file from a base path
* @param base The path list for the base path
* @return The relative path to the file at a given path
*/
public static String getRelativePath(String path, List<String> base) {
StringBuilder str = new StringBuilder();
List<String> leaf = buildPathList(new File(path));
int I = base.size();
int J = leaf.size();
int n = 0;
while (n < I && n < J && base.get(n).equals(leaf.get(n))) {
n += 1;
}
int o = n;
while (o < I) {
str.append(".." + SEPARATOR);
o += 1;
}
while (n < J) {
str.append(leaf.get(n));
n += 1;
if (n < J)
str.append(SEPARATOR);
}
return str.toString();
}
}
test.sh 0 → 100755
#!/bin/bash
set -eu
OUTDIR="${1:-data}"
echo "OUTDIR: ${OUTDIR}"
gradle jar
java -jar ragdoll2.jar \
$(find src/gen/ -name '*.java') \
$(find extendj/src/frontend -name '*.java') \
-d "${OUTDIR}"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment