001 /* 002 Copyright (C) 2001-2002 Renaud Pawlak <renaud@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, but 010 WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public 015 License along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 017 USA */ 018 019 package org.objectweb.jac.core; 020 021 022 import java.io.Serializable; 023 import java.util.*; 024 import org.apache.log4j.Logger; 025 import org.objectweb.jac.core.rtti.*; 026 import org.objectweb.jac.util.*; 027 028 /** 029 * This abstract class is the definition of a pointcut in org.objectweb.jac. 030 * 031 * @author <a mailto:renaud@aopsys.com>Renaud Pawlak</a> 032 * @see AspectComponent 033 * @see MethodPointcut */ 034 035 public abstract class Pointcut implements Serializable { 036 static Logger logger = Logger.getLogger("pointcut"); 037 static Logger loggerTags = Logger.getLogger("tags"); 038 static Logger loggerKeywords = Logger.getLogger("pointcut.keywords"); 039 040 /** 041 * Applies this pointcut to the given wrappee. 042 * 043 * @param wrappee the component the current pointcut is applied to 044 */ 045 public abstract void applyTo(Wrappee wrappee, ClassItem cl); 046 047 /** 048 * Parses a keyword expression and returns its actual value as a 049 * regular expression regarding the context. 050 */ 051 protected abstract String parseKeyword(Wrappee wrappee, 052 ClassItem cl, 053 String keywordExpr, 054 List parameters); 055 056 /** 057 * Replace elements of parameters "<attribute_name>" with members 058 * who have this attribute, and "member_name" with the member 059 * having that name. 060 * @param parameters Strings to replace 061 * @param cli replace with members of this class 062 * @return substituted list of MemberItem 063 */ 064 protected List replaceTags(List parameters,ClassItem cli) { 065 if (cli==null || parameters==null) 066 return null; 067 Vector result = new Vector(); 068 loggerTags.debug("check "+parameters+" on "+cli.getName()); 069 Iterator it = parameters.iterator(); 070 while (it.hasNext()) { 071 String param = (String)it.next(); 072 if (param.startsWith("<") && param.endsWith(">")) { 073 Collection taggedMembers; 074 if (param.charAt(1)=='!') 075 taggedMembers = cli.getTaggedFields( 076 param.substring(2,param.length()-1),true); 077 else 078 taggedMembers = cli.getTaggedFields( 079 param.substring(1,param.length()-1),false); 080 result.addAll(taggedMembers); 081 /* 082 if (taggedMembers.size()==0) { 083 result.add("#NONE#"); 084 } else { 085 result.addAll(taggedMembers); 086 } 087 */ 088 } else if (param.startsWith("{") && param.endsWith("}")) { 089 result.addAll(cli.filterFields(param.substring(1,param.length()-1))); 090 } else { 091 result.add(cli.getMember(param)); 092 } 093 } 094 loggerTags.debug(" result="+result); 095 return result; 096 } 097 098 /** 099 * A generic method that parses a pointcut expression and stores 100 * the result within a vector. 101 * 102 * @param descr a humain readable desciption of the pointcut 103 * expression type (used to make logs clearer) 104 * @param expr a pointcut expression 105 * @param result the parsing result 106 * @param inv filled with Boolean, one per element in result 107 */ 108 protected void parseExpr(String descr, Wrappee wrappee, ClassItem cl, 109 String expr, String[] keywords, 110 Vector result, Vector inv) { 111 112 result.clear(); 113 inv.clear(); 114 expr = Strings.replace(expr, " || ", "\\|"); 115 int pos = skipSpaces(expr,0); 116 boolean end = false; 117 118 if (expr.charAt(0) == '!') { 119 pos = skipSpaces(expr,pos+1); 120 inv.add(Boolean.FALSE); 121 } else { 122 inv.add(Boolean.TRUE); 123 } 124 if (pos==-1) 125 throw new RuntimeException("Invalid expression: \""+expr+"\""); 126 while (!end) { 127 int newpos = expr.indexOf("&&", pos); 128 try { 129 if (newpos != -1) { 130 result.add(replaceKeywords( 131 wrappee,cl,expr.substring(pos,newpos).trim(),keywords)); 132 pos = skipSpaces(expr,newpos + 2); 133 if (pos==-1) 134 throw new RuntimeException("Invalid expression: \""+expr+"\""); 135 if (expr.charAt(pos) == '!') { 136 pos = skipSpaces(expr,pos+1); 137 if (pos==-1) 138 throw new RuntimeException("Invalid expression: \""+expr+"\""); 139 inv.add(Boolean.FALSE); 140 } else { 141 inv.add(Boolean.TRUE); 142 } 143 } else { 144 result.add( 145 replaceKeywords(wrappee,cl,expr.substring(pos).trim(),keywords)); 146 end = true; 147 } 148 } catch (Exception e) { 149 logger.error("Invalid pointcut definition, "+descr+ 150 " construction failed at position "+pos+": "+e); 151 } 152 } 153 } 154 155 /** 156 * Skips spaces in a string. 157 * @param str a string 158 * @param pos a position in the string 159 * @return the next position in str which >= pos whose character 160 * is not a white space, or -1. 161 */ 162 static int skipSpaces(String str, int pos) { 163 while (pos<str.length()) { 164 if (!Character.isWhitespace(str.charAt(pos))) 165 return pos; 166 pos++; 167 } 168 return -1; 169 } 170 171 /** Replaces the keywords within an expression. */ 172 String replaceKeywords(Wrappee wrappee, ClassItem cl, 173 String expr, String[] keywords) { 174 String newExpr = expr; 175 for(int i=0; i<keywords.length; i++) { 176 newExpr = replaceKeyword(wrappee, cl, newExpr, keywords[i]); 177 } 178 return newExpr; 179 } 180 181 /** 182 * Parses the parameters of a keyword 183 * @param params a string representing the parameters. Should start 184 * with '(' and end with ')'. 185 * @return a list of member items 186 */ 187 List parseParameters(String params, ClassItem cli) { 188 if (params.equals("") || params.charAt(0) != '(') 189 return null; 190 /* 191 StringTokenizer st = new StringTokenizer( params, "," ); 192 while (st.hasMoreTokens()) { 193 result.add(st.nextToken()); 194 } 195 */ 196 return replaceTags( 197 Strings.splitToList(params.substring(1, params.indexOf(')',0)),",") 198 ,cli); 199 } 200 201 int parametersLength(String params) { 202 if (params.equals( "" )) 203 return 0; 204 if (params.charAt(0) != '(') 205 return 0; 206 return params.indexOf(')',0)+1; 207 } 208 209 /** Replace a keyword within an expression. */ 210 String replaceKeyword(Wrappee wrappee, ClassItem cl, 211 String expr, String keyword) { 212 213 int pos = 0; 214 boolean end = false; 215 StringBuffer newExpr = new StringBuffer(); 216 int keyLen = keyword.length(); 217 218 while (!end) { 219 int newpos = expr.indexOf(keyword, pos); 220 try { 221 if (newpos != -1) { 222 loggerKeywords.debug("replacing keyword '"+keyword+ 223 "' in expr :"+expr); 224 newExpr.append(expr.substring(pos, newpos - pos)); 225 newExpr.append( 226 parseKeyword( 227 wrappee, cl, keyword, 228 parseParameters( 229 expr.substring(newpos+keyword.length()),cl))); 230 pos = newpos + keyLen + 231 parametersLength(expr.substring(newpos+keyLen)); 232 } else { 233 newExpr.append(expr.substring(pos)); 234 end = true; 235 } 236 } catch (Exception e) { 237 e.printStackTrace(); 238 //Log.error("Invalid keyword '"+keyword+"'"); 239 } 240 } 241 return newExpr.toString(); 242 } 243 244 }