diff --git a/pom.xml b/pom.xml
index f237bea..8e019dd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
com.github.markusbernhardt
xml-doclet
- 1.0.6-SNAPSHOT
+ 2.0.0-SNAPSHOT
jar
XML Doclet
@@ -20,7 +20,9 @@
UTF-8
true
true
- 1.7
+ 11
+ 1.2.0
+ 2.3.1
@@ -52,13 +54,26 @@
-
- com.sun
- tools
- ${java.version}
- system
- ${java.home}/../lib/tools.jar
-
+
+ javax.xml.bind
+ jaxb-api
+ ${jaxb.version}
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ ${jaxb.version}
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ ${jaxb.version}
+
+
+ javax.activation
+ javax.activation-api
+ ${javax.activation.version}
+
commons-cli
commons-cli
@@ -91,7 +106,7 @@
org.jvnet.jaxb2.maven2
maven-jaxb2-plugin
- 0.12.3
+ 0.14.0
generate-sources
diff --git a/src/main/java/com/github/markusbernhardt/xmldoclet/JavadocTransformer.java b/src/main/java/com/github/markusbernhardt/xmldoclet/JavadocTransformer.java
new file mode 100644
index 0000000..d0d151a
--- /dev/null
+++ b/src/main/java/com/github/markusbernhardt/xmldoclet/JavadocTransformer.java
@@ -0,0 +1,539 @@
+package com.github.markusbernhardt.xmldoclet;
+
+import com.github.markusbernhardt.xmldoclet.xjc.Root;
+import com.github.markusbernhardt.xmldoclet.xjc.TagInfo;
+import com.github.markusbernhardt.xmldoclet.xjc.TypeInfo;
+import com.github.markusbernhardt.xmldoclet.xjc.Wildcard;
+import com.sun.source.doctree.BlockTagTree;
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.util.DocTrees;
+import java.io.Externalizable;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.JavaFileObject;
+import jdk.javadoc.doclet.DocletEnvironment;
+
+public class JavadocTransformer {
+
+ private final DocletEnvironment environment;
+ private final DocTrees docTrees;
+ private final Elements elementUtils;
+ private final Types typeUtils;
+ private final TypeMirror objectTypeMirror;
+ private final TypeElement errorTypeElement;
+ private final TypeElement exceptionTypeElement;
+ private final TypeElement externalizableTypeElement;
+ private final TypeElement serializableTypeElement;
+
+ public JavadocTransformer(DocletEnvironment environment) {
+ this.environment = environment;
+ this.docTrees = environment.getDocTrees();
+ this.elementUtils = environment.getElementUtils();
+ this.typeUtils = environment.getTypeUtils();
+ this.objectTypeMirror = elementUtils.getTypeElement(Object.class.getCanonicalName()).asType();
+ this.errorTypeElement = elementUtils.getTypeElement(Error.class.getCanonicalName());
+ this.exceptionTypeElement = elementUtils.getTypeElement(Exception.class.getCanonicalName());
+ this.externalizableTypeElement = elementUtils.getTypeElement(Externalizable.class.getCanonicalName());
+ this.serializableTypeElement = elementUtils.getTypeElement(Serializable.class.getCanonicalName());
+ }
+
+ public Root transform() {
+ final Root xmlRoot = new Root();
+ transformElements(xmlRoot, environment.getSpecifiedElements());
+ return xmlRoot;
+ }
+
+ private void transformElements(Root xmlRoot, Collection extends Element> elements) {
+ for (Element element : elements) {
+ transformElement(xmlRoot, element);
+ }
+ }
+
+ private void transformElement(Root xmlRoot, Element element) {
+ if (element instanceof PackageElement) {
+ transformPackageElement(xmlRoot, (PackageElement) element);
+ }
+ if (element instanceof TypeElement) {
+ transformTypeElement(xmlRoot, (TypeElement) element);
+ }
+ }
+
+ private void transformPackageElement(Root xmlRoot, PackageElement packageElement) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Package xmlPackage = new com.github.markusbernhardt.xmldoclet.xjc.Package();
+ xmlPackage.setName(packageElement.getQualifiedName().toString());
+ transformJavadoc(packageElement, xmlPackage::setComment, xmlPackage::getTag);
+ xmlRoot.getPackage().add(xmlPackage);
+ transformElements(xmlRoot, packageElement.getEnclosedElements());
+ }
+
+ private void transformTypeElement(Root xmlRoot, TypeElement typeElement) {
+ if (environment.getFileKind(typeElement) != JavaFileObject.Kind.SOURCE) {
+ return;
+ }
+ if (!environment.isIncluded(typeElement)) {
+ return;
+ }
+ final PackageElement packageElement = getEnclosingPackage(typeElement);
+ if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Annotation xmlAnnotation = new com.github.markusbernhardt.xmldoclet.xjc.Annotation();
+ setNames(typeElement, packageElement, xmlAnnotation::setName, xmlAnnotation::setQualified);
+ xmlAnnotation.setScope(getScope(typeElement.getModifiers()));
+ xmlAnnotation.setIncluded(environment.isIncluded(typeElement));
+ transformJavadoc(typeElement, xmlAnnotation::setComment, xmlAnnotation::getTag);
+ xmlAnnotation.getAnnotation().addAll(transformAnnotationMirrors(typeElement.getAnnotationMirrors()));
+ for (Element enclosedElement : typeElement.getEnclosedElements()) {
+ if (!environment.isIncluded(enclosedElement)) {
+ continue;
+ }
+ if (enclosedElement.getKind() == ElementKind.METHOD) {
+ final ExecutableElement methodElement = (ExecutableElement) enclosedElement;
+ xmlAnnotation.getElement().add(transformAnnotationElement(methodElement, xmlAnnotation.getQualified()));
+ }
+ }
+ getXmlPackage(xmlRoot, packageElement).getAnnotation().add(xmlAnnotation);
+ }
+ if (typeElement.getKind() == ElementKind.ENUM) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Enum xmlEnum = new com.github.markusbernhardt.xmldoclet.xjc.Enum();
+ setNames(typeElement, packageElement, xmlEnum::setName, xmlEnum::setQualified);
+ xmlEnum.setScope(getScope(typeElement.getModifiers()));
+ xmlEnum.setIncluded(environment.isIncluded(typeElement));
+ transformJavadoc(typeElement, xmlEnum::setComment, xmlEnum::getTag);
+ xmlEnum.setClazz(transformTypeMirror(typeElement.getSuperclass()));
+ xmlEnum.getInterface().addAll(transformTypeMirrors(typeElement.getInterfaces()));
+ xmlEnum.getAnnotation().addAll(transformAnnotationMirrors(typeElement.getAnnotationMirrors()));
+ for (Element enclosedElement : typeElement.getEnclosedElements()) {
+ if (!environment.isIncluded(enclosedElement)) {
+ continue;
+ }
+ if (enclosedElement.getKind() == ElementKind.ENUM_CONSTANT) {
+ final VariableElement constantElement = (VariableElement) enclosedElement;
+ xmlEnum.getConstant().add(transformEnumConstant(constantElement));
+ }
+ }
+ getXmlPackage(xmlRoot, packageElement).getEnum().add(xmlEnum);
+ }
+ if (typeElement.getKind() == ElementKind.INTERFACE) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Interface xmlInterface = new com.github.markusbernhardt.xmldoclet.xjc.Interface();
+ setNames(typeElement, packageElement, xmlInterface::setName, xmlInterface::setQualified);
+ xmlInterface.setScope(getScope(typeElement.getModifiers()));
+ xmlInterface.setIncluded(environment.isIncluded(typeElement));
+ transformJavadoc(typeElement, xmlInterface::setComment, xmlInterface::getTag);
+ xmlInterface.getGeneric().addAll(transformTypeParameters(typeElement.getTypeParameters()));
+ xmlInterface.getInterface().addAll(transformTypeMirrors(typeElement.getInterfaces()));
+ xmlInterface.getAnnotation().addAll(transformAnnotationMirrors(typeElement.getAnnotationMirrors()));
+ for (Element enclosedElement : typeElement.getEnclosedElements()) {
+ if (!environment.isIncluded(enclosedElement)) {
+ continue;
+ }
+ if (enclosedElement.getKind() == ElementKind.FIELD) {
+ final VariableElement fieldElement = (VariableElement) enclosedElement;
+ xmlInterface.getField().add(transformFieldElement(fieldElement, xmlInterface.getQualified()));
+ }
+ if (enclosedElement.getKind() == ElementKind.METHOD) {
+ final ExecutableElement methodElement = (ExecutableElement) enclosedElement;
+ xmlInterface.getMethod().add(transformMethodElement(methodElement, xmlInterface.getQualified()));
+ }
+ }
+ getXmlPackage(xmlRoot, packageElement).getInterface().add(xmlInterface);
+ }
+ if (typeElement.getKind() == ElementKind.CLASS) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Class xmlClass = new com.github.markusbernhardt.xmldoclet.xjc.Class();
+ setNames(typeElement, packageElement, xmlClass::setName, xmlClass::setQualified);
+ xmlClass.setScope(getScope(typeElement.getModifiers()));
+ setFlag(typeElement, Modifier.ABSTRACT, xmlClass::setAbstract);
+ xmlClass.setError(environment.getTypeUtils().isAssignable(typeElement.asType(), errorTypeElement.asType()));
+ xmlClass.setException(environment.getTypeUtils().isAssignable(typeElement.asType(), exceptionTypeElement.asType()));
+ xmlClass.setExternalizable(environment.getTypeUtils().isAssignable(typeElement.asType(), externalizableTypeElement.asType()));
+ xmlClass.setIncluded(environment.isIncluded(typeElement));
+ xmlClass.setSerializable(environment.getTypeUtils().isAssignable(typeElement.asType(), serializableTypeElement.asType()));
+ transformJavadoc(typeElement, xmlClass::setComment, xmlClass::getTag);
+ xmlClass.getGeneric().addAll(transformTypeParameters(typeElement.getTypeParameters()));
+ xmlClass.setClazz(transformTypeMirror(typeElement.getSuperclass()));
+ xmlClass.getInterface().addAll(transformTypeMirrors(typeElement.getInterfaces()));
+ xmlClass.getAnnotation().addAll(transformAnnotationMirrors(typeElement.getAnnotationMirrors()));
+ for (Element enclosedElement : typeElement.getEnclosedElements()) {
+ if (!environment.isIncluded(enclosedElement)) {
+ continue;
+ }
+ if (enclosedElement.getKind() == ElementKind.FIELD) {
+ final VariableElement fieldElement = (VariableElement) enclosedElement;
+ xmlClass.getField().add(transformFieldElement(fieldElement, xmlClass.getQualified()));
+ }
+ if (enclosedElement.getKind() == ElementKind.CONSTRUCTOR) {
+ final ExecutableElement constructorElement = (ExecutableElement) enclosedElement;
+ xmlClass.getConstructor().add(transformConstructorElement(constructorElement, xmlClass.getQualified()));
+ }
+ if (enclosedElement.getKind() == ElementKind.METHOD) {
+ final ExecutableElement methodElement = (ExecutableElement) enclosedElement;
+ xmlClass.getMethod().add(transformMethodElement(methodElement, xmlClass.getQualified()));
+ }
+ }
+ getXmlPackage(xmlRoot, packageElement).getClazz().add(xmlClass);
+ }
+ transformElements(xmlRoot, typeElement.getEnclosedElements());
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.AnnotationElement transformAnnotationElement(ExecutableElement annotationElement, String qualified) {
+ final com.github.markusbernhardt.xmldoclet.xjc.AnnotationElement xmlElement = new com.github.markusbernhardt.xmldoclet.xjc.AnnotationElement();
+ xmlElement.setName(annotationElement.getSimpleName().toString());
+ xmlElement.setQualified(qualified + "." + xmlElement.getName());
+ if (annotationElement.getDefaultValue() != null) {
+ xmlElement.setDefault(String.valueOf(annotationElement.getDefaultValue().getValue()));
+ }
+ xmlElement.setType(transformTypeMirror(annotationElement.getReturnType()));
+ return xmlElement;
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.EnumConstant transformEnumConstant(VariableElement constantElement) {
+ final com.github.markusbernhardt.xmldoclet.xjc.EnumConstant xmlConstant = new com.github.markusbernhardt.xmldoclet.xjc.EnumConstant();
+ xmlConstant.setName(constantElement.getSimpleName().toString());
+ transformJavadoc(constantElement, xmlConstant::setComment, xmlConstant::getTag);
+ xmlConstant.getAnnotation().addAll(transformAnnotationMirrors(constantElement.getAnnotationMirrors()));
+ return xmlConstant;
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.Field transformFieldElement(VariableElement variableElement, String qualified) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Field xmlField = new com.github.markusbernhardt.xmldoclet.xjc.Field();
+ xmlField.setName(variableElement.getSimpleName().toString());
+ xmlField.setQualified(qualified + "." + xmlField.getName());
+ xmlField.setScope(getScope(variableElement.getModifiers()));
+ setFlag(variableElement, Modifier.VOLATILE, xmlField::setVolatile);
+ setFlag(variableElement, Modifier.TRANSIENT, xmlField::setTransient);
+ setFlag(variableElement, Modifier.STATIC, xmlField::setStatic);
+ setFlag(variableElement, Modifier.FINAL, xmlField::setFinal);
+ xmlField.setType(transformTypeMirror(variableElement.asType()));
+ transformJavadoc(variableElement, xmlField::setComment, xmlField::getTag);
+ final Object constantValue = variableElement.getConstantValue();
+ if (constantValue != null) {
+ xmlField.setConstant(elementUtils.getConstantExpression(constantValue));
+ }
+ xmlField.getAnnotation().addAll(transformAnnotationMirrors(variableElement.getAnnotationMirrors()));
+ return xmlField;
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.Constructor transformConstructorElement(ExecutableElement constructorElement, String qualified) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Constructor xmlConstructor = new com.github.markusbernhardt.xmldoclet.xjc.Constructor();
+ xmlConstructor.setName(constructorElement.getEnclosingElement().getSimpleName().toString());
+ xmlConstructor.setSignature(getSignature(constructorElement));
+ xmlConstructor.setQualified(qualified);
+ xmlConstructor.setScope(getScope(constructorElement.getModifiers()));
+ setFlag(constructorElement, Modifier.FINAL, xmlConstructor::setFinal);
+ xmlConstructor.setIncluded(environment.isIncluded(constructorElement));
+ setFlag(constructorElement, Modifier.NATIVE, xmlConstructor::setNative);
+ setFlag(constructorElement, Modifier.SYNCHRONIZED, xmlConstructor::setSynchronized);
+ setFlag(constructorElement, Modifier.STATIC, xmlConstructor::setStatic);
+ xmlConstructor.setVarArgs(constructorElement.isVarArgs());
+ transformJavadoc(constructorElement, xmlConstructor::setComment, xmlConstructor::getTag);
+ xmlConstructor.getParameter().addAll(transformParameters(constructorElement));
+ xmlConstructor.getException().addAll(transformExceptions(constructorElement));
+ xmlConstructor.getAnnotation().addAll(transformAnnotationMirrors(constructorElement.getAnnotationMirrors()));
+ return xmlConstructor;
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.Method transformMethodElement(ExecutableElement methodElement, String qualified) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Method xmlMethod = new com.github.markusbernhardt.xmldoclet.xjc.Method();
+ xmlMethod.setName(methodElement.getSimpleName().toString());
+ xmlMethod.setSignature(getSignature(methodElement));
+ xmlMethod.setQualified(qualified + "." + xmlMethod.getName());
+ xmlMethod.setScope(getScope(methodElement.getModifiers()));
+ setFlag(methodElement, Modifier.ABSTRACT, xmlMethod::setAbstract);
+ setFlag(methodElement, Modifier.FINAL, xmlMethod::setFinal);
+ xmlMethod.setIncluded(environment.isIncluded(methodElement));
+ setFlag(methodElement, Modifier.NATIVE, xmlMethod::setNative);
+ setFlag(methodElement, Modifier.SYNCHRONIZED, xmlMethod::setSynchronized);
+ setFlag(methodElement, Modifier.STATIC, xmlMethod::setStatic);
+ xmlMethod.setVarArgs(methodElement.isVarArgs());
+ transformJavadoc(methodElement, xmlMethod::setComment, xmlMethod::getTag);
+ xmlMethod.getParameter().addAll(transformParameters(methodElement));
+ xmlMethod.setReturn(transformTypeMirror(methodElement.getReturnType()));
+ xmlMethod.getException().addAll(transformExceptions(methodElement));
+ xmlMethod.getAnnotation().addAll(transformAnnotationMirrors(methodElement.getAnnotationMirrors()));
+ return xmlMethod;
+ }
+
+ private List transformExceptions(ExecutableElement executableElement) {
+ return transformTypeMirrors(executableElement.getThrownTypes());
+ }
+
+ private List transformParameters(ExecutableElement executableElement) {
+ return executableElement.getParameters().stream()
+ .map(this::transformParameter)
+ .collect(Collectors.toList());
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.MethodParameter transformParameter(VariableElement methodParameter) {
+ final com.github.markusbernhardt.xmldoclet.xjc.MethodParameter xmlParameter = new com.github.markusbernhardt.xmldoclet.xjc.MethodParameter();
+ xmlParameter.setName(methodParameter.getSimpleName().toString());
+ xmlParameter.setType(transformTypeMirror(methodParameter.asType()));
+ xmlParameter.getAnnotation().addAll(transformAnnotationMirrors(methodParameter.getAnnotationMirrors()));
+ return xmlParameter;
+ }
+
+ private List transformAnnotationMirrors(List extends AnnotationMirror> annotationMirrors) {
+ return annotationMirrors.stream()
+ .map(this::transformAnnotationMirror)
+ .collect(Collectors.toList());
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.AnnotationInstance transformAnnotationMirror(AnnotationMirror annotationMirror) {
+ final com.github.markusbernhardt.xmldoclet.xjc.AnnotationInstance xmlAnnotation = new com.github.markusbernhardt.xmldoclet.xjc.AnnotationInstance();
+ setNames(annotationMirror.getAnnotationType().asElement(), xmlAnnotation::setName, xmlAnnotation::setQualified);
+ xmlAnnotation.getArgument().addAll(transformAnnotationValues(annotationMirror.getElementValues()));
+ return xmlAnnotation;
+ }
+
+ private List transformAnnotationValues(Map extends ExecutableElement, ? extends AnnotationValue> annotationValues) {
+ return annotationValues.entrySet().stream()
+ .map(entry -> transformAnnotationValue(entry.getKey(), entry.getValue()))
+ .collect(Collectors.toList());
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.AnnotationArgument transformAnnotationValue(ExecutableElement element, AnnotationValue annotationValue) {
+ final com.github.markusbernhardt.xmldoclet.xjc.AnnotationArgument xmlArgument = new com.github.markusbernhardt.xmldoclet.xjc.AnnotationArgument();
+ xmlArgument.setName(element.getSimpleName().toString());
+ final TypeMirror type = element.getReturnType();
+ xmlArgument.setType(transformTypeMirror(type));
+ final Object value = annotationValue.getValue();
+ if (type instanceof ArrayType) {
+ final ArrayType arrayType = (ArrayType) type;
+ final TypeMirror componentType = arrayType.getComponentType();
+ xmlArgument.setPrimitive(componentType instanceof PrimitiveType);
+ xmlArgument.setArray(true);
+ if (value instanceof List>) {
+ final List> list = (List>) value;
+ for (Object item : list) {
+ if (item instanceof AnnotationValue) {
+ final AnnotationValue annotationValueItem = (AnnotationValue) item;
+// transformAnnotationSingleValueCompatibleMode(xmlArgument, annotationValueItem.getValue(), componentType);
+ transformAnnotationSingleValue(xmlArgument, annotationValueItem.getValue(), componentType);
+ }
+ }
+ }
+ } else {
+ xmlArgument.setPrimitive(type instanceof PrimitiveType);
+ xmlArgument.setArray(false);
+ transformAnnotationSingleValue(xmlArgument, value, type);
+ }
+ return xmlArgument;
+ }
+
+ // TODO remove this method
+ @Deprecated
+ private void transformAnnotationSingleValueCompatibleMode(com.github.markusbernhardt.xmldoclet.xjc.AnnotationArgument xmlArgument, Object value, TypeMirror type) {
+ if (value instanceof VariableElement) {
+ // compatible mode
+ final VariableElement variableElement = (VariableElement) value;
+ final String qualifiedName = variableElement.getEnclosingElement() + "." + variableElement;
+ xmlArgument.getValue().add(qualifiedName);
+ } else {
+ transformAnnotationSingleValue(xmlArgument, value, type);
+ }
+ }
+
+ private void transformAnnotationSingleValue(com.github.markusbernhardt.xmldoclet.xjc.AnnotationArgument xmlArgument, Object value, TypeMirror type) {
+ if (type instanceof PrimitiveType) {
+ if (value instanceof Character) {
+ final Character character = (Character) value;
+ xmlArgument.getValue().add(String.valueOf((long) character));
+ } else {
+ xmlArgument.getValue().add(String.valueOf(value));
+ }
+ } else if (value instanceof String) {
+ xmlArgument.getValue().add(String.valueOf(value));
+ } else if (value instanceof TypeMirror) {
+ final TypeMirror typeMirror = (TypeMirror) value;
+ xmlArgument.getValue().add(typeMirror.toString());
+ } else if (value instanceof VariableElement) {
+ final VariableElement variableElement = (VariableElement) value;
+ xmlArgument.getValue().add(variableElement.getSimpleName().toString());
+ } else if (value instanceof AnnotationMirror) {
+ final AnnotationMirror annotationMirror = (AnnotationMirror) value;
+ xmlArgument.getAnnotation().add(transformAnnotationMirror(annotationMirror));
+ } else if (value instanceof List>) {
+ // already handled by calling method
+ }
+ }
+
+ private List transformTypeParameters(List extends TypeParameterElement> typeParameterElements) {
+ return typeParameterElements.stream()
+ .map(this::transformTypeParameter)
+ .collect(Collectors.toList());
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.TypeParameter transformTypeParameter(TypeParameterElement typeParameterElement) {
+ final com.github.markusbernhardt.xmldoclet.xjc.TypeParameter xmlTypeParameter = new com.github.markusbernhardt.xmldoclet.xjc.TypeParameter();
+ xmlTypeParameter.setName(typeParameterElement.getSimpleName().toString());
+ final List bounds = typeParameterElement.getBounds().stream()
+ .filter(bound -> !typeUtils.isSameType(bound, objectTypeMirror))
+ .map(TypeMirror::toString)
+ .collect(Collectors.toList());
+ xmlTypeParameter.getBound().addAll(bounds);
+ return xmlTypeParameter;
+ }
+
+ private void transformJavadoc(Element element, Consumer commentSetter, Supplier> tagsGetter) {
+ final DocCommentTree docCommentTree = docTrees.getDocCommentTree(element);
+ if (docCommentTree != null) {
+ final String fullBody = docCommentTree.getFullBody().stream()
+ .map(Object::toString)
+ .collect(Collectors.joining());
+ if (!fullBody.isEmpty()) {
+ commentSetter.accept(fullBody);
+ }
+ final List tags = tagsGetter.get();
+ for (DocTree blockTag : docCommentTree.getBlockTags()) {
+ if (blockTag instanceof BlockTagTree) {
+ final BlockTagTree blockTagTree = (BlockTagTree) blockTag;
+ final String[] parts = blockTagTree.toString().split(" ", 2);
+ if (parts.length == 2) {
+ final TagInfo tag = new TagInfo();
+ tag.setName(parts[0]);
+ tag.setText(parts[1]);
+ tags.add(tag);
+ }
+ }
+ }
+ }
+ }
+
+ private PackageElement getEnclosingPackage(Element element) {
+ Objects.requireNonNull(element);
+ return element instanceof PackageElement
+ ? (PackageElement) element
+ : getEnclosingPackage(element.getEnclosingElement());
+ }
+
+ private static String getScope(Set modifiers) {
+ if (modifiers.contains(Modifier.PUBLIC)) return "public";
+ if (modifiers.contains(Modifier.PROTECTED)) return "protected";
+ if (modifiers.contains(Modifier.PRIVATE)) return "private";
+ return "";
+ }
+
+ private static String getSignature(ExecutableElement executableElement) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ final List extends VariableElement> parameters = executableElement.getParameters();
+ final Iterator extends VariableElement> iterator = parameters.iterator();
+ while (iterator.hasNext()) {
+ final VariableElement variableElement = iterator.next();
+ if (iterator.hasNext()) {
+ sb.append(variableElement.asType().toString());
+ sb.append(", ");
+ } else {
+ if (variableElement.asType() instanceof ArrayType && executableElement.isVarArgs()) {
+ final ArrayType arrayType = (ArrayType) variableElement.asType();
+ sb.append(arrayType.getComponentType().toString());
+ sb.append("...");
+ } else {
+ sb.append(variableElement.asType().toString());
+ }
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ private List transformTypeMirrorIfNonNull(TypeMirror typeMirror) {
+ return typeMirror != null
+ ? Collections.singletonList(transformTypeMirror(typeMirror))
+ : Collections.emptyList();
+ }
+
+ private List transformTypeMirrors(List extends TypeMirror> typeMirrors) {
+ return typeMirrors.stream()
+ .map(this::transformTypeMirror)
+ .collect(Collectors.toList());
+ }
+
+ private TypeInfo transformTypeMirror(TypeMirror typeMirror) {
+ final TypeInfo xmlTypeInfo = new TypeInfo();
+ if (typeMirror instanceof DeclaredType) {
+ xmlTypeInfo.setQualified(typeUtils.erasure(typeMirror).toString());
+ final DeclaredType declaredType = (DeclaredType) typeMirror;
+ final List generic = declaredType.getTypeArguments().stream()
+ .map(this::transformTypeMirror)
+ .collect(Collectors.toList());
+ xmlTypeInfo.getGeneric().addAll(generic);
+ } else if (typeMirror instanceof ArrayType) {
+ final ArrayType arrayType = (ArrayType) typeMirror;
+ final TypeInfo typeInfo = transformTypeMirror(arrayType.getComponentType());
+ typeInfo.setDimension((typeInfo.getDimension() != null ? typeInfo.getDimension() : "") + "[]");
+ return typeInfo;
+ } else if (typeMirror instanceof WildcardType) {
+ xmlTypeInfo.setQualified("?");
+ final WildcardType wildcardType = (WildcardType) typeMirror;
+ final Wildcard wildcard = new Wildcard();
+ wildcard.getExtendsBound().addAll(transformTypeMirrorIfNonNull(wildcardType.getExtendsBound()));
+ wildcard.getSuperBound().addAll(transformTypeMirrorIfNonNull(wildcardType.getSuperBound()));
+ xmlTypeInfo.setWildcard(wildcard);
+ } else {
+ xmlTypeInfo.setQualified(typeMirror.toString());
+ }
+ return xmlTypeInfo;
+ }
+
+ private static void setFlag(Element element, Modifier modifier, Consumer flagSetter) {
+ flagSetter.accept(element.getModifiers().contains(modifier));
+ }
+
+ private com.github.markusbernhardt.xmldoclet.xjc.Package getXmlPackage(Root xmlRoot, PackageElement packageElement) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Package xmlPackage = xmlRoot.getPackage().stream()
+ .filter(p -> Objects.equals(p.getName(), packageElement.getQualifiedName().toString()))
+ .findFirst()
+ .orElse(null);
+ if (xmlPackage == null) {
+ final com.github.markusbernhardt.xmldoclet.xjc.Package newXmlPackage = new com.github.markusbernhardt.xmldoclet.xjc.Package();
+ newXmlPackage.setName(packageElement.getQualifiedName().toString());
+ xmlRoot.getPackage().add(newXmlPackage);
+ return newXmlPackage;
+ } else {
+ return xmlPackage;
+ }
+ }
+
+ private void setNames(QualifiedNameable qualifiedNameable, PackageElement packageElement, Consumer nameSetter, Consumer qualifiedSetter) {
+ setNames(qualifiedNameable.getQualifiedName().toString(), packageElement, nameSetter, qualifiedSetter);
+ }
+
+ private void setNames(Element element, Consumer nameSetter, Consumer qualifiedSetter) {
+ setNames(element.toString(), getEnclosingPackage(element), nameSetter, qualifiedSetter);
+ }
+
+ private void setNames(String qualifiedName, PackageElement packageElement, Consumer nameSetter, Consumer qualifiedSetter) {
+ final String packageName = packageElement.getQualifiedName().toString();
+ final String packagePrefix = packageName.isEmpty() ? "" : packageName + ".";
+ final String name = qualifiedName.substring(packagePrefix.length());
+ nameSetter.accept(name);
+ qualifiedSetter.accept(qualifiedName);
+ }
+
+}
diff --git a/src/main/java/com/github/markusbernhardt/xmldoclet/NewXmlDoclet.java b/src/main/java/com/github/markusbernhardt/xmldoclet/NewXmlDoclet.java
new file mode 100644
index 0000000..7f18a8c
--- /dev/null
+++ b/src/main/java/com/github/markusbernhardt/xmldoclet/NewXmlDoclet.java
@@ -0,0 +1,194 @@
+package com.github.markusbernhardt.xmldoclet;
+
+import com.github.markusbernhardt.xmldoclet.xjc.Annotation;
+import com.github.markusbernhardt.xmldoclet.xjc.Class;
+import com.github.markusbernhardt.xmldoclet.xjc.Enum;
+import com.github.markusbernhardt.xmldoclet.xjc.Interface;
+import com.github.markusbernhardt.xmldoclet.xjc.Package;
+import com.github.markusbernhardt.xmldoclet.xjc.Root;
+import java.io.BufferedOutputStream;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.function.Consumer;
+import javax.lang.model.SourceVersion;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+
+
+public class NewXmlDoclet implements Doclet {
+
+ /**
+ * For tests.
+ */
+ public static Root root;
+
+ private String directory;
+ private String encoding;
+ private boolean dryrun;
+ private String filename;
+
+ @Override
+ public void init(Locale locale, Reporter reporter) {
+ }
+
+ @Override
+ public String getName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public Set extends Option> getSupportedOptions() {
+ return new LinkedHashSet<>(Arrays.asList(
+ new SingleArgumentOption(
+ Arrays.asList("-d"),
+ "directory",
+ "Destination directory for output file.\nDefault: .",
+ argument -> this.directory = argument
+ ),
+ new SingleArgumentOption(
+ Arrays.asList("-docencoding"),
+ "encoding",
+ "Encoding of the output file.\nDefault: UTF8",
+ argument -> this.encoding = argument
+ ),
+ new FlagOption(
+ Arrays.asList("-dryrun"),
+ "Parse javadoc, but don't write output file.\nDefault: false",
+ () -> this.dryrun = true
+ ),
+ new SingleArgumentOption(
+ Arrays.asList("-filename"),
+ "filename",
+ "Name of the output file.\nDefault: javadoc.xml",
+ argument -> this.filename = argument
+ )
+ ));
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ @Override
+ public boolean run(DocletEnvironment environment) {
+ final Root xmlRoot = new JavadocTransformer(environment).transform();
+ root = xmlRoot;
+ save(xmlRoot);
+ return true;
+ }
+
+ private void save(Root xmlRoot) {
+ ClassLoader originalClassLoader = null;
+ try {
+ originalClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+ if (dryrun) {
+ return;
+ }
+ sort(xmlRoot);
+ final JAXBContext context = JAXBContext.newInstance(Root.class);
+ final CharArrayWriter writer = new CharArrayWriter();
+ final Marshaller marshaller = context.createMarshaller();
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ if (encoding != null) {
+ marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
+ }
+ final String name = (directory != null ? directory + File.separator : "") + (filename != null ? filename : "javadoc.xml");
+ try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(name))) {
+ marshaller.marshal(xmlRoot, outputStream);
+ }
+ marshaller.marshal(xmlRoot, writer);
+ } catch (JAXBException | IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (originalClassLoader != null) {
+ Thread.currentThread().setContextClassLoader(originalClassLoader);
+ }
+ }
+ }
+
+ public static void sort(Root xmlRoot) {
+ xmlRoot.getPackage().sort(Comparator.comparing(Package::getName));
+ for (Package pkg : xmlRoot.getPackage()) {
+ pkg.getAnnotation().sort(Comparator.comparing(Annotation::getQualified));
+ pkg.getEnum().sort(Comparator.comparing(Enum::getQualified));
+ pkg.getInterface().sort(Comparator.comparing(Interface::getQualified));
+ pkg.getClazz().sort(Comparator.comparing(Class::getQualified));
+ }
+ }
+
+ private static class FlagOption extends StandardOption {
+ public FlagOption(List names, String description, Runnable processor) {
+ super(names, null, description, 0, arguments -> processor.run());
+ }
+ }
+
+ private static class SingleArgumentOption extends StandardOption {
+ public SingleArgumentOption(List names, String parameter, String description, Consumer processor) {
+ super(names, parameter, description, 1, arguments -> processor.accept(arguments.get(0)));
+ }
+ }
+
+ private static class StandardOption implements Option {
+
+ private final List names;
+ private final String parameters;
+ private final String description;
+ private final int argumentCount;
+ private final Consumer> processor;
+
+ public StandardOption(List names, String parameters, String description, int argumentCount, Consumer> processor) {
+ this.names = names;
+ this.parameters = parameters;
+ this.description = description;
+ this.argumentCount = argumentCount;
+ this.processor = processor;
+ }
+
+ @Override
+ public int getArgumentCount() {
+ return argumentCount;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public Kind getKind() {
+ return Kind.STANDARD;
+ }
+
+ @Override
+ public List getNames() {
+ return names;
+ }
+
+ @Override
+ public String getParameters() {
+ return parameters;
+ }
+
+ @Override
+ public boolean process(String option, List arguments) {
+ processor.accept(arguments);
+ return true;
+ }
+ }
+
+}
diff --git a/src/main/java/com/github/markusbernhardt/xmldoclet/XmlDoclet.java b/src/main/java/com/github/markusbernhardt/xmldoclet/XmlDoclet.java
index 99b029e..d7094c2 100644
--- a/src/main/java/com/github/markusbernhardt/xmldoclet/XmlDoclet.java
+++ b/src/main/java/com/github/markusbernhardt/xmldoclet/XmlDoclet.java
@@ -158,6 +158,8 @@ public static void save(CommandLine commandLine, Root root) {
if (commandLine.hasOption("dryrun")) {
return;
}
+ // for easier comparison between original and new version
+ NewXmlDoclet.sort(root);
FileOutputStream fileOutputStream = null;
BufferedOutputStream bufferedOutputStream = null;
diff --git a/src/test/java/com/github/markusbernhardt/xmldoclet/AbstractTestParent.java b/src/test/java/com/github/markusbernhardt/xmldoclet/AbstractTestParent.java
index 4a0b392..4cea1c8 100644
--- a/src/test/java/com/github/markusbernhardt/xmldoclet/AbstractTestParent.java
+++ b/src/test/java/com/github/markusbernhardt/xmldoclet/AbstractTestParent.java
@@ -3,8 +3,19 @@
import java.io.File;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.tools.DocumentationTool;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import javax.xml.bind.JAXB;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,7 +72,7 @@ public class AbstractTestParent {
* Additional Arguments.
* @return XStream compatible data structure
*/
- public Root executeJavadoc(String extendedClassPath, String[] sourcePaths, String[] packages, String[] sourceFiles,
+ public Root oldExecuteJavadoc(String extendedClassPath, String[] sourcePaths, String[] packages, String[] sourceFiles,
String[] subPackages, String[] additionalArguments) {
try {
OutputStream errors = new LoggingOutputStream(log, LoggingLevelEnum.ERROR);
@@ -130,9 +141,69 @@ public Root executeJavadoc(String extendedClassPath, String[] sourcePaths, Strin
log.error("doclet error", e);
}
+ // for debugging
+// System.out.println(marshalJAXB(XmlDoclet.root));
return XmlDoclet.root;
}
+ public Root executeJavadoc(String extendedClassPath, String[] sourcePaths, String[] packages, String[] sourceFiles,
+ String[] subPackages, String[] additionalArguments) {
+ try {
+ final DocumentationTool documentationTool = ToolProvider.getSystemDocumentationTool();
+ final StandardJavaFileManager fileManager = documentationTool.getStandardFileManager(null, null, null);
+ final ArrayList compilationUnits = new ArrayList<>();
+
+ // sourcePaths
+ if (sourcePaths != null) {
+ fileManager.setLocation(
+ StandardLocation.SOURCE_PATH,
+ Stream.of(sourcePaths).map(File::new).collect(Collectors.toList())
+ );
+ }
+
+ // subPackages
+ if (subPackages != null) {
+ for (String pkg : subPackages) {
+ final Iterable javaFileObjects = fileManager.list(
+ StandardLocation.SOURCE_PATH,
+ pkg,
+ Collections.singleton(JavaFileObject.Kind.SOURCE),
+ true
+ );
+ javaFileObjects.forEach(compilationUnits::add);
+ }
+ }
+
+ // sourceFiles
+ if (sourceFiles != null) {
+ fileManager.getJavaFileObjects(sourceFiles).forEach(compilationUnits::add);
+ }
+
+ // additionalArguments
+ final List options = Stream
+ .concat(
+ Stream.of("-private"),
+ Stream.of(additionalArguments != null ? additionalArguments : new String[]{})
+ )
+ .collect(Collectors.toList());
+
+ final DocumentationTool.DocumentationTask task = documentationTool.getTask(null, fileManager, null, NewXmlDoclet.class, options, compilationUnits);
+ task.call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // for debugging
+// System.out.println(marshalJAXB(NewXmlDoclet.root));
+ return NewXmlDoclet.root;
+ }
+
+ private static String marshalJAXB(Object jaxbObject) {
+ final StringWriter writer = new StringWriter();
+ JAXB.marshal(jaxbObject, writer);
+ return writer.toString();
+ }
+
/**
* Helper method to concat strings.
*