package com.webcohesion.enunciate.modules.docs; import com.webcohesion.enunciate.api.ApiRegistrationContext; import com.webcohesion.enunciate.api.ApiRegistry; import com.webcohesion.enunciate.api.datatype.DataType; import com.webcohesion.enunciate.api.datatype.Syntax; import com.webcohesion.enunciate.api.resources.Method; import com.webcohesion.enunciate.api.resources.ResourceApi; import com.webcohesion.enunciate.api.resources.ResourceGroup; import com.webcohesion.enunciate.api.services.Operation; import com.webcohesion.enunciate.api.services.Service; import com.webcohesion.enunciate.api.services.ServiceApi; import com.webcohesion.enunciate.javac.decorations.element.DecoratedElement; import com.webcohesion.enunciate.javac.decorations.element.DecoratedPackageElement; import com.webcohesion.enunciate.javac.decorations.element.DecoratedTypeElement; import com.webcohesion.enunciate.javac.javadoc.JavaDoc; import com.webcohesion.enunciate.javac.javadoc.JavaDocTagHandler; import javax.lang.model.element.PackageElement; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * @author Ryan Heaton */ public class ApiDocsJavaDocTagHandler implements JavaDocTagHandler { static final Pattern RAW_LINK_PATTERN = Pattern.compile("(?:^|[^>=\"'])(http.[^\"'<\\s]+)(?![^<>]*>|[^\"]*?<\\/a)"); private final ApiRegistry registry; private final ApiRegistrationContext context; public ApiDocsJavaDocTagHandler(ApiRegistry registry, ApiRegistrationContext context) { this.registry = registry; this.context = context; } @Override public String onInlineTag(String tagName, String tagText, DecoratedElement context) { if ("link".equals(tagName)) { tagText = tagText.trim(); int fragmentStart = tagText.indexOf('#'); //the start index of where we need to start looking for the value. int fragmentEnd = fragmentStart; int firstParen = -1; if (fragmentStart >= 0) { //if there's a '#' char, we have to check for a left-right paren pair before checking for the space. firstParen = tagText.indexOf('(', fragmentStart); if (firstParen >= 0) { fragmentEnd = tagText.indexOf(')', firstParen); } } String value = tagText; int valueStart = JavaDoc.indexOfWhitespaceFrom(tagText, fragmentStart < 0 ? 0 : fragmentEnd); if (valueStart >= 0 && valueStart + 1 < tagText.length()) { value = tagText.substring(valueStart + 1, tagText.length()); } String classRef = ""; String subelementRef = ""; if (fragmentStart > 0) { classRef = tagText.substring(0, fragmentStart).trim(); if (firstParen >= 0) { subelementRef = tagText.substring(fragmentStart + 1, firstParen).trim(); } else if (valueStart >= 0) { subelementRef = tagText.substring(fragmentStart + 1, valueStart).trim(); } } else if (valueStart > 0){ classRef = tagText.substring(0, valueStart).trim(); } else { classRef = tagText; } //use the current context as the class ref. if ("".equals(classRef)){ DecoratedElement type = context; while (!(type instanceof DecoratedTypeElement)) { type = (DecoratedElement) type.getEnclosingElement(); if (type == null || type instanceof PackageElement) { break; } } if (type instanceof DecoratedTypeElement) { classRef = ((DecoratedTypeElement) type).getQualifiedName().toString(); } } if (!"".equals(classRef)) { if (classRef.indexOf('.') < 0) { //if it's a local reference, assume it's in the current package. DecoratedElement pckg = context; while (!(pckg instanceof DecoratedPackageElement)) { pckg = (DecoratedElement) pckg.getEnclosingElement(); if (pckg == null) { break; } } if (pckg != null) { classRef = ((DecoratedPackageElement) pckg).getQualifiedName() + "." + classRef; } } //now find the reference Set<Syntax> syntaxes = this.registry.getSyntaxes(this.context); for (Syntax syntax : syntaxes) { List<DataType> dataTypes = syntax.findDataTypes(classRef); if (dataTypes != null && !dataTypes.isEmpty()) { if (value.equals(tagText)) { value = dataTypes.get(0).getLabel(); } return "<a href=\"" + dataTypes.get(0).getSlug() + ".html\">" + value + "</a>"; } } List<ResourceApi> resourceApis = this.registry.getResourceApis(this.context); for (ResourceApi resourceApi : resourceApis) { Method method = resourceApi.findMethodFor(classRef, subelementRef); if (method != null) { if (value.equals(tagText)) { value = method.getLabel() + " " + method.getResource().getGroup().getLabel(); } return "<a href=\"" + method.getResource().getGroup().getSlug() + ".html#" + method.getSlug() + "\">" + value + "</a>"; } else { ResourceGroup resourceGroup = resourceApi.findResourceGroupFor(classRef); if (resourceGroup != null) { if (value.equals(tagText)) { value = resourceGroup.getLabel(); } return "<a href=\"" + resourceGroup.getSlug() + ".html\">" + value + "</a>"; } } } List<ServiceApi> serviceApis = this.registry.getServiceApis(this.context); for (ServiceApi serviceApi : serviceApis) { Operation operation = serviceApi.findOperationFor(classRef, subelementRef); if (operation != null) { if (value.equals(tagText)) { value = operation.getName(); } return "<a href=\"" + operation.getService().getSlug() + ".html#" + operation.getSlug() + "\">" + value + "</a>"; } else { Service service = serviceApi.findServiceFor(classRef); if (service != null) { if (value.equals(tagText)) { value = service.getLabel(); } return "<a href=\"" + service.getSlug() + ".html\">" + value + "</a>"; } } } } return value; } return tagText; } @Override public String onBlockTag(String tagName, String value, DecoratedElement context) { if ("see".equals(tagName)) { if (value.startsWith("\"")) { return value; } else if (value.startsWith("<")) { return value; } else if (value.startsWith("http")) { return "<a target=\"_blank\" href=\"" + value + "\">" + value + "</a>"; } else { //process 'see' block tags as if they were 'link' inline tags. return onInlineTag("link", value, context); } } else { return RAW_LINK_PATTERN.matcher(value).replaceAll(" <a target=\"_blank\" href=\"$1\">$1</a>"); } } }