001    package edu.rice.cs.cunit.instrumentors.threadCheck;
002    
003    import edu.rice.cs.cunit.threadCheck.OnlyRunBy;
004    import edu.rice.cs.cunit.threadCheck.SuppressSubtypingWarning;
005    
006    import java.io.Serializable;
007    import java.util.HashSet;
008    import java.util.ArrayList;
009    
010    /**
011         * Class that keeps track of what thread names/ids/groups are allowed and denied for a method.
012     */
013    public class ThreadCheckAnnotationRecord implements Serializable {
014        public HashSet<String> denyThreadNames;
015        public HashSet<Long> denyThreadIds;
016        public HashSet<String> denyThreadGroups;
017        public HashSet<String> allowThreadNames;
018        public HashSet<Long> allowThreadIds;
019        public HashSet<String> allowThreadGroups;
020        public OnlyRunBy.EVENT_THREAD allowEventThread;
021    
022        public ArrayList<PredicateAnnotationRecord> predicateAnnotations;
023    
024        public boolean suppressSubtypingWarning = false;
025    
026        /** Timestamp of the last modification. */
027        public long timeStamp;
028    
029        /**
030         * Create a new empty annotation record.
031         */
032        public ThreadCheckAnnotationRecord() {
033            denyThreadNames = new HashSet<String>();
034            denyThreadIds = new HashSet<Long>();
035            denyThreadGroups = new HashSet<String>();
036            allowThreadNames = new HashSet<String>();
037            allowThreadIds = new HashSet<Long>();
038            allowThreadGroups = new HashSet<String>();
039            allowEventThread = OnlyRunBy.EVENT_THREAD.NO;
040            predicateAnnotations = new ArrayList<PredicateAnnotationRecord>();
041            timeStamp = System.currentTimeMillis();
042        }
043    
044        /**
045         * Create a new annotation record by merging the two specified records
046         * @param firstAR first record to merge
047         * @param secondAR second record to merge
048         */
049        public ThreadCheckAnnotationRecord(ThreadCheckAnnotationRecord firstAR,
050                                           ThreadCheckAnnotationRecord secondAR) {
051            denyThreadNames = new HashSet<String>(firstAR.denyThreadNames);
052            denyThreadNames.addAll(secondAR.denyThreadNames);
053            denyThreadIds = new HashSet<Long>(firstAR.denyThreadIds);
054            denyThreadIds.addAll(secondAR.denyThreadIds);
055            denyThreadGroups = new HashSet<String>(firstAR.denyThreadGroups);
056            denyThreadGroups.addAll(secondAR.denyThreadGroups);
057            allowThreadNames = new HashSet<String>(firstAR.allowThreadNames);
058            allowThreadNames.addAll(secondAR.allowThreadNames);
059            allowThreadIds = new HashSet<Long>(firstAR.allowThreadIds);
060            allowThreadIds.addAll(secondAR.allowThreadIds);
061            allowThreadGroups = new HashSet<String>(firstAR.allowThreadGroups);
062            allowThreadGroups.addAll(secondAR.allowThreadGroups);
063            if (firstAR.allowEventThread.compareTo(secondAR.allowEventThread)>=0) {
064                allowEventThread = firstAR.allowEventThread;
065            }
066            else {
067                allowEventThread = secondAR.allowEventThread;
068            }
069            predicateAnnotations = new ArrayList<PredicateAnnotationRecord>(firstAR.predicateAnnotations);
070            predicateAnnotations.addAll(predicateAnnotations);
071            timeStamp = Math.max(firstAR.timeStamp, secondAR.timeStamp);
072            suppressSubtypingWarning = firstAR.suppressSubtypingWarning || secondAR.suppressSubtypingWarning;
073        }
074    
075        /**
076         * Adds the annotations in the other annotation record to this one
077         * @param otherAR other record
078         */
079        public void add(ThreadCheckAnnotationRecord otherAR) {
080            denyThreadNames.addAll(otherAR.denyThreadNames);
081            denyThreadIds.addAll(otherAR.denyThreadIds);
082            denyThreadGroups.addAll(otherAR.denyThreadGroups);
083            allowThreadNames.addAll(otherAR.allowThreadNames);
084            allowThreadIds.addAll(otherAR.allowThreadIds);
085            allowThreadGroups.addAll(otherAR.allowThreadGroups);
086            if (otherAR.allowEventThread.compareTo(allowEventThread)>0) {
087                allowEventThread = otherAR.allowEventThread;
088            }
089            predicateAnnotations.addAll(otherAR.predicateAnnotations);
090            timeStamp = Math.max(timeStamp, otherAR.timeStamp);
091            suppressSubtypingWarning = suppressSubtypingWarning || otherAR.suppressSubtypingWarning;
092        }
093    
094        /**
095         * Return true if there is nothing specified in this annotation record.
096         * @return true if nothing specified
097         */
098        public boolean empty() {
099            return (denyThreadNames.size()==0) &&
100                (denyThreadIds.size()==0) &&
101                (denyThreadGroups.size()==0) &&
102                (allowThreadNames.size()==0) &&
103                (allowThreadIds.size()==0) &&
104                (allowThreadGroups.size()==0) &&
105                (allowEventThread==OnlyRunBy.EVENT_THREAD.NO) &&
106                (predicateAnnotations.size()==0);
107        }
108    
109    //        /**
110    //         * Returns true if the two ThreadCheckAnnotationRecords are equal.
111    //         * @param o other object
112    //         * @return true if equal
113    //         */
114    //        public boolean equals(Object o) {
115    //            if (this == o) {
116    //                return true;
117    //            }
118    //            if (o == null || getClass() != o.getClass()) {
119    //                return false;
120    //            }
121    //
122    //            ThreadCheckAnnotationRecord that = (ThreadCheckAnnotationRecord)o;
123    //
124    //            if (allowEventThread != that.allowEventThread) {
125    //                return false;
126    //            }
127    //            if (!allowThreadGroups.equals(that.allowThreadGroups)) {
128    //                return false;
129    //            }
130    //            if (!allowThreadIds.equals(that.allowThreadIds)) {
131    //                return false;
132    //            }
133    //            if (!allowThreadNames.equals(that.allowThreadNames)) {
134    //                return false;
135    //            }
136    //            if (!denyThreadGroups.equals(that.denyThreadGroups)) {
137    //                return false;
138    //            }
139    //            if (!denyThreadIds.equals(that.denyThreadIds)) {
140    //                return false;
141    //            }
142    //            if (!denyThreadNames.equals(that.denyThreadNames)) {
143    //                return false;
144    //            }
145    //            if (!predicateAnnotations.equals(that.predicateAnnotations)) {
146    //                return false;
147    //            }
148    //
149    //            return true;
150    //        }
151    //
152    //        /**
153    //         * Returns a hashcode for this ThreadCheckAnnotationRecord.
154    //         * @return hashcode
155    //         */
156    //        public int hashCode() {
157    //            int result;
158    //            result = denyThreadNames.hashCode();
159    //            result = 31 * result + denyThreadIds.hashCode();
160    //            result = 31 * result + denyThreadGroups.hashCode();
161    //            result = 31 * result + allowThreadNames.hashCode();
162    //            result = 31 * result + allowThreadIds.hashCode();
163    //            result = 31 * result + allowThreadGroups.hashCode();
164    //            result = 31 * result + allowEventThread.hashCode();
165    //            result = 31 * result + predicateAnnotations.hashCode();
166    //            return result;
167    //        }
168    
169    
170        /**
171         * Returns true if the two ThreadCheckAnnotationRecords are equal.
172         * @param o other object
173         * @return true if equal
174         */
175        public boolean equals(Object o) {
176            if (this == o) {
177                return true;
178            }
179            if (o == null || getClass() != o.getClass()) {
180                return false;
181            }
182    
183            ThreadCheckAnnotationRecord that = (ThreadCheckAnnotationRecord)o;
184    
185            if (suppressSubtypingWarning != that.suppressSubtypingWarning) {
186                return false;
187            }
188            if (allowEventThread != that.allowEventThread) {
189                return false;
190            }
191            if (allowThreadGroups != null ? !allowThreadGroups.equals(that.allowThreadGroups) :
192                that.allowThreadGroups != null) {
193                return false;
194            }
195            if (allowThreadIds != null ? !allowThreadIds.equals(that.allowThreadIds) : that.allowThreadIds != null) {
196                return false;
197            }
198            if (allowThreadNames != null ? !allowThreadNames.equals(that.allowThreadNames) :
199                that.allowThreadNames != null) {
200                return false;
201            }
202            if (denyThreadGroups != null ? !denyThreadGroups.equals(that.denyThreadGroups) :
203                that.denyThreadGroups != null) {
204                return false;
205            }
206            if (denyThreadIds != null ? !denyThreadIds.equals(that.denyThreadIds) : that.denyThreadIds != null) {
207                return false;
208            }
209            if (denyThreadNames != null ? !denyThreadNames.equals(that.denyThreadNames) :
210                that.denyThreadNames != null) {
211                return false;
212            }
213            if (predicateAnnotations != null ? !predicateAnnotations.equals(that.predicateAnnotations) :
214                that.predicateAnnotations != null) {
215                return false;
216            }
217    
218            return true;
219        }
220    
221        /**
222         * Returns a hashcode for this ThreadCheckAnnotationRecord.
223         * @return hashcode
224         */
225        public int hashCode() {
226            int result;
227            result = (denyThreadNames != null ? denyThreadNames.hashCode() : 0);
228            result = 31 * result + (denyThreadIds != null ? denyThreadIds.hashCode() : 0);
229            result = 31 * result + (denyThreadGroups != null ? denyThreadGroups.hashCode() : 0);
230            result = 31 * result + (allowThreadNames != null ? allowThreadNames.hashCode() : 0);
231            result = 31 * result + (allowThreadIds != null ? allowThreadIds.hashCode() : 0);
232            result = 31 * result + (allowThreadGroups != null ? allowThreadGroups.hashCode() : 0);
233            result = 31 * result + (allowEventThread != null ? allowEventThread.hashCode() : 0);
234            result = 31 * result + (predicateAnnotations != null ? predicateAnnotations.hashCode() : 0);
235            result = 31 * result + (suppressSubtypingWarning ? 1 : 0);
236            return result;
237        }
238    
239        /**
240         * Returns a string representation of the object.
241         * @return a string representation of the object.
242         */
243        public String toString() {
244            if (empty()) { return ""; }
245            String LF = System.getProperty("line.separator");
246            StringBuilder sb = new StringBuilder();
247            if (suppressSubtypingWarning) {
248                sb.append('@');
249                sb.append(SuppressSubtypingWarning.class.getSimpleName());
250                sb.append(LF);
251            }
252            if ((denyThreadNames.size()>0) ||
253                (denyThreadGroups.size()>0) ||
254                (denyThreadIds.size()>0)) {
255                StringBuilder sbNot = new StringBuilder();
256                for(String s: denyThreadNames) {
257                    sbNot.append(',');
258                    sbNot.append(LF);
259                    sbNot.append("\t@ThreadDesc(name=\"");
260                    sbNot.append(s);
261                    sbNot.append("\")");
262                }
263                for(String s: denyThreadGroups) {
264                    sbNot.append(',');
265                    sbNot.append(LF);
266                    sbNot.append("\t@ThreadDesc(group=\"");
267                    sbNot.append(s);
268                    sbNot.append("\")");
269                }
270                for(Long l: denyThreadIds) {
271                    sbNot.append(',');
272                    sbNot.append(LF);
273                    sbNot.append("\t@ThreadDesc(id=");
274                    sbNot.append(l);
275                    sbNot.append(")");
276                }
277                sbNot.append(LF);
278                sbNot.append("})");
279    
280                sbNot.deleteCharAt(0); // delete first ','
281    
282                sb.append("@NotRunBy({");
283                sb.append(sbNot.toString());
284            }
285    
286            if ((allowThreadNames.size()>0) ||
287                (allowThreadGroups.size()>0) ||
288                (allowThreadIds.size()>0) ||
289                allowEventThread!=OnlyRunBy.EVENT_THREAD.NO) {
290                if (sb.length()>0) {
291                    sb.append(LF);
292                }
293    
294                StringBuilder sbOnly = new StringBuilder();
295                for(String s: allowThreadNames) {
296                    sbOnly.append(',');
297                    sbOnly.append(LF);
298                    sbOnly.append("\t@ThreadDesc(name=\"");
299                    sbOnly.append(s);
300                    sbOnly.append("\")");
301                }
302                for(String s: allowThreadGroups) {
303                    sbOnly.append(',');
304                    sbOnly.append(LF);
305                    sbOnly.append("\t@ThreadDesc(group=\"");
306                    sbOnly.append(s);
307                    sbOnly.append("\")");
308                }
309                for(Long l: allowThreadIds) {
310                    sbOnly.append(',');
311                    sbOnly.append(LF);
312                    sbOnly.append("\t@ThreadDesc(id=");
313                    sbOnly.append(l);
314                    sbOnly.append(")");
315                }
316                if (allowEventThread==OnlyRunBy.EVENT_THREAD.ONLY) {
317                    sbOnly.append(',');
318                    sbOnly.append(LF);
319                    sbOnly.append("\t@ThreadDesc(eventThread=OnlyRunBy.EVENT_THREAD.ONLY)");
320                }
321                else if (allowEventThread==OnlyRunBy.EVENT_THREAD.ONLY_AFTER_REALIZED) {
322                    sbOnly.append(',');
323                    sbOnly.append(LF);
324                    sbOnly.append("\t@ThreadDesc(eventThread=OnlyRunBy.EVENT_THREAD.ONLY_AFTER_REALIZED)");
325                }
326                sbOnly.append(LF);
327                sbOnly.append("})");
328    
329                sbOnly.deleteCharAt(0); // delete first ','
330    
331                sb.append("@OnlyRunBy({");
332                sb.append(sbOnly.toString());
333            }
334    
335            if (predicateAnnotations.size()>0) {
336                if (sb.length()>0) {
337                    sb.append(LF);
338                }
339                boolean notFirst = false;
340                for(PredicateAnnotationRecord pa: predicateAnnotations) {
341                    if (notFirst) { sb.append(LF); } else { notFirst = true; }
342                    sb.append(pa.toString());
343                }
344            }
345    
346            return sb.toString();
347        }
348    }