/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.build.docs.dsl.docbook;
import org.gradle.build.docs.dsl.source.TypeNameResolver;
import org.gradle.build.docs.dsl.source.model.ClassMetaData;
import org.gradle.build.docs.dsl.source.model.MethodMetaData;
import org.gradle.build.docs.dsl.source.model.TypeMetaData;
import org.gradle.build.docs.model.ClassMetaDataRepository;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Converts a javadoc link into docbook.
*/
public class JavadocLinkConverter {
private static final Pattern LINK_PATTERN = Pattern.compile("(?s)\\s*([\\w\\.]*)(#(\\w+)(\\((.*)\\))?)?.*");
private static final Pattern TYPE_PATTERN = Pattern.compile("(\\w+)\\s*(.*?)\\s*");
private static final Pattern PARAM_DELIMITER = Pattern.compile(",\\s*");
private final Document document;
private final TypeNameResolver typeNameResolver;
private final LinkRenderer linkRenderer;
private final ClassMetaDataRepository<ClassMetaData> repository;
public JavadocLinkConverter(Document document, TypeNameResolver typeNameResolver, LinkRenderer linkRenderer,
ClassMetaDataRepository<ClassMetaData> repository) {
this.document = document;
this.typeNameResolver = typeNameResolver;
this.linkRenderer = linkRenderer;
this.repository = repository;
}
/**
* Converts a javadoc link into docbook.
*/
public Node resolve(String link, ClassMetaData classMetaData, GenerationListener listener) {
Node node = doResolve(link, classMetaData, listener);
if (node != null) {
return node;
}
listener.warning(String.format("Could not convert Javadoc link '%s'", link));
Element element = document.createElement("UNHANDLED-LINK");
element.appendChild(document.createTextNode(link));
return element;
}
private Node doResolve(String link, ClassMetaData classMetaData, GenerationListener listener) {
Matcher matcher = LINK_PATTERN.matcher(link);
if (!matcher.matches()) {
return null;
}
String className = null;
if (matcher.group(1).length() > 0) {
className = typeNameResolver.resolve(matcher.group(1), classMetaData);
if (className == null) {
return null;
}
}
if (matcher.group(2) == null) {
return linkRenderer.link(new TypeMetaData(className), listener);
}
ClassMetaData targetClass;
if (className != null) {
targetClass = repository.find(className);
if (targetClass == null) {
return null;
}
} else {
targetClass = classMetaData;
}
String methodSignature = matcher.group(3);
if (matcher.group(5) != null) {
StringBuilder signature = new StringBuilder();
signature.append(methodSignature);
signature.append("(");
if (matcher.group(5).length() > 0) {
String[] types = PARAM_DELIMITER.split(matcher.group(5));
for (int i = 0; i < types.length; i++) {
String type = types[i];
Matcher typeMatcher = TYPE_PATTERN.matcher(type);
if (!typeMatcher.matches()) {
return null;
}
if (i > 0) {
signature.append(", ");
}
signature.append(typeNameResolver.resolve(typeMatcher.group(1), classMetaData));
String suffix = typeMatcher.group(2);
if (suffix.equals("...")) {
suffix = "[]";
}
signature.append(suffix);
}
}
signature.append(")");
methodSignature = signature.toString();
}
if (targetClass.isEnum() && targetClass.getEnumConstant(methodSignature) != null) {
return linkRenderer.link(targetClass.getEnumConstant(methodSignature), listener);
}
MethodMetaData method = findMethod(methodSignature, targetClass);
if (method == null) {
return null;
}
return linkRenderer.link(method, listener);
}
private MethodMetaData findMethod(String name, ClassMetaData targetClass) {
List<MethodMetaData> candidates = new ArrayList<MethodMetaData>();
for (MethodMetaData methodMetaData : targetClass.getDeclaredMethods()) {
if (name.equals(methodMetaData.getOverrideSignature())) {
return methodMetaData;
}
if (name.equals(methodMetaData.getName())) {
candidates.add(methodMetaData);
}
}
if (candidates.size() != 1) {
return null;
}
return candidates.get(0);
}
/**
* Converts a javadoc value link into docbook.
*/
public Node resolveValue(String fieldName, ClassMetaData classMetaData, GenerationListener listener) {
String[] parts = fieldName.split("#");
ClassMetaData targetClass;
if (parts[0].length() > 0) {
String targetClassName = typeNameResolver.resolve(parts[0], classMetaData);
targetClass = repository.find(targetClassName);
if (targetClass == null) {
listener.warning(String.format("Could not locate target class '%s' for field value link '%s'", targetClass, fieldName));
Element element = document.createElement("UNHANDLED-VALUE");
element.appendChild(document.createTextNode(targetClassName + ":" + parts[1]));
return element;
}
} else {
targetClass = classMetaData;
}
String value = targetClass.getConstants().get(parts[1]);
if (value == null) {
listener.warning(String.format("Field '%s' does not have any value", fieldName));
Element element = document.createElement("NO-VALUE-FOR_FIELD");
element.appendChild(document.createTextNode(targetClass.getClassName() + ":" + parts[1]));
return element;
}
return createLiteralNode(value);
}
private Node createLiteralNode(String value) {
Element element = document.createElement("literal");
element.appendChild(document.createTextNode(value));
return element;
}
}