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    }