blob: 88482b8725688400e79c71c4c2fc2174eaad71fe [file] [log] [blame]
package org.eclipse.ease.helpgenerator;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sun.javadoc.ClassDoc;
/**
* Collects registered packages and converts classes & API links to http
* anchors.
*/
public class LinkProvider {
/** Pattern to detect a link token. */
private static final Pattern PATTERN_LINK = Pattern.compile("\\{@(link|module)\\s+(.*?)\\}", Pattern.DOTALL);
/** Pattern to parse a link. */
private static final Pattern PATTERN_INNER_LINK = Pattern
.compile("(\\w+(?:\\.\\w+)*)?(?:#(\\w+)(?:\\((.*?)\\))?)?");
/** Maps (URL to use) -> Collection of package names. */
private final Map<String, Collection<String>> fExternalDocs = new HashMap<String, Collection<String>>();
public void registerAddress(final String location, final Collection<String> packages) {
fExternalDocs.put(location, packages);
}
public static String resolveClassName(final String candidate, final ClassDoc clazz) {
String foundCandidate = findClass(candidate, clazz);
return (foundCandidate != null) ? foundCandidate : candidate;
}
public Object createClassText(final String qualifiedName) {
if (qualifiedName.contains(".")) {
String urlLocation = findClassURL(qualifiedName);
if (urlLocation != null) {
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.'));
// first run, look for exact package match
for (final Entry<String, Collection<String>> entry : fExternalDocs.entrySet()) {
if (entry.getValue().contains(packageName))
return "<a href=\"" + urlLocation + "\" title=\"" + qualifiedName + "\">"
+ qualifiedName.substring(packageName.length() + 1) + "</a>";
}
// not found; try to locate matching parent package and hope for
// the best
for (final Entry<String, Collection<String>> entry : fExternalDocs.entrySet()) {
for (String entryPackage : entry.getValue()) {
if (packageName.startsWith(entryPackage))
return "<a href=\"" + urlLocation + "\" title=\"" + qualifiedName + "\">"
+ qualifiedName.substring(packageName.length() + 1) + "</a>";
}
}
}
}
return qualifiedName;
}
private static String findClass(final String name, final ClassDoc baseClass) {
try {
for (final ClassDoc doc : baseClass.importedClasses()) {
if (doc.toString().endsWith(name))
return doc.toString();
}
} catch (NullPointerException e) {
// sometimes thrown by ClassDoc.importedClasses(). Nothing we can do here but ignore
}
final ClassDoc target = baseClass.findClass(name);
return (target != null) ? target.toString() : null;
}
private String findClassURL(final String qualifiedName) {
if (qualifiedName.contains(".")) {
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.'));
// first run, look for exact package match
for (final Entry<String, Collection<String>> entry : fExternalDocs.entrySet()) {
if (entry.getValue().contains(packageName))
return entry.getKey() + "/" + qualifiedName.replace('.', '/') + ".html";
}
// not found; try to locate matching parent package and hope for the
// best
for (final Entry<String, Collection<String>> entry : fExternalDocs.entrySet()) {
for (String entryPackage : entry.getValue()) {
if (packageName.startsWith(entryPackage))
return entry.getKey() + "/" + qualifiedName.replace('.', '/') + ".html";
}
}
}
return null;
}
public String insertLinks(final ClassDoc clazz, final String text) {
final StringBuilder output = new StringBuilder();
int startPos = 0;
final Matcher matcher = PATTERN_LINK.matcher(text);
while (matcher.find()) {
output.append(text.substring(startPos, matcher.start()));
startPos = matcher.end();
final Matcher linkMatcher = PATTERN_INNER_LINK.matcher(matcher.group(2).replace('\r', ' ')
.replace('\n', ' '));
if (linkMatcher.matches()) {
// group 1 = class
// group 2 = method (optional)
// group 3 = params (without parenthesis)
if ("link".equals(matcher.group(1))) {
// link to java API
StringBuilder link = new StringBuilder();
if (linkMatcher.group(2) != null) {
link.append("#");
link.append(linkMatcher.group(2));
if (linkMatcher.group(3) != null) {
link.append("-");
for (String parameter : linkMatcher.group(3).split(",")) {
parameter = parameter.trim().replace(" ", "");
if (parameter.endsWith("]"))
link.append(findClass(parameter.substring(0, parameter.indexOf('[')), clazz));
else
link.append(findClass(parameter, clazz));
while (parameter.endsWith("]")) {
link.append(":A");
parameter = parameter.substring(0, parameter.lastIndexOf('[')).trim();
}
link.append("-");
}
if (link.charAt(link.length() - 1) != '-')
link.append("-");
}
}
if (linkMatcher.group(1) == null) {
// link to same document
output.append("<a href=\"" + link + "\">" + linkMatcher.group(2)
+ ((linkMatcher.group(3) != null) ? "(" + linkMatcher.group(3) + ")" : "") + "</a>");
} else {
// external document
String classURL = findClassURL(resolveClassName(linkMatcher.group(1), clazz));
if (classURL != null)
output.append("<a href=\"" + classURL + link + "\">");
output.append(linkMatcher.group(1));
if (linkMatcher.group(2) != null) {
output.append(linkMatcher.group(2));
if (linkMatcher.group(3) != null) {
output.append('(');
output.append(linkMatcher.group(3));
output.append(')');
}
}
if (classURL != null)
output.append("</a>");
}
} else if ("module".equals(matcher.group(1))) {
// link to a scripting module
if (linkMatcher.group(1) == null) {
// link to same document
output.append("<a href=\"#" + linkMatcher.group(2) + "\">" + linkMatcher.group(2)
+ ((linkMatcher.group(3) != null) ? "()" : "") + "</a>");
} else {
// external document
final String plugin = linkMatcher.group(1).substring(0, linkMatcher.group(1).lastIndexOf('.'));
if (linkMatcher.group(2) != null)
output.append("<a href=\"../../" + plugin + "/help/"
+ ModuleDoclet.createHTMLFileName(linkMatcher.group(1)) + "#"
+ linkMatcher.group(2) + "\">" + linkMatcher.group(2)
+ ((linkMatcher.group(3) != null) ? "()" : "") + "</a>");
else
output.append("<a href=\"../../"
+ plugin
+ "/help/"
+ ModuleDoclet.createHTMLFileName(linkMatcher.group(1))
+ "\">"
+ capitalizeFirst(linkMatcher.group(1).substring(
linkMatcher.group(1).lastIndexOf('.') + 1)) + " module</a>");
}
}
}
}
if (startPos == 0)
return text;
output.append(text.substring(startPos));
return output.toString();
}
private static String capitalizeFirst(final String content) {
if (!content.isEmpty())
return content.substring(0, 1).toUpperCase() + content.substring(1);
return content;
}
}