001 package daikon;
002
003 import java.io.ObjectInputStream;
004 import java.io.Serializable;
005 import java.io.IOException;
006 import utilMDE.*;
007
008 /**
009 * PptName is an immutable ADT that represents naming data associated with a
010 * given program point, such as the class or method.
011 *
012 * <p> Examples below are as if the full value of this PptName were
013 * "DataStructures.StackAr.pop()Ljava/lang/Object;:::EXIT84"
014 *
015 * <p>
016 * PptName is deprecated, because declaration file format 2 should not need
017 * it. Uses of PptName should be eliminated.
018 **/
019 // No "@Deprecated" annotation yet, but we should add it once support for
020 // file format 1 is removed from Daikon.
021 public class PptName
022 implements Serializable
023 {
024 // We are Serializable, so we specify a version to allow changes to
025 // method signatures without breaking serialization. If you add or
026 // remove fields, you should change this number to the current date.
027 static final long serialVersionUID = 20020122L;
028
029 // These are never changed but cannot be declared "final", because they
030 // must be re-interned upon deserialization.
031 private /*@Interned*/ String fullname; // interned full program point name
032 // fn_name and point together comprise fullname
033 private /*@Interned*/ String fn_name; // interned; the part of fullname before ":::"
034 private /*@Interned*/ String point; // interned post-separator (separator is ":::")
035 // cls and method together comprise fn_name
036 private /*@Nullable*/ /*@Interned*/ String cls; // interned fully-qualified class name
037 final private /*@Nullable*/ /*@Interned*/ String method; // interned method signature, including types
038
039 // Representation invariant:
040 //
041 // fullname must contain ":::".
042 // fn_name is the part of fullname before ::: and point is the part after.
043 // If fn_name does contain a '(' and a '.' that comes before it, then
044 // cls is the portion before the dot and method is the portion after.
045 // If fn_name contains '(' but no dot, cls is null and method
046 // is the same as fn_name.
047 // If fn_name does not contain '(', then class is the same as fn_name and
048 // method is null.
049
050 // ==================== CONSTRUCTORS ====================
051
052 /**
053 * @param name non-null ppt name as given in the decls file
054 **/
055 public PptName( String name ) {
056 // If the name is well-formed, like "mvspc.setupGUI()V:::EXIT75",
057 // then this constructor will extract the class and method names.
058 // If not (eg for lisp code), it's okay because only the GUI uses
059 // this class/method information.
060
061 fullname = name.intern();
062 int separatorPosition = name.indexOf( FileIO.ppt_tag_separator );
063 if (separatorPosition == -1) {
064 throw new Daikon.TerminationMessage("no ppt_tag_separator in '"+name+"'");
065 // // probably a lisp program, which was instrumented differently
066 // cls = method = point = fn_name = null;
067 // return;
068 }
069 fn_name = name.substring(0, separatorPosition).intern();
070 point = name.substring(separatorPosition + FileIO.ppt_tag_separator.length()).intern();
071
072 int lparen = fn_name.indexOf('(');
073 if (lparen == -1) {
074 cls = fn_name;
075 method = null;
076 return;
077 }
078 int dot = fn_name.lastIndexOf('.', lparen);
079 if (dot == -1) {
080 // throw new Daikon.TerminationMessage("No dot in function name " + fn_name);
081 method = fn_name;
082 cls = null;
083 return;
084 }
085 // now 0 <= dot < lparen
086 cls = fn_name.substring(0, dot).intern();
087 method = fn_name.substring(dot + 1).intern();
088 }
089
090 /**
091 * className or methodName (or both) must be non-null
092 **/
093 public PptName(/*@Nullable*/ String className, /*@Nullable*/ String methodName, String pointName) {
094 if ((className == null) && (methodName == null)) {
095 throw new UnsupportedOperationException
096 ("One of class or method must be non-null");
097 }
098 // First set class name
099 if (className != null) {
100 cls = className.intern();
101 fn_name = cls;
102 }
103 // Then add method name
104 if (methodName == null) {
105 method = null;
106 } else {
107 method = methodName.intern();
108 if (cls != null) {
109 fn_name = (cls + "." + method).intern();
110 } else {
111 fn_name = method;
112 }
113 }
114 assert fn_name != null;
115 // Finally, add point
116 point = pointName.intern();
117 fullname = (fn_name + FileIO.ppt_tag_separator + point).intern();
118 }
119
120 // ==================== OBSERVERS ====================
121
122 /**
123 * @return getName() [convenience accessor]
124 * @see #getName()
125 **/
126 public String name() {
127 return getName();
128 }
129
130 /**
131 * @return the complete program point name
132 * e.g. "DataStructures.StackAr.pop()Ljava/lang/Object;:::EXIT84"
133 **/
134 public String getName() {
135 return fullname;
136 }
137
138 /**
139 * @return the fully-qualified class name, which uniquely identifies
140 * a given class.
141 * May be null.
142 * e.g. "DataStructures.StackAr"
143 **/
144 public /*@Nullable*/ String getFullClassName() {
145 return cls;
146 }
147
148 /**
149 * @return the short name of the class, not including any
150 * additional context, such as the package it is in.
151 * May be null.
152 * e.g. "StackAr"
153 **/
154 public /*@Nullable*/ String getShortClassName() {
155 if (cls == null) return null;
156 int pt = cls.lastIndexOf('.');
157 if (pt == -1)
158 return cls;
159 else
160 return cls.substring(pt+1);
161 }
162
163 /**
164 * @return a guess at the package name.
165 * May be null.
166 **/
167 public /*@Nullable*/ String getPackageName() {
168 if (cls == null) return null;
169 int pt = cls.lastIndexOf('.');
170 if (pt == -1)
171 return null;
172 else
173 return cls.substring(0, pt);
174 }
175
176 /**
177 * @return the full name which can uniquely identify a method within
178 * a class. The name includes symbols for the argument types and
179 * return type.
180 * May be null.
181 * e.g. "pop()Ljava/lang/Object;"
182 **/
183 public /*@Nullable*/ String getSignature() {
184 return method;
185 }
186
187 /**
188 * @return the name (identifier) of the method, not taking into
189 * account any arguments, return values, etc.
190 * May be null.
191 * e.g. "pop"
192 **/
193 public /*@Nullable*/ String getMethodName() {
194 if (method == null) return null;
195 int lparen = method.indexOf('(');
196 assert lparen >= 0;
197 return method.substring(0, lparen);
198 }
199
200 /**
201 * @return the fully-qualified class and method name (and signature).
202 * Does not include any point information (such as ENTER or EXIT).
203 * May be null.
204 * e.g. "DataStructures.StackAr.pop()Ljava/lang/Object;"
205 **/
206 public /*@Nullable*/ /*@Interned*/ String getNameWithoutPoint() {
207 return fn_name;
208 // if (cls == null && method == null) return null;
209 // if (cls == null) return method;
210 // if (method == null) return cls;
211 // return (cls + "." + method).intern();
212 }
213
214 /**
215 * @return something interesting and descriptive about the point in
216 * question, along the lines of "ENTER" or "EXIT" or somesuch. The
217 * semantics of this method are not yet decided, so don't try to do
218 * aynthing useful with this result.
219 * May be null.
220 * e.g. "EXIT84"
221 **/
222 public /*@Nullable*/ String getPoint() {
223 return point;
224 }
225
226 /**
227 * @return a numerical subscript of the given point, or
228 * Integer.MIN_VALUE if none exists.
229 * e.g. "84"
230 * @see #exitLine()
231 **/
232 public int getPointSubscript() {
233 int result = Integer.MIN_VALUE;
234 if (point != null) {
235 // returns the largest substring [i..] which parses to an integer
236 for (int i = 0; i < point.length(); i++) {
237 char c = point.charAt(i);
238 if (('0' <= c) && (c <= '9')) {
239 try {
240 result = Integer.parseInt(point.substring(i));
241 break;
242 } catch (NumberFormatException e) {
243 }
244 }
245 }
246 }
247 return result;
248 }
249
250 /**
251 * @return true iff this name refers to a synthetic object instance
252 * program point
253 **/
254 public boolean isObjectInstanceSynthetic() {
255 return FileIO.object_suffix.equals(point);
256 }
257
258 /**
259 * @return true iff this name refers to a synthetic class instance
260 * program point
261 **/
262 public boolean isClassStaticSynthetic() {
263 return FileIO.class_static_suffix.equals(point);
264 }
265
266 /**
267 * @return true iff this name refers to program globals
268 **/
269 public boolean isGlobalPoint() {
270 return FileIO.global_suffix.equals (point);
271 }
272
273 /**
274 * @return true iff this name refers to a procedure exit point
275 **/
276 public boolean isExitPoint() {
277 return (point != null) && point.startsWith(FileIO.exit_suffix);
278 }
279
280 /**
281 * @return true iff this name refers to an abrupt completion point
282 **/
283 public boolean isThrowsPoint() {
284 return (point != null) && point.startsWith(FileIO.throws_suffix);
285 }
286
287 /**
288 * @return true iff this name refers to a combined (synthetic) procedure
289 * exit point
290 **/
291 public boolean isCombinedExitPoint() {
292 return (point != null) && point.equals(FileIO.exit_suffix);
293 }
294
295 /**
296 * @return true iff this name refers to an actual (not combined)
297 * procedure exit point (eg, EXIT22)
298 */
299 public boolean isNumberedExitPoint() {
300 return ((point != null) && (isExitPoint() && !isCombinedExitPoint()));
301 }
302
303 /**
304 * @return true iff this name refers to a procedure exit point
305 **/
306 public boolean isEnterPoint() {
307 return (point != null) && point.startsWith(FileIO.enter_suffix);
308 }
309
310 /**
311 * @return a string containing the line number, if this is an exit point;
312 * otherwise, return an empty string
313 * @see #getPointSubscript()
314 **/
315 public String exitLine() {
316 if (!isExitPoint())
317 return "";
318 int non_digit;
319 for (non_digit=FileIO.exit_suffix.length(); non_digit<point.length(); non_digit++) {
320 if (! Character.isDigit(point.charAt(non_digit)))
321 break;
322 }
323 return point.substring(FileIO.exit_suffix.length(), non_digit);
324 }
325
326 /**
327 * @return true iff this program point is a constructor entry or exit.
328 * There are two ways in which this works. With the older declaration
329 * format, the method name starts with <init>. The newer declaration
330 * format does not have <init> but their method name includes the class
331 * name. For compatibility both mechanisms are checked.
332 **/
333 public boolean isConstructor() {
334
335 if (method != null) {
336
337 if (method.startsWith ("<init>"))
338 return (true);
339
340 if (cls == null)
341 return (false);
342
343 String class_name = UtilMDE.unqualified_name (cls);
344 assert method != null; // for nullness checker
345 int arg_start = method.indexOf ('(');
346 String method_name = method;
347 if (arg_start != -1)
348 method_name = method.substring (0, arg_start);
349
350 // System.out.println ("fullname = " + fullname);
351 // System.out.println ("fn_name = " + fn_name);
352 // System.out.println ("method = " + method);
353 // System.out.println ("cls = " + cls);
354 // System.out.println ("class_name = " + class_name);
355 // System.out.println ("method_name = " + method_name);
356
357 if (class_name.equals (method_name))
358 return (true);
359 }
360
361 return (false);
362
363 }
364
365
366 /** Debugging output **/
367 public String repr() {
368 return "PptName: fullname=" + fullname
369 + "; fn_name=" + fn_name
370 + "; point=" + point
371 + "; cls=" + cls
372 + "; method=" + method;
373 }
374
375
376 // ==================== PRODUCERS ====================
377
378 /**
379 * Requires: this.isExitPoint()
380 * @return a name for the corresponding enter point
381 **/
382 public PptName makeEnter() {
383 // This associates throw points with the main entry point.
384 // We may wish to have a different exceptional than non-exceptional
385 // entry point; in particular, if there was an exception, then perhaps
386 // the precondition or object invariant was not met.
387 assert isExitPoint() : fullname;
388
389 assert isExitPoint() || isThrowsPoint();
390 return new PptName(cls, method, FileIO.enter_suffix);
391 }
392
393 /**
394 * Requires: this.isExitPoint() || this.isEnterPoint()
395 * @return a name for the combined exit point
396 **/
397 public PptName makeExit() {
398 assert isExitPoint() || isEnterPoint() : fullname;
399 return new PptName(cls, method, FileIO.exit_suffix);
400 }
401
402 /**
403 * Requires: this.isExitPoint() || this.isEnterPoint()
404 * @return a name for the corresponding object invariant
405 **/
406 public PptName makeObject() {
407 assert isExitPoint() || isEnterPoint() : fullname;
408 return new PptName(cls, null, FileIO.object_suffix);
409 }
410
411 /**
412 * Requires: this.isExitPoint() || this.isEnterPoint() || this.isObjectInstanceSynthetic()
413 * @return a name for the corresponding class-static invariant
414 **/
415 public PptName makeClassStatic() {
416 assert isExitPoint() || isEnterPoint() || isObjectInstanceSynthetic() : fullname;
417 return new PptName(cls, null, FileIO.class_static_suffix);
418 }
419
420 // ==================== OBJECT METHODS ====================
421
422 /* @return interned string such that this.equals(new PptName(this.toString())) */
423 public String toString() {
424 return fullname;
425 }
426
427 public boolean equals(/*@Nullable*/ Object o) {
428 return (o instanceof PptName) && equals((PptName) o);
429 }
430
431 public boolean equals(PptName o) {
432 return (o != null) && (o.fullname == fullname);
433 }
434
435 public int hashCode() {
436 return fullname.hashCode();
437 }
438
439 // Interning is lost when an object is serialized and deserialized.
440 // Manually re-intern any interned fields upon deserialization.
441 private void readObject(ObjectInputStream in)
442 throws IOException, ClassNotFoundException
443 {
444 try {
445 in.defaultReadObject();
446 if (fullname != null)
447 fullname = fullname.intern();
448 if (fn_name != null)
449 fn_name = fn_name.intern();
450 if (cls != null)
451 cls = cls.intern();
452 if (method != null) {
453 // method = method.intern();
454 UtilMDE.setFinalField(this, "method", method.intern());
455 }
456 if (point != null)
457 point = point.intern();
458 } catch (NoSuchFieldException e) {
459 throw new Error(e);
460 } catch (IllegalAccessException e) {
461 throw new Error(e);
462 }
463 }
464
465 }