001 package edu.rice.cs.cunit.threadCheck.subAnnot;
002
003 import java.lang.reflect.*;
004 import java.awt.Component;
005 import java.awt.EventQueue;
006 import java.util.HashSet;
007
008 import edu.rice.cs.cunit.threadCheck.subAnnot.predicates.NumberBoundedArgument;
009 import static edu.rice.cs.cunit.threadCheck.subAnnot.predicates.NumberBoundedArgument.Mode.*;
010
011 /**
012 * Class containing several common predicates.
013 * @author Mathias Ricken
014 */
015 public class Predicates {
016 /**
017 * Return true if one or more of the annotations in the array returned true.
018 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
019 * @param methodArgs array with method arguments
020 * @param value array of invariant annotations
021 * @return true if one or more of the annotations returned true
022 */
023 @SuppressWarnings("unchecked")
024 public static boolean checkOr(Object thisObject, Object[] methodArgs, InvariantAnnotation[] value) {
025 for(InvariantAnnotation ia: value) {
026 if (SubAnnotThreadCheck.checkInvariantAnnotation(ia, thisObject, methodArgs)) { return true; }
027 }
028 return false;
029 }
030
031 /**
032 * Return false if one or more of the annotations in the array returned false.
033 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
034 * @param methodArgs array with method arguments
035 * @param value array of invariant annotations
036 * @return falseif one or more of the annotations returned false
037 */
038 @SuppressWarnings("unchecked")
039 public static boolean checkAnd(Object thisObject, Object[] methodArgs, InvariantAnnotation[] value) {
040 for(InvariantAnnotation ia: value) {
041 if (!SubAnnotThreadCheck.checkInvariantAnnotation(ia, thisObject, methodArgs)) { return false; }
042 }
043 return true;
044 }
045
046 /**
047 * Return true if exactly one of the annotations in the array returned true.
048 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
049 * @param methodArgs array with method arguments
050 * @param value array of invariant annotations
051 * @return true if exactly one of the annotations returned true
052 */
053 @SuppressWarnings("unchecked")
054 public static boolean checkXOr(Object thisObject, Object[] methodArgs, InvariantAnnotation[] value) {
055 int count = 0;
056 for(InvariantAnnotation ia: value) {
057 if (SubAnnotThreadCheck.checkInvariantAnnotation(ia, thisObject, methodArgs)) {
058 ++count;
059 if (count>1) { return false; }
060 }
061 }
062 return (count==1);
063 }
064
065 /**
066 * Return true if the annotations in the array returned false, and vice versa.
067 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
068 * @param methodArgs array with method arguments
069 * @param value invariant annotation
070 * @return true if the annotation returned false, and vice versa
071 */
072 @SuppressWarnings("unchecked")
073 public static boolean checkNot(Object thisObject, Object[] methodArgs, InvariantAnnotation value) {
074 return !SubAnnotThreadCheck.checkInvariantAnnotation(value, thisObject, methodArgs);
075 }
076
077 /**
078 * Return true if A -> B is true, where A is the first annotation and B is the second.
079 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
080 * @param methodArgs array with method arguments
081 * @param value array of invariant annotations; must be of length two
082 * @return true if A -> B is true
083 */
084 @SuppressWarnings("unchecked")
085 public static boolean checkImplies(Object thisObject, Object[] methodArgs, InvariantAnnotation[] value) {
086 if (value.length!=2) {
087 SubAnnotThreadCheck.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
088 SubAnnotThreadCheck.log("Thread Checker found an @Implies invariant annotation that did not have exactly two member annotations.");
089 SubAnnotThreadCheck.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
090 SubAnnotThreadCheck.flushLog();
091 return false;
092 }
093 boolean a = SubAnnotThreadCheck.checkInvariantAnnotation(value[0], thisObject, methodArgs);
094 boolean b = SubAnnotThreadCheck.checkInvariantAnnotation(value[1], thisObject, methodArgs);
095 // A -> B is (NOT A) OR B
096 return (!a) || b;
097 }
098
099 /**
100 * Return true if the current thread's name equals the specified string
101 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
102 * @param value string to compare the current thread's name to
103 * @param regex true if value should be treated as a regular expression
104 * @return true if the current thread's name equals the specified string
105 */
106 public static boolean checkName(Object thisObject, String value, boolean regex) {
107 if (regex) {
108 return Thread.currentThread().getName().matches(value);
109 }
110 else {
111 return Thread.currentThread().getName().equals(value);
112 }
113 }
114
115 /**
116 * Return true if the current thread's group equals the specified string
117 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
118 * @param value string to compare the current thread's group to
119 * @param regex true if value should be treated as a regular expression
120 * @return true if the current thread's group equals the specified string
121 */
122 public static boolean checkGroup(Object thisObject, String value, boolean regex) {
123 if (regex) {
124 return Thread.currentThread().getThreadGroup().getName().matches(value);
125 }
126 else {
127 return Thread.currentThread().getThreadGroup().getName().equals(value);
128 }
129 }
130
131 /**
132 * Return true if the any thread owns the monitor of the object
133 * with index value in the array of method arguments
134 * @param thisObject object that represents "this", or null if static
135 * @param args array of method arguments
136 * @param value index of the method argument that should be checked
137 * @return true if the any thread owns the monitor of the argument (false if value is out of range)
138 */
139 public static boolean checkSynchronizedArgument(Object thisObject, Object[] args, int value) {
140 if (value>=args.length) { return false; }
141 return checkMonitorOwned(args[value]);
142 }
143
144 /**
145 * Return true if the any thread owns the monitor of the object.
146 * If this method is directly used in a predicate annotation, then the object
147 * passed is "this".
148 * @param o object whose monitor should be checked; must be non-null
149 * @return true if the any thread owns the monitor (false if o is null)
150 */
151 public static boolean checkMonitorOwned(final Object o) {
152 if (o==null) return false;
153 final Boolean[] owned = new Boolean[] { null };
154 Thread inquirer = new Thread(new Runnable() {
155 public void run() {
156 final Thread inquirerThread = Thread.currentThread();
157 Thread acquirer = new Thread(new Runnable() {
158 public void run() {
159 synchronized(o) {
160 owned[0] = false;
161 inquirerThread.interrupt();
162 }
163 }
164 });
165 acquirer.start();
166 synchronized(this) {
167 while (owned[0]==null) {
168 try {
169 wait(10);
170 owned[0] = true;
171 return;
172 }
173 catch(InterruptedException e) {
174 if (!owned[0]) {
175 return;
176 }
177 }
178 }
179 }
180 }
181 });
182
183 inquirer.start();
184 try {
185 inquirer.join();
186 }
187 catch(InterruptedException e) { /* ignore */ }
188 return owned[0];
189 }
190
191 /**
192 * Return true if the any thread owns the monitor of the specified field.
193 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
194 * @param fieldClass the class the field is in
195 * @param fieldName the name of the field
196 * @return true if the any thread owns the monitor of the specified field
197 */
198 public static boolean checkSynchronizedField(Object thisObject, Class fieldClass, String fieldName) {
199 try {
200 Field field = fieldClass.getDeclaredField(fieldName);
201 boolean isAccessible = field.isAccessible();
202 field.setAccessible(true);
203 Object value = field.get(thisObject);
204 field.setAccessible(isAccessible);
205 return checkMonitorOwned(value);
206 }
207 catch(Exception e) {
208 e.printStackTrace();
209 }
210 return false;
211 }
212
213 /**
214 * Return true if any thread owns the monitor of the specified field.
215 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
216 * @param fieldClassName the name of the class the field is in
217 * @param fieldName the name of the field
218 * @return true if any thread owns the monitor of the specified field
219 */
220 public static boolean checkSynchronizedFieldByName(Object thisObject, String fieldClassName, String fieldName) {
221 try {
222 return checkSynchronizedField(thisObject, Class.forName(fieldClassName), fieldName);
223 }
224 catch(Exception e) {
225 e.printStackTrace();
226 }
227 return false;
228 }
229
230 /**
231 * Return true if the current thread is the event thread.
232 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
233 * @return true if the current thread is the event thread
234 */
235 public static boolean checkEventThread(Object thisObject) {
236 return EventQueue.isDispatchThread();
237 }
238
239 /**
240 * Return true if the current thread is the event thread, or if thisObject is an instance of a subclass of
241 * java.awt.Component and that component has not been realized yet.
242 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
243 * @return true if the current thread is the event thread, or thisObject is a component that has not been realized
244 */
245 public static boolean checkEventThreadAfterRealized(Object thisObject) {
246 if (thisObject!=null) {
247 // non-static context
248 if (thisObject instanceof Component) {
249 // subclass of java.awt.Component
250 Component thisComponent = (Component)thisObject;
251 if (!thisComponent.isDisplayable()) {
252 // and not realized yet; any thread is ok
253 return true;
254 }
255 }
256 return EventQueue.isDispatchThread();
257 }
258 else {
259 // static context
260 return EventQueue.isDispatchThread();
261 }
262 }
263
264 /**
265 * Return true if the current thread is equal to the thread in the specified field.
266 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
267 * @param fieldClass the class the field is in
268 * @param fieldName the name of the field
269 * @return true if the current thread is equal to the thread in the specified field
270 */
271 public static boolean checkThread(Object thisObject, Class fieldClass, String fieldName) {
272 try {
273 Field field = fieldClass.getDeclaredField(fieldName);
274 boolean isAccessible = field.isAccessible();
275 field.setAccessible(true);
276 Object value = field.get(thisObject);
277 field.setAccessible(isAccessible);
278 if (value instanceof Thread) {
279 return (Thread.currentThread()==value);
280 }
281 }
282 catch(Exception e) {
283 e.printStackTrace();
284 }
285 return false;
286 }
287
288 /**
289 * Return true if the current thread is equal to the thread in the specified field.
290 * @param thisObject "this" at the time of the check, or null if not available; ignored by this method
291 * @param fieldClassName the name of the class the field is in
292 * @param fieldName the name of the field
293 * @return true if the current thread is equal to the thread in the specified field
294 */
295 public static boolean checkThreadByName(Object thisObject, String fieldClassName, String fieldName) {
296 try {
297 return checkThread(thisObject, Class.forName(fieldClassName), fieldName);
298 }
299 catch(Exception e) {
300 e.printStackTrace();
301 }
302 return false;
303 }
304
305 /**
306 * Return true if the object with index value in the array of method arguments is null.
307 * @param thisObject object that represents "this", or null if static
308 * @param args array of method arguments
309 * @param value index of the method argument that should be checked
310 * @return true if the argument is null (false if value is out of range)
311 */
312 public static boolean checkNullArgument(Object thisObject, Object[] args, int value) {
313 if (value>=args.length) { return false; }
314 return args[value]==null;
315 }
316
317 /**
318 * Return true if the object with the specified indices are all distinct.
319 * @param thisObject object that represents "this", or null if static
320 * @param args array of method arguments
321 * @param value array of indices of the method argument that should be checked
322 * @return true if the arguments are all distinct, false otherwise, if value is out of range, or if fewer than two indices are specified
323 */
324 public static boolean checkDistinctArguments(Object thisObject, Object[] args,
325 int[] value) {
326 if (value.length<2) { return false; }
327 HashSet<Object> found = new HashSet<Object>();
328 for (int i=0;i< value.length;++i) {
329 if (found.contains(args[i])) { return false; }
330 found.add(args[i]);
331 }
332 return true;
333 }
334
335 /**
336 * Return true if the object with the specified indices are all the same.
337 * @param thisObject object that represents "this", or null if static
338 * @param args array of method arguments
339 * @param value array of indices of the method argument that should be checked
340 * @return true if the arguments are all the same, false otherwise, if value is out of range, or if fewer than two indices are specified
341 */
342 public static boolean checkSameArguments(Object thisObject, Object[] args,
343 int[] value) {
344 if (value.length<2) { return false; }
345 for (int i=1;i< value.length;++i) {
346 if (!args[0].equals(args[i])) { return false; }
347 }
348 return true;
349 }
350
351 /**
352 * Return true if the object with the specified index is a Number and meeds the bounds.
353 * @param thisObject object that represents "this", or null if static
354 * @param args array of method arguments
355 * @param mode bounding mode
356 * @param index index of the method argument that should be checked
357 * @param bound bound
358 * @param upperBound upper bound
359 * @return true if the number meets the bound, false otherwise, if index is out of range, or if the argument is not a Number
360 */
361 public static boolean checkNumberBoundedArgument(Object thisObject, Object[] args,
362 NumberBoundedArgument.Mode mode,
363 int index,
364 double bound,
365 double upperBound) {
366 if (index>=args.length) { return false; }
367 if (args[index]==null) { return false; }
368 if (!(args[index] instanceof Number)) {
369 return false;
370 }
371 Number n = ((Number)args[index]);
372 switch(mode) {
373 case LESS:
374 return n.doubleValue()<bound;
375 case LESS_EQ:
376 return n.doubleValue()<=bound;
377 case GREATER:
378 return n.doubleValue()>bound;
379 case GREATER_EQ:
380 return n.doubleValue()>=bound;
381 case IN_EQ:
382 return (n.doubleValue()<=upperBound) && (n.doubleValue()>=bound);
383 case IN:
384 return (n.doubleValue()<upperBound) && (n.doubleValue()>bound);
385 case OUT_EQ:
386 return (n.doubleValue()<=bound) || (n.doubleValue()>=upperBound);
387 case OUT:
388 return (n.doubleValue()<bound) || (n.doubleValue()>upperBound);
389 }
390 return false;
391 }
392 }