/* -*- Mode: JDE; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This file is part of the debugger and core tools for the AspectJ(tm) * programming language; see http://aspectj.org * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * either http://www.mozilla.org/MPL/ or http://aspectj.org/MPL/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is AspectJ. * * The Initial Developer of the Original Code is Xerox Corporation. Portions * created by Xerox Corporation are Copyright (C) 1999-2002 Xerox Corporation. * All Rights Reserved. */ package org.aspectj.tools.ajdoc; import org.aspectj.compiler.base.ast.Constructor; import org.aspectj.compiler.base.ast.ConstructorDec; import org.aspectj.compiler.base.ast.Exprs; import org.aspectj.compiler.base.ast.Field; import org.aspectj.compiler.base.ast.FieldDec; import org.aspectj.compiler.base.ast.Formals; import org.aspectj.compiler.base.ast.Method; import org.aspectj.compiler.base.ast.MethodDec; import org.aspectj.compiler.base.ast.NameType; import org.aspectj.compiler.base.ast.Type; import org.aspectj.compiler.base.ast.TypeDec; import org.aspectj.compiler.crosscuts.ast.PointcutDec; import org.aspectj.compiler.crosscuts.ast.PointcutSO; import com.sun.javadoc.Doc; import com.sun.javadoc.ExecutableMemberDoc; import com.sun.javadoc.MemberDoc; import com.sun.javadoc.Parameter; import com.sun.javadoc.Tag; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; /** * A utility class used by lots of folks. * * @author Jeff Palm */ public class Util { /** * Delegate to Character.isJavaIdentifierStart(char). * * @return true if c can * start a java identifier. */ public final static boolean start(char c) { return Character.isJavaIdentifierStart(c); } /** * Delegate to Character.isJavaIdentifierPart(char). * * @return true if c can * be a part of a java identifier. */ public final static boolean ident(char c) { return Character.isJavaIdentifierPart(c); } /** * Delegate to Character.isWhitespace(char). * * @return true if c is * valid white space. */ public final static boolean space(char c) { return Character.isWhitespace(c); } /** * Returns true is c is a newline character. * * @return true if * c == '\n' || c == '\r'. */ public final static boolean newline(char c) { return c == '\n' || c == '\r'; } /** * Returns two strings split at the first white space. * * @return an array of two Strings split at * the first white space. */ public static String[] split(String str) { String[] strs = new String[2]; for (int i = 0; i < str.length(); i++) { if (space(str.charAt(i))) { strs[0] = str.substring(0, i); strs[1] = str.substring(i+1); break; } } if (strs[0] == null) { strs[0] = str; strs[1] = ""; } return strs; } /** * Returns the inline tags found in str. * * @param doc Doc to give to the new tag. * @param str String from which to create the tags. * @param loc Locale to give to the new tag. * @param err ErrPrinter to give to the new tag. * @return an array of Tag representing the inline * tags found in str. */ public final static Tag[] inlineTags(Doc doc, String str, Locale loc, ErrPrinter err) { if (str == null || str.length() < 1) { return new Tag[0]; } int N = str.length(); List list = new ArrayList(); int i = 0; for (int j = i; i < N; j = i) { // Try to match a link tag int ileft = str.indexOf("{@", i); // If there are no more link tags, return the rest // of str as a 'Text' Tag if (ileft == -1) { list.add(new TagImpl(doc, "Text", str.substring(i), loc, err)); break; } if (j < ileft) { list.add(new TagImpl(doc, "Text", str.substring(j, ileft), loc, err)); } // If there is a tag and it's name is 'link ' try to // match it i = ileft; if (i+7 < N && str.substring(i+2, i+7).toLowerCase().equals("link ")) { i += 7; for (; str.charAt(i) != '}'; i++) { if (i == N-1) { err.error("tag_unterminated_link_tag", str.substring(i)); break; } } list.add(new SeeTagImpl(doc, "@link", str.substring(ileft+7, i), loc, err)); } else { err.error("tag_invalid_link_tag", str.substring(i)); } // Don't want to include the right brace i += 1; } return (Tag[])list.toArray(new Tag[list.size()]); } /** * Returns the first sentence tags found in str. * * @param doc Doc to give to the new tag. * @param str String from which to create the tags. * @param loc Locale to give to the new tag. * @param err ErrPrinter to give to the new tag. * @return an array of Tag representing the first * sentence tags found in str. */ public final static Tag[] firstSentenceTags(Doc doc, String str, Locale loc, ErrPrinter err) { return inlineTags(doc, firstSentenceText(str, loc, err), loc, err); } /** * Returns the first sentence tags found in str, * using Locale.US as the default locale. * * @param doc Doc to give to the new tag. * @param str String from which to create the tags. * @param err ErrPrinter to give to the new tag. * @return an array of Tag representing the first * sentence tags found in str. */ private static String firstSentenceText(String str, Locale loc, ErrPrinter err) { if (str == null || loc == null || !loc.equals(Locale.US)) { return ""; } final int N = str.length(); int i; for (i = 0; i < N; i++) { // A period at the end of the text or a // period followed by white space is the // end of a sentence if (str.charAt(i) == '.') { if (i == N-1) { return str.substring(0, i+1); } if (space(str.charAt(i+1))) { return str.substring(0, i+2); } } // An HTML tag the signals the end -- one of: //

//


 or 
if (str.charAt(i) == '<') { int j = i+1; // Find the closing '>' while (j < N && str.charAt(j) != '>') j++; // If there's no closing '>' signal an error if (j == N) { err.error("unterminated_html_tag", str); return str; } // Inspect the inside of the tag String innards = str.substring(i+1, j).trim().toLowerCase(); if (innards.equals("p") || innards.equals("pre") || innards.equals("h1") || innards.equals("h2") || innards.equals("h3") || innards.equals("h4") || innards.equals("h5") || innards.equals("h6") || innards.equals("hr")) { return str.substring(0, i+1); } } } return str; } /** * Returns the tags found in str. * * @param doc Doc to give to the new tag. * @param str String from which to create the tags. * @param loc Locale to give to the new tag. * @param err ErrPrinter to give to the new tag. * @return an array of Tag representing the * tags found in str. */ public final static List findTags(Doc doc, String str, Locale loc, ErrPrinter err) { //XXX This sucks!!! Will redo later. boolean newline = true; List result = new ArrayList(); if (str == null) return result; final int N = str.length(); int lastTag = -1; for (int i = 0; i < N; i++) { if (newline(str.charAt(i))) { newline = true; // XXX need to evaluate - some tags can span newlines? // if (lastTag != -1) { // now requiring tags not to span newlines // result.add(parse(doc, str.substring(lastTag, i), // loc, err)); // } // lastTag = -1 } else if (space(str.charAt(i)) && newline) { } else if (str.charAt(i) == '@' && newline) { if (lastTag != -1) { result.add(parse(doc, str.substring(lastTag, i), loc, err)); } lastTag = i; } else { newline = false; } } if (lastTag != -1) { result.add(parse(doc, str.substring(lastTag), loc, err)); } return result; } private final static Tag parse(Doc doc, String str, Locale loc, ErrPrinter err) { Tag result = null; String[] split = split(str); String name = split[0]; String rest = split[1]; if (name.equals("@see")) { result = new SeeTagImpl(doc, name, rest, loc, err); } else if (name.equals("@exception") || name.equals("@throws")) { result = new ThrowsTagImpl(doc, name, rest, loc, err); } else if (name.equals("@serialField")) { result = new SerialFieldTagImpl(doc, name, rest, loc, err); } else if (name.equals("@param")) { result = new ParamTagImpl(doc, name, rest, loc, err); } else { result = new TagImpl(doc, name, rest, loc, err); } return result; } /** * Returns the raw comment text found in str. * * @param str String containing comment from which * the raw comment is found. * @return String with the raw comment taken * from str. */ public final static String rawCommentText(String str) { if (str == null) return ""; if (str.length() < 3) return ""; String withstars = ""; int islash = str.indexOf('/'); if (islash == -1 || islash+2 >= str.length()) { return ""; } if (str.charAt(islash+1) != '*' || str.charAt(islash+2) != '*') { return ""; } int start = islash+2+1; while (str.charAt(start) == '*' || space(str.charAt(start))) start++; int end = str.length()-2; while (str.charAt(end) == '*') end--; if (start != -1 && end > start) { withstars = str.substring(start, end+1); } //String result = ""; StringBuffer result = new StringBuffer(withstars.length()); for (StringTokenizer t = new StringTokenizer(withstars, "\n", true); t.hasMoreTokens();) { String line = t.nextToken(); if (line == null || line.length() == 0) continue; int i; for (i = 0; i < line.length(); i++) { if (!(line.charAt(i) == '*' || line.charAt(i) == ' ')) { break; } } //result += line.substring(i); result.append(line.substring(i)); } //return result; return result.toString(); } /** * Returns the comment text from the passed in * raw comment text -- e.g. no tags at the end. * * @param rawCommentText raw comment text to search. * @return the comment text from * rawCommentText. */ public final static String commentText(String rawCommentText) { //String result = ""; if (rawCommentText == null) { return ""; } StringBuffer result = new StringBuffer(rawCommentText.length()); outer: for (StringTokenizer t = new StringTokenizer(rawCommentText, "\n", true); t.hasMoreTokens();) { String line = t.nextToken(); if (line == null || line.length() == 0) continue; int i; for (i = 0; i < line.length(); i++) { char c = line.charAt(i); if (c == ' ' || c == '\t') { } else if (c == '@') { break outer; } else { //result += line; result.append(line); continue outer; } } } //return result; return result.toString(); } /** * Compares using names. * * @param $ First Doc. * @param _ Second Doc. * @return -1 if either are null, else * $.name.compareTo(_.name. */ public final static int compareTo(Doc $, Doc _) { return ($ == null || _ == null) ? -1 : $.name().compareTo(_.name()); } /** * Returns the signature, given parameters, * without flattening. * * @param parameters an array of Parameter. * @return String representation of the parameters. * @see #signature(Parameter[],boolean) */ public final static String signature(Parameter[] parameters) { return signature(parameters, false); } /** * Returns the signature, given parameters, * with flattening. * * @param parameters an array of Parameter. * @return String representation of the parameters. * @see #signature(Parameter[],boolean) */ public final static String flatSignature(Parameter[] parameters) { return signature(parameters, true); } /** * Returns the signature, given parameters * and flattens if flatten. * * @param parameters an array of Parameter. * @param flatten true if the parameter names * should be flattened. * @return String representation of the parameters. */ public final static String signature(Parameter[] parameters, boolean flatten) { if (parameters == null || parameters.length == 0) { return "()"; } //String str = "("; StringBuffer str = new StringBuffer((flatten ? 8 : 20) * parameters.length); str.append("("); final int N = parameters.length; for (int i = 0; i < N; i++) { String typeName = parameters[i].typeName(); if (flatten) { int idot = typeName.lastIndexOf('.'); if (idot != -1) { typeName = typeName.substring(idot+1); } } //str += typeName + (i < N-1 ? "," : ""); str.append(typeName + (i < N-1 ? "," : "")); } //str += ")"; str.append(")"); //return str; return str.toString(); } /** * Returns true -- include all members for now. * * @param doc member to consider. * @param flags access flags. */ public final static boolean isIncluded(MemberDoc doc, long flags) { return true; } /** * Returns true if dec * isn't local or annonymous or null. * * @param dec TypeDec to consider. * @return true isn't dec is local or * annonymous or null. */ public final static boolean isIncluded(TypeDec dec) { if (dec == null) { return false; } if (dec.isLocal() && dec.isAnonymous()) { return false; } return true; //XXX to do } //XXX Debugging public final static void dump(Object o, String prefix) { System.err.println(">> Dumping:"+o); java.lang.reflect.Method[] ms = o.getClass().getMethods(); List list = new ArrayList(); for (int i = 0; i < ms.length; i++) { list.add(ms[i]); } Collections.sort(list, new Comparator() { public int compare(Object o1, Object o2) { return str(o1).compareTo(str(o2)); } public boolean equals(Object o1, Object o2) { return str(o1).equals(str(o2)); } private String str(Object _) { return (_ != null && _ instanceof java.lang.reflect.Method) ? ((java.lang.reflect.Method)_).getName() : _+""; } }); for (Iterator i = list.iterator(); i.hasNext();) { java.lang.reflect.Method m = (java.lang.reflect.Method)i.next(); if (m.getParameterTypes().length == 0 && m.getName().startsWith(prefix)) { try { System.err.println(" "+m.getName()+":"+ m.invoke(o, new Object[0])); } catch (Throwable _) {} } } } public final static void gets(Object o) { dump(o, "get"); } public final static void array(Object[] os) { array(os, false); } public final static void array(Object[] os, boolean gets) { if (os == null) { System.err.println("NULL"); return; } System.err.println(os.getClass().getName()+":" + os.length); for (int i = 0; i < os.length; i++) { System.err.println(" [" + i +"]:" + os[i]); if (gets) gets(os[i]); } } /** * Returns the HTML documentation found in html * using err to report errors. * * @param html File in which to look. * @param err ErrPrinter to use to report errors. * @return HTML documentaiton found in html. */ public static String documentation(File html, ErrPrinter err) { String str = ""; InputStream in = null; try { in = new FileInputStream(html); } catch (IOException ioe) { err.ex(ioe, "ioexception_open", html.getAbsolutePath()); return ""; } try { byte[] bytes = new byte[in.available()]; in.read(bytes, 0, bytes.length); in.close(); str = new String(bytes); } catch (IOException ioe) { err.ex(ioe, "ioexception_reading", html.getAbsolutePath()); } int[] is = new int[]{-10,-1}; int i = 0; final char[] chars = new char[]{'/','B','O','D','Y','>'}; for (int j = 1; j >= 0; j--) { nextTag: for (; i != -1; i = str.indexOf('<', i+1)) { nextLt: for (int s = i+1, k = j; s < str.length(); s++, k++) { char c = str.charAt(s); if (k == chars.length) { is[j] += s+2; break nextTag; } if (!(c == chars[k] || c == (chars[k] | 0x01000000))) { break nextLt; } } } } if (is[0] > -1 && is[1] > -1) { str = str.substring(is[1], is[0]); } return str; } /** * Returns the result of invoking the method name * on target with parameters params * declared in the target's class using arguments * args. * * @param target target Object. * @param name name of the method. * @param params array of Class of parameters of the method. * @param args array of Object of arguments to the method. * @return the result of invoking the method. * @see #invoke(Class,Object,String,Class[],Object[]) */ public static Object invoke(Object target, String name, Class[] params, Object[] args) { return invoke(target.getClass(), target, name, params, args); } /** * Returns the result of invoking the method name * on target with parameters params * declared in the type using arguments * args. * This method handles any errors that arise in doing so. * * @param type type in which the method is declared. * @param target target Object -- null for static methods. * @param name name of the method. * @param params array of Class of parameters of the method. * @param args array of Object of arguments to the method. * @return the result of invoking the method. */ public static Object invoke(Class type, Object target, String name, Class[] params, Object[] args) { try { java.lang.reflect.Method method = type.getDeclaredMethod(name, params); method.setAccessible(true); return method.invoke(target, args); } catch (Exception e) { e.printStackTrace(); //TODO } return null; } /** * Returns the value of access the field name * declared in type on target. * This method handles any errors that arise in doing so. * * @param type type in which the field is declared. * @param target target that is currently holding the field -- * null for static fields. * @param name name of the field. * @return the result of accessing this field. */ public static Object access(Class type, Object target, String name) { try { java.lang.reflect.Field field = type.getDeclaredField(name); field.setAccessible(true); return field.get(target); } catch (Exception e) { //TODO e.printStackTrace(); } return null; } /** * Returns the ExecutableMemberDoc from the array passed * in whose parameters weakly match those of * params and whose name matches exactly * with name. * This method can return null. * * @param emds an array of ExecutableMemberDoc from which * the returned value comes. * @param name the name of the member to return. * @param params an array of Parameter that represent * the parameters we're trying to match. * @return an ExecutableMemberDoc whose parameters * match the names and order found in * params and whose name * exactly equals name. */ public final static ExecutableMemberDoc executableMemberDoc (ExecutableMemberDoc[] emds, String name, Parameter[] params) { ExecutableMemberDoc result = null; next: for (int i = 0; i < emds.length; i++) { ExecutableMemberDoc emd = emds[i]; if (emd.name().equals(name) && params.length == emd.parameters().length) { for (int j = 0; j < params.length; j++) { if (!params[j].typeName().equals (emd.parameters()[j].typeName())) { continue next; } result = emd; break next; } } } return result; } /** * Returns the PointcutDoc from the array passed * in whose parameters weakly match those of * formals and whose name matches exactly * with id. * This method can return null. * * @param nameType the type in which we're searching. * @param id the name of the pointcut to return. * @param formals the Formals whose name and order * must match to return a pointcut. * @return a PointcutDoc whose parameters * match the names and order found in * formals and whose name * exactly equals id. */ public final static PointcutDec pointcutDec(NameType nameType, String id, Formals formals) { PointcutDec result = null; next: for (Iterator i = nameType.getPointcuts().iterator(); i.hasNext();) { PointcutDec md = ((PointcutSO)i.next()).getPointcutDec(); if (md.getFormals().size() == formals.size() && id.equals(md.getId())) { for (int j = 0; j < formals.size(); j++) { if (!md.getFormals().get(j).getType().getString(). equals(formals.get(j).getType().getString())) { continue next; } } result = md; break next; } } return result; } /** * Returns the MethodDoc from the array passed * in whose parameters weakly match those of * formals and whose name matches exactly * with id. * This method can return null. * * @param nameType the type in which we're searching. * @param id the name of the method to return. * @param formals the Formals whose name and order * must match to return a method. * @return a MethodDoc whose parameters * match the names and order found in * formals and whose name * exactly equals id. */ public final static MethodDec methodDec(NameType nameType, String id, Formals formals) { MethodDec result = null; next: for (Iterator i = nameType.getMethods().iterator(); i.hasNext();) { //MethodDec md = (MethodDec)i.next(); MethodDec md = ((Method)i.next()).getMethodDec(); if (md.getFormals().size() == formals.size() && id.equals(md.getId())) { for (int j = 0; j < formals.size(); j++) { if (!md.getFormals().get(j).getType().getString(). equals(formals.get(j).getType().getString())) { continue next; } } result = md; break next; } } return result; } /** * Returns the PointcutDec named name, * contained in typeDec with type type. * showError is passed to subsequent methods * to supress or warrant error printing. * This may return null. * * @param type Type in which this pointcut was declared. * @param name name of the pointcut. * @param typeDec TypeDec in which we're searching. * @param showError true is an error should * be printed upon not finding a pointcut. * @return the pointcut declared in type * named name, found by searching * from typeDec. This may be * null. */ public static PointcutDec getPointcutDec(Type type, String name, TypeDec typeDec, boolean showError) { PointcutSO so = ((NameType)type).getPointcut(name, typeDec, showError); PointcutDec dec = null; if (so != null) { dec = (PointcutDec)so.getCorrespondingDec(); } return dec; } /** * Returns the FieldDec named name, * contained in typeDec with type type. * showError is passed to subsequent methods * to supress or warrant error printing. * This may return null. * * @param type Type in which this field was declared. * @param name name of the field. * @param typeDec TypeDec in which we're searching. * @param showError true is an error should * be printed upon not finding a field. * @return the field declared in type * named name, found by searching * from typeDec. This may be * null. */ public static FieldDec getFieldDec(Type type, String name, TypeDec typeDec, boolean showError) { Field so = ((NameType)type).getField(name, typeDec, showError); FieldDec dec = null; if (so != null) { dec = (FieldDec)so.getCorrespondingDec(); } return dec; } /** * Returns the MethodDec named name, with * formals params, * contained in typeDec with type type. * showError is passed to subsequent methods * to supress or warrant error printing. * This may return null. * * @param type Type in which this method was declared. * @param name name of the method. * @param typeDec TypeDec in which we're searching. * @param params the method's formal parameters. * @param showError true is an error should * be printed upon not finding a method. * @return the method declared in type * named name, found by searching * from typeDec. This may be * null. */ public static MethodDec getMethodDec(Type type, String name, TypeDec typeDec, Exprs params, boolean showError) { Method so = ((NameType)type).getMethod(name, typeDec, params, showError); MethodDec dec = null; if (so != null) { dec = so.getMethodDec(); } return dec; } /** * Returns the ConstructorDec named name, with * formals params, * contained in typeDec with type type. * showError is passed to subsequent constructors * to supress or warrant error printing. * This may return null. * * @param type Type in which this constructor was declared. * @param name name of the constructor. * @param typeDec TypeDec in which we're searching. * @param params the constructor's formal parameters. * @param showError true is an error should * be printed upon not finding a constructor. * @return the constructor declared in type * named name, found by searching * from typeDec. This may be * null. */ public static ConstructorDec getConstructorDec(Type type, TypeDec typeDec, Exprs params, boolean showError) { Constructor so = ((NameType)type).getConstructor(typeDec, params, showError); ConstructorDec dec = null; if (so != null) { dec = (ConstructorDec)so.getCorrespondingDec(); } return dec; } }