blob: 621fceb9fb6af0dc36bf7a4debe09c9a17db8f14 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.helpgenerator.sunapi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.ease.helpgenerator.model.AbstractClassModel;
import org.eclipse.ease.helpgenerator.model.Description;
import org.eclipse.ease.helpgenerator.model.ExceptionValue;
import org.eclipse.ease.helpgenerator.model.Field;
import org.eclipse.ease.helpgenerator.model.Method;
import org.eclipse.ease.helpgenerator.model.Parameter;
import org.eclipse.ease.helpgenerator.model.ReturnValue;
import org.eclipse.ease.helpgenerator.model.ScriptExample;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationDesc.ElementValuePair;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MemberDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.Tag;
import com.sun.javadoc.ThrowsTag;
import com.sun.javadoc.Type;
public class Java5ClassModel extends AbstractClassModel {
private static final String WRAP_TO_SCRIPT = "WrapToScript";
private static final String QUALIFIED_WRAP_TO_SCRIPT = "org.eclipse.ease.modules." + WRAP_TO_SCRIPT;
private static final Object SCRIPT_PARAMETER = "ScriptParameter";
private static final Object QUALIFIED_SCRIPT_PARAMETER = "org.eclipse.ease.modules." + SCRIPT_PARAMETER;
private static String getParameterComment(final MethodDoc method, final String name) {
final String comment = extractComment(method, method1 -> {
for (final ParamTag paramTags : method1.paramTags()) {
if (name.equals(paramTags.parameterName()))
return paramTags.parameterComment();
}
return "";
});
return comment;
}
private static String getDefaultValue(com.sun.javadoc.Parameter param) {
final AnnotationDesc parameterAnnotation = getScriptParameterAnnotation(param);
if (parameterAnnotation != null) {
for (final ElementValuePair pair : parameterAnnotation.elementValues()) {
if ("org.eclipse.ease.modules.ScriptParameter.defaultValue()".equals(pair.element().toString()))
return pair.value().toString();
}
}
return null;
}
private static AnnotationDesc getScriptParameterAnnotation(final com.sun.javadoc.Parameter parameter) {
for (final AnnotationDesc annotation : parameter.annotations()) {
if (isScriptParameterAnnotation(annotation))
return annotation;
}
return null;
}
private static boolean isScriptParameterAnnotation(final AnnotationDesc annotation) {
return (QUALIFIED_SCRIPT_PARAMETER.equals(annotation.annotationType().qualifiedName()))
|| (SCRIPT_PARAMETER.equals(annotation.annotationType().qualifiedName()));
}
private static String extractComment(MethodDoc method, CommentExtractor extractor) {
String comment = extractor.extract(method);
if ((comment != null) && (!comment.isEmpty()))
return comment;
// try to look up interfaces
for (final ClassDoc iface : method.containingClass().interfaces()) {
for (final MethodDoc ifaceMethod : iface.methods()) {
if (method.overrides(ifaceMethod)) {
comment = extractComment(ifaceMethod, extractor);
if ((comment != null) && (!comment.isEmpty()))
return comment;
}
}
}
// not found, retry with super class
final ClassDoc parent = method.containingClass().superclass();
if (parent != null) {
for (final MethodDoc superMethod : parent.methods()) {
if (method.overrides(superMethod))
return (extractComment(superMethod, extractor));
}
}
return "";
}
private static AnnotationDesc getWrapAnnotation(final ProgramElementDoc method) {
for (final AnnotationDesc annotation : method.annotations()) {
if (isWrapToScriptAnnotation(annotation))
return annotation;
}
return null;
}
private static boolean isWrapToScriptAnnotation(final AnnotationDesc annotation) {
return (QUALIFIED_WRAP_TO_SCRIPT.equals(annotation.annotationType().qualifiedName()))
|| (WRAP_TO_SCRIPT.equals(annotation.annotationType().qualifiedName()));
}
private static boolean isDeprecated(final MemberDoc field) {
final Tag[] tags = field.tags("deprecated");
return (tags != null) && (tags.length > 0);
}
private static Collection<String> getFunctionAliases(final MethodDoc method) {
final Collection<String> aliases = new HashSet<>();
final AnnotationDesc annotation = getWrapAnnotation(method);
if (annotation != null) {
for (final ElementValuePair pair : annotation.elementValues()) {
if ("alias".equals(pair.element().name())) {
String candidates = pair.value().toString();
candidates = candidates.substring(1, candidates.length() - 1);
for (final String token : candidates.split("[,;]")) {
if (!token.trim().isEmpty())
aliases.add(token.trim());
}
}
}
}
return aliases;
}
private final ClassDoc fClassDoc;
public Java5ClassModel(ClassDoc classDoc) {
fClassDoc = classDoc;
}
@Override
public void populateModel() {
setClassName(fClassDoc.name());
setClassDocumentation(new Description(fClassDoc.commentText()));
setExportedFields(fetchExportedFields());
setExportedMethods(fetchExportedMethods());
setImportedClasses(fetchImportedClasses());
};
private List<String> fetchImportedClasses() {
return Arrays.asList(fClassDoc.importedClasses()).stream().map(f -> f.toString()).collect(Collectors.toList());
}
private List<Field> fetchExportedFields() {
final List<FieldDoc> fields = new ArrayList<>();
final boolean hasAnnotation = hasWrapToScriptAnnotation();
final ArrayList<ClassDoc> candidates = new ArrayList<>();
candidates.add(fClassDoc);
while (!candidates.isEmpty()) {
final ClassDoc clazzDoc = candidates.remove(0);
for (final FieldDoc field : clazzDoc.fields()) {
if ((!hasAnnotation) || (getWrapAnnotation(field) != null))
fields.add(field);
}
// add interfaces
candidates.addAll(Arrays.asList(clazzDoc.interfaces()));
// add super class/interface
final ClassDoc nextCandidate = clazzDoc.superclass();
if ((nextCandidate != null) && (!Object.class.getName().equals(nextCandidate.qualifiedName())))
candidates.add(nextCandidate);
}
// sort fields alphabetically
return fields.stream().map(doc -> {
final String deprecationMessage = isDeprecated(doc) ? doc.tags("deprecated")[0].text() : null;
return new Field(doc.name(), doc.commentText(), deprecationMessage);
}).collect(Collectors.toList());
}
private boolean hasWrapToScriptAnnotation() {
ClassDoc classDoc = fClassDoc;
while (classDoc != null) {
for (final MethodDoc method : classDoc.methods()) {
if (getWrapAnnotation(method) != null)
return true;
}
for (final FieldDoc field : classDoc.fields()) {
if (getWrapAnnotation(field) != null)
return true;
}
classDoc = classDoc.superclass();
}
return false;
}
private List<Method> fetchExportedMethods() {
return new MethodExtractor().getMethods(fClassDoc, hasWrapToScriptAnnotation());
}
private String getExceptionComment(MethodDoc method, Type exceptionType) {
final String comment = extractComment(method, method1 -> {
for (final ThrowsTag tag : method1.throwsTags()) {
if ((exceptionType.simpleTypeName().equals(tag.exceptionName())) || (exceptionType.typeName().equals(tag.exceptionName())))
return tag.exceptionComment();
}
return "";
});
return comment;
}
private static interface CommentExtractor {
String extract(MethodDoc method);
}
private class MethodExtractor {
private final List<Method> fMethods = new ArrayList<>();
private Method findRegisteredMethod(String methodName) {
return fMethods.stream().filter(m -> methodName.equals(m.getName())).findFirst().orElse(null);
}
private void addMethod(Method method) {
final Method existingMethod = findRegisteredMethod(method.getName());
if (existingMethod != null)
existingMethod.fetchDetailsFrom(method);
else
fMethods.add(method);
}
public List<Method> getMethods(ClassDoc classDoc, boolean hasWrapToScriptAnnotation) {
for (final MethodDoc doc : classDoc.methods()) {
final Method registeredMethod = findRegisteredMethod(doc.name());
final AnnotationDesc wrapAnnotation = getWrapAnnotation(doc);
if (!hasWrapToScriptAnnotation || (wrapAnnotation != null) || (registeredMethod != null)) {
if (doc.isPublic()) {
final String deprecationMessage = isDeprecated(doc) ? doc.tags("deprecated")[0].text() : null;
final String returnComment = (doc.tags("return").length > 0) ? doc.tags("return")[0].text() : null;
final ReturnValue returnValue = new ReturnValue(doc.returnType().qualifiedTypeName(), returnComment);
final List<Parameter> parameters = Arrays.asList(doc.parameters()).stream().map(param -> {
final String defaultValue = getDefaultValue(param);
return new Parameter(param.name(), param.typeName(), getParameterComment(doc, param.name()), defaultValue);
}).collect(Collectors.toList());
final List<ExceptionValue> exceptions = Arrays.asList(doc.thrownExceptionTypes()).stream()
.map(e -> new ExceptionValue(e.qualifiedTypeName(), getExceptionComment(doc, e))).collect(Collectors.toList());
final List<ScriptExample> examples = Arrays.asList(doc.tags("scriptExample")).stream().map(example -> new ScriptExample(example.text()))
.collect(Collectors.toList());
addMethod(new Method(doc.name(), extractComment(doc, d -> d.commentText()), deprecationMessage, getFunctionAliases(doc), returnValue,
parameters, exceptions, examples));
}
}
}
for (final ClassDoc interfaceDoc : classDoc.interfaces())
getMethods(interfaceDoc, hasWrapToScriptAnnotation);
final ClassDoc superclassDoc = classDoc.superclass();
if ((superclassDoc != null) && (!Object.class.getName().equals(superclassDoc.qualifiedName())))
getMethods(superclassDoc, hasWrapToScriptAnnotation);
return fMethods;
}
}
}