All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.tools.ajdoc.HtmlDecorator Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * Copyright (c) 1999-2001 Xerox Corporation,
 *               2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *     Xerox/PARC     initial implementation
 *     Mik Kersten	  port to AspectJ 1.1+ code base
 * ******************************************************************/

package org.aspectj.tools.ajdoc;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.aspectj.asm.AsmManager;
import org.aspectj.asm.HierarchyWalker;
import org.aspectj.asm.IProgramElement;
import org.aspectj.asm.IRelationship;
import org.aspectj.util.LangUtil;
import org.aspectj.util.TypeSafeEnum;

/**
 * @author Mik Kersten
 */
class HtmlDecorator {

	public static final String TYPE_NAME_LABEL;
	public static final String CLOSING_SPAN;

	static {
		if (LangUtil.is16VMOrGreater())
			TYPE_NAME_LABEL = "element-name type-name-label";
		else if (LangUtil.is15VMOrGreater())
			TYPE_NAME_LABEL = "type-name-label";
		else if (LangUtil.is1dot8VMOrGreater())
			TYPE_NAME_LABEL = "typeNameLabel";
		else
			TYPE_NAME_LABEL = "strong";

		if (LangUtil.is16VMOrGreater())
			CLOSING_SPAN = "";
				else
			CLOSING_SPAN = "";
	}

	private static final String POINTCUT_DETAIL = "Pointcut Detail";
	private static final String ADVICE_DETAIL = "Advice Detail";
	private static final String DECLARE_DETAIL = "Declare Detail";
	private static final String ADVICE_SUMMARY = "Advice Summary";
	private static final String POINTCUT_SUMMARY = "Pointcut Summary";
	private static final String DECLARE_SUMMARY = "Declare Summary";
	private static final String ITD_METHOD_SUMMARY = "Inter-Type Method Summary";
	private static final String ITD_FIELD_SUMMARY = "Inter-Type Field Summary";
	private static final String ITD_CONSTRUCTOR_SUMMARY = "Inter-Type Constructor Summary";

	static List visibleFileList = new ArrayList<>();
	static Hashtable declIDTable = null;
	static File rootDir = null;
	static String docVisibilityModifier;

	static void decorateHTMLFromInputFiles(AsmManager model, Hashtable table, File newRootDir, File[] inputFiles, String docModifier)
			throws IOException {
		rootDir = newRootDir;
		declIDTable = table;
		docVisibilityModifier = docModifier;
		for (File inputFile : inputFiles) {
			decorateHTMLFromIPEs(getProgramElements(model, inputFile.getCanonicalPath()), rootDir.getCanonicalPath()
					+ Config.DIR_SEP_CHAR, docModifier, false);
		}
	}

	static void decorateHTMLFromIPEs(IProgramElement[] decls, String base, String docModifier, boolean exceededNestingLevel)
			throws IOException {
		if (decls != null) {
			for (IProgramElement decl : decls) {
				decorateHTMLFromIPE(decl, base, docModifier, exceededNestingLevel);
			}
		}
	}

	/**
	 * Before attempting to decorate the HTML file we have to verify that it exists, which depends on the documentation visibility
	 * specified to c.
	 *
	 * Depending on docModifier, can document - public: only public - protected: protected and public (default) - package: package
	 * protected and public - private: everything
	 */
	static void decorateHTMLFromIPE(IProgramElement decl, String base, String docModifier, boolean exceededNestingLevel)
			throws IOException {
		boolean nestedClass = false;
		if (decl.getKind().isType()) {
			boolean decorateFile = true;
			if (isAboveVisibility(decl)) {
				visibleFileList.add(decl.toSignatureString());
				String packageName = decl.getPackageName();
				String filename = "";
				if (packageName != null) {

					int index1 = base.lastIndexOf(Config.DIR_SEP_CHAR);
					int index2 = base.lastIndexOf(".");
					String currFileClass = "";
					if (index1 > -1 && index2 > 0 && index1 < index2) {
						currFileClass = base.substring(index1 + 1, index2);
					}

					// XXX only one level of nexting
					if (currFileClass.equals(decl.getDeclaringType())) {
						nestedClass = true;
						packageName = packageName.replace('.', '/');
						String newBase = "";
						if (base.lastIndexOf(Config.DIR_SEP_CHAR) > 0) {
							newBase = base.substring(0, base.lastIndexOf(Config.DIR_SEP_CHAR));
						}
						String signature = constructNestedTypeName(decl);

						filename = newBase + Config.DIR_SEP_CHAR + packageName + Config.DIR_SEP_CHAR + currFileClass + // "." +
								signature + ".html";
					} else {
						packageName = packageName.replace('.', '/');
						filename = base + packageName + Config.DIR_SEP_CHAR + decl.toSignatureString() + ".html";
					}
				} else {
					filename = base + decl.toSignatureString() + ".html";
				}
				if (!exceededNestingLevel) {
					decorateHTMLFile(new File(filename));
				} else {
					System.out.println("Warning: can not generate documentation for nested " + "inner class: "
							+ decl.toSignatureString());
				}
			}
		}
	}

	private static String constructNestedTypeName(IProgramElement node) {
		if (node.getParent().getKind().isSourceFile()) {
			return node.getName();
		} else {
			String nodeName = "";
			if (node.getKind().isType())
				nodeName += '.' + node.getName();
			return constructNestedTypeName(node.getParent()) + nodeName;
		}
	}

	/**
	 * Skips files that are public in the model but not public in the source, e.g. nested aspects.
	 */
	static void decorateHTMLFile(File file) throws IOException {
		if (!file.exists())
			return;

		System.out.println("> Decorating " + file.getCanonicalPath() + "...");
		BufferedReader reader = new BufferedReader(new FileReader(file));

		StringBuilder fileContents = new StringBuilder();
		String line = reader.readLine();
		while (line != null) {
			fileContents.append(line + "\n");
			line = reader.readLine();
		}

		boolean isSecond = false;
		int index = 0;
		IProgramElement decl;
		while (true) {

			// ---this next part is an inlined procedure that returns two values---
			// ---the next declaration and the index at which that declaration's---
			// ---DeclID sits in the .html file ---
			String contents = fileContents.toString();
			int start = contents.indexOf(Config.DECL_ID_STRING, index);
			int end = contents.indexOf(Config.DECL_ID_TERMINATOR, index);
			if (start == -1)
				decl = null;
			else if (end == -1)
				throw new Error("Malformed DeclID.");
			else {
				String tid = contents.substring(start + Config.DECL_ID_STRING.length(), end);
				decl = (IProgramElement) declIDTable.get(tid);
				index = start;
			}
			// --- ---
			// --- ---

			if (decl == null)
				break;
			fileContents.delete(start, end + Config.DECL_ID_TERMINATOR.length());
			if (decl.getKind().isType()) {
				isSecond = true;
				String fullname = "";
				if (decl.getParent().getKind().equals(IProgramElement.Kind.ASPECT)
						|| decl.getParent().getKind().equals(IProgramElement.Kind.CLASS)) {
					fullname += decl.getParent().toSignatureString().concat(".").concat(decl.toSignatureString());
				} else {
					fullname += decl.toSignatureString();
				}
				// only add aspect documentation if we're in the correct
				// file for the given IProgramElement
				if (file.getName().contains(fullname + ".html")) {
					addAspectDocumentation(decl, fileContents, index);
				}
			} else {
				decorateMemberDocumentation(decl, fileContents, index);
			}
			// Change "Class" to "Aspect"
			// moved this here because then can use the IProgramElement.Kind
			// rather than checking to see if there's advice - this fixes
			// the case with an inner aspect not having the title "Aspect"
			if (decl.getKind().equals(IProgramElement.Kind.ASPECT) && file.getName().contains(decl.toSignatureString())) {
				// only want to change "Class" to "Aspect" if we're in the
				// file corresponding to the IProgramElement
				String fullname = "";
				if (decl.getParent().getKind().equals(IProgramElement.Kind.ASPECT)
						|| decl.getParent().getKind().equals(IProgramElement.Kind.CLASS)) {
					fullname += decl.getParent().toSignatureString().concat(".").concat(decl.toSignatureString());
				} else {
					fullname += decl.toSignatureString();
				}
				if (!file.getName().contains(fullname + ".html")) {
					// we're still in the file for a parent IPE
					continue;
				}

				boolean br = true;
				contents = fileContents.toString();
				int classStartIndex = contents.indexOf("
\nClass "); if (classStartIndex == -1) { classStartIndex = contents.indexOf("

\nClass "); br = false; } if (classStartIndex == -1) { // Java8 looks more like this: //

Class A

classStartIndex = contents.indexOf("

", classStartIndex); if (classStartIndex == -1) { // Java 13 - replaced h2 with h1 here classStartIndex = contents.indexOf("

", classStartIndex); } if (classEndIndex != -1) { // Convert it to "

Aspect A

" String classLine = contents.substring(classStartIndex, classEndIndex); String aspectLine = classLine.replaceAll("Class ","Aspect "); fileContents.delete(classStartIndex, classEndIndex); fileContents.insert(classStartIndex, aspectLine); } } else if (classStartIndex != -1) { contents = fileContents.toString(); int classEndIndex = contents.indexOf("", classStartIndex); if (classEndIndex != -1) { String classLine = contents.substring(classStartIndex, classEndIndex); String aspectLine = ""; if (br) { aspectLine += "
\n" + "Aspect " + classLine.substring(11, classLine.length()); } else { aspectLine += "

\n" + "Aspect " + classLine.substring(11, classLine.length()); } fileContents.delete(classStartIndex, classEndIndex); fileContents.insert(classStartIndex, aspectLine); } } contents = fileContents.toString(); int secondClassStartIndex = contents.indexOf("class "); if (secondClassStartIndex != -1) { String name = decl.toSignatureString(); int classEndIndex = contents.indexOf(name + "
"); if (classEndIndex != -1) { StringBuilder sb = new StringBuilder(contents.substring(secondClassStartIndex, classEndIndex)); sb.replace(0, 5, "aspect"); fileContents.delete(secondClassStartIndex, classEndIndex); fileContents.insert(secondClassStartIndex, sb.toString()); } } else { contents = fileContents.toString(); // Java16: static class ClassA.InnerAspect // Java15:
static class ClassA.InnerAspect
					// Java8: 
static class ClassA.InnerAspect
					// Java7 (464604): 
public class Azpect
					String startString = "class " + CLOSING_SPAN + "");
					int classEndIndex = contents.indexOf("", classStartIndex + startString.length());

					// This is where after Java version upgrades usually tests fail or the first time.
					// Logging context information helps fixing the issue quickly.
					if (classStartIndex == -1 || classEndIndex == -1) {
						System.out.println(
							"Something unexpected went wrong in HtmlDecorator. Here is the full file causing the problem:\n\n" +
								"------------------------------------------------------------------------\n\n" +
								contents + "\n" +
								"------------------------------------------------------------------------\n"
						);
					}

					if (classEndIndex != -1) {
						// Convert it to "aspect ClassA.InnerAspect"
						String aspectLine = "aspect" + contents.substring(classStartIndex + 5, classEndIndex);
						fileContents.delete(classStartIndex, classEndIndex);
						fileContents.insert(classStartIndex, aspectLine);
					}
				}
			}
		}
		file.delete();
		FileOutputStream fos = new FileOutputStream(file);
		fos.write(fileContents.toString().getBytes());

		reader.close();
		fos.close();
	}

	static void addAspectDocumentation(IProgramElement node, StringBuilder fileBuffer, int index) {
		List pointcuts = new ArrayList<>();
		List advice = new ArrayList<>();
		List declares = new ArrayList<>();
		List methodsDeclaredOn = StructureUtil.getDeclareInterTypeTargets(node, IProgramElement.Kind.INTER_TYPE_METHOD);
		if (methodsDeclaredOn != null && !methodsDeclaredOn.isEmpty()) {
			insertDeclarationsSummary(fileBuffer, methodsDeclaredOn, ITD_METHOD_SUMMARY, index);
		}
		List fieldsDeclaredOn = StructureUtil.getDeclareInterTypeTargets(node, IProgramElement.Kind.INTER_TYPE_FIELD);
		if (fieldsDeclaredOn != null && !fieldsDeclaredOn.isEmpty()) {
			insertDeclarationsSummary(fileBuffer, fieldsDeclaredOn, ITD_FIELD_SUMMARY, index);
		}
		List constDeclaredOn = StructureUtil.getDeclareInterTypeTargets(node, IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR);
		if (fieldsDeclaredOn != null && !constDeclaredOn.isEmpty()) {
			insertDeclarationsSummary(fileBuffer, constDeclaredOn, ITD_CONSTRUCTOR_SUMMARY, index);
		}
		for (IProgramElement member : node.getChildren()) {
			if (member.getKind().equals(IProgramElement.Kind.POINTCUT)) {
				pointcuts.add(member);
			} else if (member.getKind().equals(IProgramElement.Kind.ADVICE)) {
				advice.add(member);
			} else if (member.getKind().isDeclare() || member.getKind().isInterTypeMember()) {
				declares.add(member);
			}
		}
		if (declares.size() > 0) {
			insertDeclarationsDetails(fileBuffer, declares, DECLARE_DETAIL, index);
			insertDeclarationsSummary(fileBuffer, declares, DECLARE_SUMMARY, index);
		}
		if (pointcuts.size() > 0) {
			insertDeclarationsSummary(fileBuffer, pointcuts, POINTCUT_SUMMARY, index);
			insertDeclarationsDetails(fileBuffer, pointcuts, POINTCUT_DETAIL, index);
		}
		if (advice.size() > 0) {
			insertDeclarationsSummary(fileBuffer, advice, ADVICE_SUMMARY, index);
			insertDeclarationsDetails(fileBuffer, advice, ADVICE_DETAIL, index);
		}
		// add the 'aspect declarations' information against the type
		List parentsDeclaredOn = StructureUtil.getDeclareInterTypeTargets(node, IProgramElement.Kind.DECLARE_PARENTS);
		if (parentsDeclaredOn != null && parentsDeclaredOn.size() > 0) {
			decorateDocWithRel(node, fileBuffer, index, parentsDeclaredOn, HtmlRelationshipKind.ASPECT_DECLARATIONS);
		}
		// add the 'annotated by' information against the type
		List annotatedBy = StructureUtil.getTargets(node, IRelationship.Kind.DECLARE_INTER_TYPE, "annotated by");
		if (annotatedBy != null && annotatedBy.size() > 0) {
			decorateDocWithRel(node, fileBuffer, index, annotatedBy, HtmlRelationshipKind.ANNOTATED_BY);
		}
		// add the 'advised by' information against the type
		List advisedBy = StructureUtil.getTargets(node, IRelationship.Kind.ADVICE);
		if (advisedBy != null && advisedBy.size() > 0) {
			decorateDocWithRel(node, fileBuffer, index, advisedBy, HtmlRelationshipKind.ADVISED_BY);
		}
	}

	static void insertDeclarationsSummary(StringBuilder fileBuffer, List decls, String kind, int index) {
		if (!declsAboveVisibilityExist(decls))
			return;

		int insertIndex = findSummaryIndex(fileBuffer, index);

		// insert the head of the table
		String tableHead = "\n\n"
				+ "\n";
		fileBuffer.insert(insertIndex, tableHead);
		insertIndex += tableHead.length();

		// insert the body of the table
		for (Object o : decls) {
			IProgramElement decl = (IProgramElement) o;
			if (isAboveVisibility(decl)) {
				// insert the table row accordingly
				String comment = generateSummaryComment(decl);
				String entry = "";
				if (kind.equals(ADVICE_SUMMARY)) {
					entry += "" + "\n"
							+ "\n";
				} else if (kind.equals(DECLARE_SUMMARY)) {
					entry += "" + "" + "
" + "" + kind + "
" + "" + "" + generateSignatures(decl) + "
 "; if (!comment.equals("")) { entry += comment + "

"; } entry += generateAffects(decl) + "

\n"; } else if (kind.equals(POINTCUT_SUMMARY)) { entry += "
" + "" + genAccessibility(decl) + "" + "" + "" + decl.toLabelString() + "
 "; if (!comment.equals("")) { entry += comment + "

"; } entry += "

" + "" + generateModifierInformation(decl, false) + "" + "" + "" + "" + decl.toLabelString() + "

" + generateAffects(decl); } else if (kind.equals(ITD_FIELD_SUMMARY) || kind.equals(ITD_METHOD_SUMMARY)) { entry += "

" + "" + generateModifierInformation(decl, false) + "" + "" + "" + "" + decl.toLabelString() + "

" + generateDeclaredBy(decl); } else if (kind.equals(ITD_CONSTRUCTOR_SUMMARY)) { entry += "

" + "" + "" + decl.toLabelString() + "

" + generateDeclaredBy(decl); } // insert the entry fileBuffer.insert(insertIndex, entry); insertIndex += entry.length(); } } // insert the end of the table String tableTail = "

 \n"; fileBuffer.insert(insertIndex, tableTail); insertIndex += tableTail.length(); } private static boolean declsAboveVisibilityExist(List decls) { boolean exist = false; for (Object decl : decls) { IProgramElement element = (IProgramElement) decl; if (isAboveVisibility(element)) exist = true; } return exist; } private static boolean isAboveVisibility(IProgramElement element) { IProgramElement.Accessibility acc = element.getAccessibility(); if (docVisibilityModifier.equals("private")) { // show all classes and members return true; } else if (docVisibilityModifier.equals("package")) { // show package, protected and public classes and members return acc.equals(IProgramElement.Accessibility.PACKAGE) || acc.equals(IProgramElement.Accessibility.PROTECTED) || acc.equals(IProgramElement.Accessibility.PUBLIC); } else if (docVisibilityModifier.equals("protected")) { // show protected and public classes and members return acc.equals(IProgramElement.Accessibility.PROTECTED) || acc.equals(IProgramElement.Accessibility.PUBLIC); } else if (docVisibilityModifier.equals("public")) { // show public classes and members return acc.equals(IProgramElement.Accessibility.PUBLIC); } return false; } private static String genAccessibility(IProgramElement decl) { if (decl.getAccessibility().equals(IProgramElement.Accessibility.PACKAGE)) { return "(package private)"; } else { return decl.getAccessibility().toString(); } } static void insertDeclarationsDetails(StringBuilder fileBuffer, List decls, String kind, int index) { if (!declsAboveVisibilityExist(decls)) return; int insertIndex = findDetailsIndex(fileBuffer, index); // insert the table heading String detailsHeading = "

 \n" + "\n\n" + "

\n" + "\n" + "\n" + "\n" + "
\n" + "" + kind + "
"; fileBuffer.insert(insertIndex, detailsHeading); insertIndex += detailsHeading.length(); // insert the details for (int i = 0; i < decls.size(); i++) { IProgramElement decl = (IProgramElement) decls.get(i); if (isAboveVisibility(decl)) { String entry = ""; // insert the table row accordingly entry += "\n"; if (kind.equals(ADVICE_DETAIL)) { entry += "

" + decl.getName() + "

"; entry += "" + generateSignatures(decl) + "\n" + "

" + generateDetailsComment(decl) + "

" + generateAffects(decl); } else if (kind.equals(POINTCUT_DETAIL)) { entry += "

" + decl.toLabelString() + "

" + generateDetailsComment(decl); } else if (kind.equals(DECLARE_DETAIL)) { entry += "

" + decl.toLabelString() + "

" + generateModifierInformation(decl, true); if (!decl.getKind().equals(IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR)) { entry += "  "; } // if we're not a declare statement then we need to generate the signature. // If we did this for declare statements we get two repeated lines if (!decl.getKind().isDeclare()) { String sigs = generateSignatures(decl); entry += sigs + "

"; } entry += generateAffects(decl) + generateDetailsComment(decl); } // insert the entry if (i != decls.size() - 1) { entry += "


\n"; } else { entry += "

"; } fileBuffer.insert(insertIndex, entry); insertIndex += entry.length(); } } } /** * TODO: don't place the summary first. */ static int findSummaryIndex(StringBuilder fileBuffer, int index) { String fbs = fileBuffer.toString(); String MARKER_1 = ""; String MARKER_2 = ""; int index1 = fbs.indexOf(MARKER_1, index); int index2 = fbs.indexOf(MARKER_2, index); if (index1 < index2 && index1 != -1) { return index1; } else if (index2 != -1) { return index2; } else { return index; } } static int findDetailsIndex(StringBuilder fileBuffer, int index) { String fbs = fileBuffer.toString(); String MARKER_1 = ""; String MARKER_2 = ""; String MARKER_3 = ""; int index1 = fbs.indexOf(MARKER_1, index); int index2 = fbs.indexOf(MARKER_2, index); int index3 = fbs.indexOf(MARKER_3, index); if (index1 != -1 && index1 < index2 && index1 < index3) { return index1; } else if (index2 != -1 && index2 < index1 && index2 < index3) { return index2; } else if (index3 != -1) { return index3; } else { return index; } } static void decorateDocWithRel(IProgramElement node, StringBuilder fileContentsBuffer, int index, List targets, HtmlRelationshipKind relKind) { if (targets != null && !targets.isEmpty()) { StringBuilder adviceDoc = new StringBuilder("

" + "
" + relKind.toString() + ""); String relativePackagePath = getRelativePathFromHere(node.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR); List addedNames = new ArrayList(); for (Iterator it = targets.iterator(); it.hasNext();) { Object o = it.next(); IProgramElement currDecl = null; if (o instanceof String) { String currHandle = (String) o; currDecl = node.getModel().getHierarchy().findElementForHandle(currHandle); } else if (o instanceof IProgramElement) { currDecl = (IProgramElement) o; } else { return; } String packagePath = ""; if (currDecl.getPackageName() != null && !currDecl.getPackageName().equals("")) { packagePath = currDecl.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR; } String hrefName = ""; String hrefLink = ""; // Start the hRefLink with the relative path based on where // *this* type (i.e. the advised) is in the package structure. hrefLink = relativePackagePath + packagePath; if (currDecl.getPackageName() != null) { hrefName = currDecl.getPackageName().replace('.', '/'); } // in the case of nested classes, in order for the links to work, // need to have the correct file name which is something of the // form parentClass.nestedAspect.html List names = new ArrayList(); IProgramElement parent = currDecl; while (parent != null && parent.getParent() != null && (!parent.getParent().getKind().equals(IProgramElement.Kind.FILE_JAVA) && !parent.getParent().getKind() .equals(IProgramElement.Kind.FILE_ASPECTJ))) { parent = parent.getParent(); names.add(parent.toLinkLabelString()); } StringBuilder sbuff = new StringBuilder(); for (int i = names.size() - 1; i >= 0; i--) { String element = (String) names.get(i); if (i == 0) { sbuff.append(element); } else { sbuff.append(element + "."); } } // use the currDecl.toLabelString rather than currDecl.getName() // because two distinct advice blocks can have the same // currDecl.getName() and wouldn't both appear in the ajdoc hrefName += Config.DIR_SEP_CHAR + sbuff.toString() + "." + currDecl.toLabelString(); // need to replace " with quot; otherwise the links wont work // for 'matches declare' relationship StringBuilder sb = new StringBuilder(currDecl.toLabelString()); int nextQuote = sb.toString().indexOf("\""); while (nextQuote != -1) { sb.deleteCharAt(nextQuote); sb.insert(nextQuote, "quot;"); nextQuote = sb.toString().indexOf("\""); } hrefLink += sbuff.toString() + ".html" + "#" + sb.toString(); if (!addedNames.contains(hrefName)) { adviceDoc.append("").append(hrefName.replace('/', '.')).append(""); if (it.hasNext()) adviceDoc.append(", "); addedNames.add(hrefName); } } adviceDoc.append("
\n"); fileContentsBuffer.insert(index, adviceDoc.toString()); } } static void decorateMemberDocumentation(IProgramElement node, StringBuilder fileContentsBuffer, int index) { List targets = StructureUtil.getTargets(node, IRelationship.Kind.ADVICE); decorateDocWithRel(node, fileContentsBuffer, index, targets, HtmlRelationshipKind.ADVISED_BY); List warnings = StructureUtil.getTargets(node, IRelationship.Kind.DECLARE, "matches declare"); decorateDocWithRel(node, fileContentsBuffer, index, warnings, HtmlRelationshipKind.MATCHES_DECLARE); List softenedBy = StructureUtil.getTargets(node, IRelationship.Kind.DECLARE, "softened by"); decorateDocWithRel(node, fileContentsBuffer, index, softenedBy, HtmlRelationshipKind.SOFTENED_BY); List annotatedBy = StructureUtil.getTargets(node, IRelationship.Kind.DECLARE_INTER_TYPE, "annotated by"); decorateDocWithRel(node, fileContentsBuffer, index, annotatedBy, HtmlRelationshipKind.ANNOTATED_BY); } /** * pr119453 - adding "declared by" relationship */ static String generateDeclaredBy(IProgramElement decl) { String entry = "" + "
" + " Declared by:"; String relativePackagePath = getRelativePathFromHere(decl.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR); if (decl != null && !StructureUtil.isAnonymous(decl.getParent())) { String packagePath = ""; if (decl.getPackageName() != null && !decl.getPackageName().equals("")) { packagePath = decl.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR; } String typeSignature = constructNestedTypeName(decl); String hrefName = packagePath + typeSignature; // The hrefLink needs to just be the corresponding aspect String hrefLink = relativePackagePath + packagePath + typeSignature + ".html"; entry += "" + hrefName.replace('/', '.') + ""; // !!! don't replace } entry += "
\n\n"; return entry; } /** * TODO: probably want to make this the same for intros and advice. */ static String generateAffects(IProgramElement decl) { List targets = null; if (decl.getKind().isDeclare() || decl.getKind().isInterTypeMember()) { targets = StructureUtil.getDeclareTargets(decl); } else { targets = StructureUtil.getTargets(decl, IRelationship.Kind.ADVICE); } if (targets == null) return ""; StringBuilder entry = new StringBuilder(""); IProgramElement.Kind kind = decl.getKind(); if (kind.equals(IProgramElement.Kind.ADVICE)) { entry.append("
") .append(HtmlRelationshipKind.ADVISES) .append(""); } else if (kind.equals(IProgramElement.Kind.DECLARE_WARNING) || kind.equals(IProgramElement.Kind.DECLARE_ERROR)) { entry.append("") .append(HtmlRelationshipKind.MATCHED_BY) .append(""); } else if (kind.isDeclareAnnotation()) { entry.append("") .append(HtmlRelationshipKind.ANNOTATES) .append(""); } else if (kind.equals(IProgramElement.Kind.DECLARE_SOFT)) { entry.append("") .append(HtmlRelationshipKind.SOFTENS) .append(""); } else { entry.append("") .append(HtmlRelationshipKind.DECLARED_ON) .append(""); } String relativePackagePath = getRelativePathFromHere(decl.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR); List addedNames = new ArrayList(); // for ensuring that we don't add duplciates for (Iterator it = targets.iterator(); it.hasNext();) { String currHandle = (String) it.next(); IProgramElement currDecl = decl.getModel().getHierarchy().findElementForHandle(currHandle); if (currDecl.getKind().equals(IProgramElement.Kind.CODE)) { currDecl = currDecl.getParent(); // promote to enclosing } if (currDecl != null && !StructureUtil.isAnonymous(currDecl.getParent())) { String packagePath = ""; if (currDecl.getPackageName() != null && !currDecl.getPackageName().equals("")) { packagePath = currDecl.getPackageName().replace('.', '/') + Config.DIR_SEP_CHAR; } String typeSignature = constructNestedTypeName(currDecl); String hrefName = packagePath + typeSignature; // Start the hRefLink with the relative path based on where // *this* type (i.e. the advisor) is in the package structure. String hrefLink = relativePackagePath + packagePath + typeSignature + ".html"; if (!currDecl.getKind().isType()) { hrefName += '.' + currDecl.getName(); hrefLink += "#" + currDecl.toLabelString(); } if (!addedNames.contains(hrefName)) { entry.append("").append(hrefName.replace('/', '.')).append(""); // !!! don't replace if (it.hasNext()) entry.append(", "); addedNames.add(hrefName); } } } entry.append("
\n\n"); return entry.toString(); } /** * Generates a relative directory path fragment that can be used to navigate "upwards" from the directory location implied by * the argument. * * @param packagePath * @return String consisting of multiple "../" parts, one for each component part of the input packagePath. */ private static String getRelativePathFromHere(String packagePath) { StringBuilder result = new StringBuilder(""); if (packagePath != null && (packagePath.contains("/"))) { StringTokenizer sTok = new StringTokenizer(packagePath, "/", false); while (sTok.hasMoreTokens()) { sTok.nextToken(); // don't care about the token value result.append(".." + Config.DIR_SEP_CHAR); }// end while }// end if return result.toString(); } /** * Generate the "public int"-type information about the given IProgramElement. Used when dealing with ITDs. To mirror the * behaviour of methods and fields in classes, if we're generating the summary information we don't want to include "public" if * the accessibility of the IProgramElement is public. * */ private static String generateModifierInformation(IProgramElement decl, boolean isDetails) { String intro = ""; if (decl.getKind().isDeclare()) { return intro + ""; } if (isDetails || !decl.getAccessibility().equals(IProgramElement.Accessibility.PUBLIC)) { intro += "" + decl.getAccessibility().toString() + " "; } if (decl.getKind().equals(IProgramElement.Kind.INTER_TYPE_FIELD)) { return intro + decl.getCorrespondingType() + ""; } else if (decl.getKind().equals(IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR) && isDetails) { return intro + ""; } else { return intro + decl.getCorrespondingType(true) + ""; } } static String generateIntroductionSignatures(IProgramElement decl, boolean isDetails) { return ""; } static String generateSignatures(IProgramElement decl) { return "" + decl.toLabelString() + ""; } static String generateSummaryComment(IProgramElement decl) { String COMMENT_INDENT = "          "; // !!! String formattedComment = getFormattedComment(decl); int periodIndex = formattedComment.indexOf('.'); if (formattedComment.equals("")) { return ""; } else if (periodIndex != -1) { return COMMENT_INDENT + formattedComment.substring(0, periodIndex + 1); } else { return COMMENT_INDENT + formattedComment; } } static String generateDetailsComment(IProgramElement decl) { return getFormattedComment(decl); } static String generateHREFName(IProgramElement decl) { StringBuilder hrefLinkBuffer = new StringBuilder(); char[] declChars = decl.toLabelString().toCharArray(); for (char declChar : declChars) { if (declChar == '"') { hrefLinkBuffer.append("quot;"); } else { hrefLinkBuffer.append(declChar); } } return hrefLinkBuffer.toString(); } /** * Figure out the link relative to the package. */ static String generateAffectsHREFLink(String declaringType) { String link = rootDir.getAbsolutePath() + "/" + declaringType + ".html"; return link; } /** * This formats a comment according to the rules in the Java Langauge Spec: The text of a docuemntation comment consists of * the characters between the /** that begins the comment and the 'star-slash' that ends it. The text is devided into one or * more lines. On each of these lines, the leading * characters are ignored; for lines other than the first, blanks and tabs * preceding the initial * characters are also discarded. * * TODO: implement formatting or linking for tags. */ static String getFormattedComment(IProgramElement decl) { String comment = decl.getFormalComment(); if (comment == null) return ""; StringBuilder formattedComment = new StringBuilder(); // strip the comment markers int startIndex = comment.indexOf("/**"); int endIndex = comment.indexOf("*/"); if (startIndex == -1) { startIndex = 0; } else { startIndex += 3; } if (endIndex == -1) { endIndex = comment.length(); } comment = comment.substring(startIndex, endIndex); // string the leading whitespace and '*' characters at the beginning of each line BufferedReader reader = new BufferedReader(new StringReader(comment)); try { for (String line = reader.readLine(); line != null; line = reader.readLine()) { line = line.trim(); for (int i = 0; i < line.length(); i++) { if (line.charAt(0) == '*') { line = line.substring(1, line.length()); } else { break; } } // !!! remove any @see and @link tags from the line // int seeIndex = line.indexOf("@see"); // int linkIndex = line.indexOf("@link"); // if ( seeIndex != -1 ) { // line = line.substring(0, seeIndex) + line.substring(seeIndex); // } // if ( linkIndex != -1 ) { // line = line.substring(0, linkIndex) + line.substring(linkIndex); // } formattedComment.append(line); } } catch (IOException ioe) { throw new Error("Couldn't format comment for declaration: " + decl.getName()); } return formattedComment.toString(); } static public IProgramElement[] getProgramElements(AsmManager model, String filename) { IProgramElement file = model.getHierarchy().findElementForSourceFile(filename); final List nodes = new ArrayList(); HierarchyWalker walker = new HierarchyWalker() { public void preProcess(IProgramElement node) { IProgramElement p = node; if (accept(node)) nodes.add(p); } }; file.walk(walker); return (IProgramElement[]) nodes.toArray(new IProgramElement[0]); } /** * Rejects anonymous kinds by checking if their name is an integer */ static private boolean accept(IProgramElement node) { if (node.getKind().isType()) { boolean isAnonymous = StructureUtil.isAnonymous(node); return !node.getParent().getKind().equals(IProgramElement.Kind.METHOD) && !isAnonymous; } else { return !node.getKind().equals(IProgramElement.Kind.IMPORT_REFERENCE); } } /** * TypeSafeEnum for the entries which need to be put in the html doc */ public static class HtmlRelationshipKind extends TypeSafeEnum { public HtmlRelationshipKind(String name, int key) { super(name, key); } public static HtmlRelationshipKind read(DataInputStream s) throws IOException { int key = s.readByte(); switch (key) { case 1: return ADVISES; case 2: return ADVISED_BY; case 3: return MATCHED_BY; case 4: return MATCHES_DECLARE; case 5: return DECLARED_ON; case 6: return ASPECT_DECLARATIONS; case 7: return SOFTENS; case 8: return SOFTENED_BY; case 9: return ANNOTATES; case 10: return ANNOTATED_BY; case 11: return USES_POINTCUT; case 12: return POINTCUT_USED_BY; } throw new Error("weird relationship kind " + key); } public static final HtmlRelationshipKind ADVISES = new HtmlRelationshipKind(" Advises:", 1); public static final HtmlRelationshipKind ADVISED_BY = new HtmlRelationshipKind(" Advised by:", 2); public static final HtmlRelationshipKind MATCHED_BY = new HtmlRelationshipKind(" Matched by:", 3); public static final HtmlRelationshipKind MATCHES_DECLARE = new HtmlRelationshipKind(" Matches declare:", 4); public static final HtmlRelationshipKind DECLARED_ON = new HtmlRelationshipKind(" Declared on:", 5); public static final HtmlRelationshipKind ASPECT_DECLARATIONS = new HtmlRelationshipKind(" Aspect declarations:", 6); public static final HtmlRelationshipKind SOFTENS = new HtmlRelationshipKind(" Softens:", 7); public static final HtmlRelationshipKind SOFTENED_BY = new HtmlRelationshipKind(" Softened by:", 8); public static final HtmlRelationshipKind ANNOTATES = new HtmlRelationshipKind(" Annotates:", 9); public static final HtmlRelationshipKind ANNOTATED_BY = new HtmlRelationshipKind(" Annotated by:", 10); public static final HtmlRelationshipKind USES_POINTCUT = new HtmlRelationshipKind(" Uses pointcut:", 11); public static final HtmlRelationshipKind POINTCUT_USED_BY = new HtmlRelationshipKind(" Pointcut used by:", 12); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy