Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / util / MethodSpec.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18
19 package gov.nasa.jpf.util;
20
21 import gov.nasa.jpf.vm.ClassInfo;
22 import gov.nasa.jpf.vm.MethodInfo;
23 import gov.nasa.jpf.vm.Types;
24
25 import java.util.BitSet;
26
27 /**
28  * utility class that can match methods/args against specs.
29  * argument signatures can be given in dot notation (like javap), arguments
30  * can be marked with a preceeding '^'
31  * if the class or name part are omitted, "*" is assumed
32  * a preceeding '!' means the match is inverted
33  *
34  * spec examples
35  *   "x.y.Foo.*"
36  *   "java.util.HashMap.add(java.lang.Object,^java.lang.Object)"
37  *   "*.*(x.y.MyClass)"
38  *   
39  * Note: with a single '*' we can't tell if this is for the typename
40  * or the method, so something like "java.*" is probably not doing
41  * what you expect - it uses the wildcard for the method and 'java' for
42  * the type
43  *   
44  * <2do> we should extend this to allow alternatives
45  */
46 public class MethodSpec extends FeatureSpec {
47
48   static class MethodParseData extends ParseData {
49     String sigSpec;
50   }
51
52   static final char MARK = '^';  // to mark arguments
53
54   String  sigSpec;  // this is only the argument part, including parenthesis
55   BitSet  markedArgs;
56
57   /**
58    * factory method that includes the parser
59    */
60   public static MethodSpec createMethodSpec (String s){
61     MethodParseData d = new MethodParseData();
62
63     s = s.trim();
64     String src = s;
65
66     s = parseInversion(s,d);
67
68     int i = s.indexOf(('('));
69     if (i >= 0){ // we have a signature part
70       int j = s.lastIndexOf(')');
71       if (j > i){
72         d.sigSpec = s.substring(i, j+1);
73         s = s.substring(0, i);
74
75       } else {
76         return null; // error, unbalanced parenthesis
77       }
78     }
79
80     parseTypeAndName(s,d);
81
82     try {
83       return new MethodSpec(src, d.typeSpec, d.nameSpec, d.sigSpec, d.matchInverted);
84     } catch (IllegalArgumentException iax){
85       return null;
86     }
87   }
88
89
90   public MethodSpec (String rawSpec, String cls, String name, String argSig, boolean inverted){
91     super(rawSpec,cls,name,inverted);
92
93     if (argSig != null){
94       parseSignature(argSig);
95     }
96   }
97
98   /**
99    * assumed to be comma separated type list using fully qualified dot notation 
100    * like javap, but arguments can be marked with preceeding '^', 
101    * like "(java.lang.String,^int[])"
102    * spec includes parenthesis
103    */
104   void parseSignature (String spec){
105     BitSet m = null;
106     StringBuilder sb = new StringBuilder();
107     String al = spec.substring(1, spec.length()-1);
108     String[] args = al.split(",");
109
110     sb.append('(');
111     int i=0;
112     for (String a : args){
113       a = a.trim();
114       if (a.length() > 0){
115         if (a.charAt(0) == MARK){
116           if (m == null){
117             m = new BitSet(args.length);
118           }
119           m.set(i);
120           a = a.substring(1);
121         }
122         String tc = Types.getTypeSignature(a, false);
123         sb.append(tc);
124         i++;
125
126       } else {
127         // error in arg type spec
128       }
129     }
130     sb.append(')');
131
132     sigSpec = sb.toString();
133     markedArgs = m;
134   }
135
136
137   @Override
138   public String toString() {
139     StringBuilder sb = new StringBuilder();
140     sb.append("MethodSpec {");
141     sb.append("matchInverted:");
142     sb.append(matchInverted);
143     if (clsSpec != null){
144       sb.append(",clsSpec:\"");
145       sb.append(clsSpec);
146       sb.append('"');
147     }
148     if (nameSpec != null){
149       sb.append(",nameSpec:\"");
150       sb.append(nameSpec);
151       sb.append('"');
152     }
153     if (sigSpec != null){
154       sb.append(",sigSpec:\"");
155       sb.append(sigSpec);
156       sb.append('"');
157     }
158     if (markedArgs != null){
159       sb.append(",marked:");
160       sb.append(markedArgs);
161     }
162     sb.append('}');
163     return sb.toString();
164   }
165
166   
167   public BitSet getMarkedArgs () {
168     return markedArgs;
169   }
170
171   public boolean isMarkedArg(int idx){
172     return (markedArgs == null || markedArgs.get(idx));
173   }
174
175
176   //--- our matchers
177
178   @Override
179   public boolean matches (Object feature){
180     if (feature instanceof MethodInfo){
181       return matches((MethodInfo)feature);
182     } else {
183       return false;
184     }
185   }
186
187   public boolean matches (MethodInfo mi){
188     boolean isMatch = false;
189
190     ClassInfo ci = mi.getClassInfo();
191     if (isMatchingType(ci)){
192       if (nameSpec.matches(mi.getName())){
193         if (sigSpec != null){
194           // sigSpec includes '(',')' but not return type
195           isMatch = mi.getSignature().startsWith(sigSpec);
196         } else { // no sigSpec -> matches all signatures
197           isMatch = true;
198         }
199       }
200     }
201
202     return (isMatch != matchInverted);
203   }
204
205   public boolean matches (String clsName, String mthName){
206     boolean isMatch = clsSpec.matches(clsName) && nameSpec.matches(mthName);
207     return isMatch != matchInverted;
208   }
209
210   public boolean matchesClass (String clsName){
211     return clsSpec.matches(clsName) != matchInverted;
212   }
213 }