/* Copyright 2011 Patrick Meredith
* getPackageDoc, getTopLevelClassDoc, findClass, most of buildRelativeUrl Copyright 2010 Chad Retz
*
* 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 edu.uiuc.cs.fsl.propertydocs.util;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.Taglet;
public class GenerateUrls {
public static PackageDoc getPackageDoc(Tag tag) {
Doc holder = tag.holder();
if (holder instanceof ProgramElementDoc) {
return ((ProgramElementDoc) holder).containingPackage();
} else if (holder instanceof PackageDoc) {
return (PackageDoc) holder;
} else {
throw new RuntimeException("Unrecognized holder: " + holder);
}
}
private static ClassDoc getTopLevelClassDoc(ClassDoc classDoc, int depth) {
if (classDoc.containingClass() == null) {
return classDoc;
}
if (classDoc.containingClass().equals(classDoc)) {
return classDoc;
}
if (depth < 30){ //something is wonky here, not sure why I am getting infinite recursion
//my guess is .equals is not properly implemented
return getTopLevelClassDoc(classDoc, depth + 1);
}
else {
return classDoc;
}
}
private static ClassDoc getTopLevelClassDoc(Tag tag, int depth) {
Doc holder = tag.holder();
if (holder instanceof PackageDoc) {
return null;
} else if (holder instanceof ClassDoc) {
return getTopLevelClassDoc((ClassDoc) holder, depth);
} else if (holder instanceof ProgramElementDoc) {
return getTopLevelClassDoc(((ProgramElementDoc) holder)
.containingClass(), depth);
} else {
throw new RuntimeException("Unrecognized holder: " + holder);
}
}
private static ClassDoc findClass(String className, ClassDoc[] classImports) {
for (ClassDoc classDoc : classImports) {
if (classDoc.name().equals(className)) {
return classDoc;
}
}
return null;
}
private static ClassDoc findClass(String className, PackageDoc... packageImports) {
for (PackageDoc packageDoc : packageImports) {
for (ClassDoc found : packageDoc.allClasses(true)) {
if (found.name().equals(className)) {
return found;
}
}
}
return null;
}
public static String buildRelativeUrl(Tag tag){
PackageDoc packageDoc = getPackageDoc(tag);
ClassDoc topLevelClassDoc = getTopLevelClassDoc(tag,0);
StringBuilder href = new StringBuilder();
//here we are attempting to find the root directory of the
//generated documents so that we can add the proper number of ".."
//to any generated links
int dotIndex = packageDoc.name().indexOf('.');
while (dotIndex != -1) {
href.append("../");
dotIndex = packageDoc.name().indexOf('.', dotIndex + 1);
}
//package name is empty when it is the root package
if (!(packageDoc.name().length() == 0)) {
href.append("../");
}
return href.toString();
}
//Anything that gets its own webpage in javadoc
private static String classNameToUrl(String name){
return name.replaceAll("[.]","/") + ".html";
}
//this is for turning a method to a url.
//this will generate a url that points to the middle of a webpage somewhere
private static String methodNameToUrl(String name){
String[] toplevel = name.split("[(]");
String[] pieces = toplevel[0].split("[.]");
StringBuilder ret = new StringBuilder();
{ int i = 0;
for(; i < pieces.length - 2; ++i){
ret.append(pieces[i]);
ret.append("/");
}
ret.append(pieces[i]);
}
ret.append(".html#");
ret.append(pieces[pieces.length - 1]);
ret.append("(");
ret.append(toplevel[1]);
return ret.toString();
}
//this is for class elements that are not methods.
//these are easier to handle because they do not have possible
//arguments, as arguments to methods do NOT have their "."'s changed
//to "/". Everything is convoluted here, /sigh
private static String elementNameToUrl(String name){
String[] pieces = name.split("[.]");
StringBuilder ret = new StringBuilder();
for(int i = 0; i < pieces.length - 2; ++i){
ret.append(pieces[i]);
ret.append("/");
}
ret.append(pieces[pieces.length - 2]);
ret.append(".html#");
ret.append(pieces[pieces.length - 1]);
return ret.toString();
}
public static String getUrl(Tag tag){
Doc doc = tag.holder();
String name = doc.toString();
//remove anything between nested < and >
{
StringBuilder nameFixer = new StringBuilder();
int nestedDepth = 0;
for(int i = 0; i < name.length(); ++i){
char c = name.charAt(i);
if(c == '<'){
++nestedDepth;
}
else if(c == '>'){
--nestedDepth;
}
else if(nestedDepth == 0){
nameFixer.append(c);
}
}
name = nameFixer.toString();
}
//If the document is a class, interface, or annotation it has its
//own file, and the url to the file is constructed by classNameToUrl
if(doc.isClass() || doc.isInterface() || doc.isAnnotationType()
|| doc.isError() || doc.isException()){
return classNameToUrl(name);
}
//Methods must be handled specially
if(doc.isMethod() || doc.isConstructor() || doc.isAnnotationTypeElement()){
return methodNameToUrl(name);
}
//Non-method elements, such as fields, are handled with a simpler method:
//elementNameToUrl
if(doc.isField() || doc.isEnumConstant()){
return elementNameToUrl(name);
}
throw new RuntimeException("Type of tag: " + tag + " could not be handled!");
}
private static String[] getUrlAndName(Tag tag){
String text = tag.text().trim();
//I'm tired of java and it's uninitialized ERROR. Make it a warning. Total dumbassery
String urlPart, namePart = null;
String[] args = text.split("\\s+");
urlPart = args[0];
if(args.length == 1) namePart = text;
else if(args.length > 1) {
StringBuilder nameBuilder = new StringBuilder();
for(int i = 1; i < args.length; ++i){
nameBuilder.append(args[i] + " ");
}
namePart = nameBuilder.toString().trim();
}
String[] ret = new String[3];
ret[0] = buildRelativeUrl(tag);
ret[1] = classNameToUrl(urlPart);
ret[2] = namePart;
return ret;
}
public static String processLinkTag(Tag tag){
if(!tag.name().equals("@link")) throw new RuntimeException("Not a link tag: " + tag.name());
String[] urlAndName = getUrlAndName(tag);
return "<A HREF=\"" + urlAndName[0] + urlAndName[1] + "\"><CODE>" + urlAndName[2] + "</CODE></A>";
}
public static String processLinkPlainTag(Tag tag){
if(!tag.name().equals("@linkplain")) throw new RuntimeException("Not a linkplain tag" + tag.name());
String[] urlAndName = getUrlAndName(tag);
return "<A HREF=\"" + urlAndName[0] + urlAndName[1] + "\">" + urlAndName[2] + "</A>";
}
public static String processPropertyLinkTag(Tag tag){
if(!tag.name().equals("@property.link"))
throw new RuntimeException("Not a property.link tag" + tag.name());
String[] urlAndName = getUrlAndName(tag);
return "<A HREF=\"" + urlAndName[0] + "__properties/" + urlAndName[1] + "\">" + urlAndName[2] + "</A>";
}
public static String processPropertyShortcutTag(Tag tag){
if(!tag.name().equals("@property.shortcut"))
throw new RuntimeException("Not a property.shortcut tag" + tag.name());
String[] urlAndName = getUrlAndName(tag);
return "<A HREF=\\'" + urlAndName[0] + "__properties/" + urlAndName[1] + "\\'>" + urlAndName[2] + "</A>";
}
}