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    }