| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
/*  | 
 | 
 * This source code is provided to illustrate the usage of a given feature  | 
 | 
 * or technique and has been deliberately simplified. Additional steps  | 
 | 
 * required for a production-quality application, such as security checks,  | 
 | 
 * input validation and proper error handling, might not be present in  | 
 | 
 * this sample code.  | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
package com.sun.tools.example.debug.tty;  | 
 | 
 | 
 | 
import com.sun.jdi.*;  | 
 | 
import com.sun.jdi.request.*;  | 
 | 
 | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.List;  | 
 | 
 | 
 | 
class BreakpointSpec extends EventRequestSpec { | 
 | 
    String methodId;  | 
 | 
    List<String> methodArgs;  | 
 | 
    int lineNumber;  | 
 | 
 | 
 | 
    BreakpointSpec(ReferenceTypeSpec refSpec, int lineNumber) { | 
 | 
        super(refSpec);  | 
 | 
        this.methodId = null;  | 
 | 
        this.methodArgs = null;  | 
 | 
        this.lineNumber = lineNumber;  | 
 | 
    }  | 
 | 
 | 
 | 
    BreakpointSpec(ReferenceTypeSpec refSpec, String methodId,  | 
 | 
                   List<String> methodArgs) throws MalformedMemberNameException { | 
 | 
        super(refSpec);  | 
 | 
        this.methodId = methodId;  | 
 | 
        this.methodArgs = methodArgs;  | 
 | 
        this.lineNumber = 0;  | 
 | 
        if (!isValidMethodName(methodId)) { | 
 | 
            throw new MalformedMemberNameException(methodId);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    EventRequest resolveEventRequest(ReferenceType refType)  | 
 | 
                           throws AmbiguousMethodException,  | 
 | 
                                  AbsentInformationException,  | 
 | 
                                  InvalidTypeException,  | 
 | 
                                  NoSuchMethodException,  | 
 | 
                                  LineNotFoundException { | 
 | 
        Location location = location(refType);  | 
 | 
        if (location == null) { | 
 | 
            throw new InvalidTypeException();  | 
 | 
        }  | 
 | 
        EventRequestManager em = refType.virtualMachine().eventRequestManager();  | 
 | 
        EventRequest bp = em.createBreakpointRequest(location);  | 
 | 
        bp.setSuspendPolicy(suspendPolicy);  | 
 | 
        bp.enable();  | 
 | 
        return bp;  | 
 | 
    }  | 
 | 
 | 
 | 
    String methodName() { | 
 | 
        return methodId;  | 
 | 
    }  | 
 | 
 | 
 | 
    int lineNumber() { | 
 | 
        return lineNumber;  | 
 | 
    }  | 
 | 
 | 
 | 
    List<String> methodArgs() { | 
 | 
        return methodArgs;  | 
 | 
    }  | 
 | 
 | 
 | 
    boolean isMethodBreakpoint() { | 
 | 
        return (methodId != null);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public int hashCode() { | 
 | 
        return refSpec.hashCode() + lineNumber +  | 
 | 
            ((methodId != null) ? methodId.hashCode() : 0) +  | 
 | 
            ((methodArgs != null) ? methodArgs.hashCode() : 0);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public boolean equals(Object obj) { | 
 | 
        if (obj instanceof BreakpointSpec) { | 
 | 
            BreakpointSpec breakpoint = (BreakpointSpec)obj;  | 
 | 
 | 
 | 
            return ((methodId != null) ?  | 
 | 
                        methodId.equals(breakpoint.methodId)  | 
 | 
                      : methodId == breakpoint.methodId) &&  | 
 | 
                   ((methodArgs != null) ?  | 
 | 
                        methodArgs.equals(breakpoint.methodArgs)  | 
 | 
                      : methodArgs == breakpoint.methodArgs) &&  | 
 | 
                   refSpec.equals(breakpoint.refSpec) &&  | 
 | 
                   (lineNumber == breakpoint.lineNumber);  | 
 | 
        } else { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    String errorMessageFor(Exception e) { | 
 | 
        if (e instanceof AmbiguousMethodException) { | 
 | 
            return (MessageOutput.format("Method is overloaded; specify arguments", | 
 | 
                                         methodName()));  | 
 | 
            /*  | 
 | 
             * TO DO: list the methods here  | 
 | 
             */  | 
 | 
        } else if (e instanceof NoSuchMethodException) { | 
 | 
            return (MessageOutput.format("No method in", | 
 | 
                                         new Object [] {methodName(), | 
 | 
                                                        refSpec.toString()}));  | 
 | 
        } else if (e instanceof AbsentInformationException) { | 
 | 
            return (MessageOutput.format("No linenumber information for", | 
 | 
                                         refSpec.toString()));  | 
 | 
        } else if (e instanceof LineNotFoundException) { | 
 | 
            return (MessageOutput.format("No code at line", | 
 | 
                                         new Object [] {new Long (lineNumber()), | 
 | 
                                                        refSpec.toString()}));  | 
 | 
        } else if (e instanceof InvalidTypeException) { | 
 | 
            return (MessageOutput.format("Breakpoints can be located only in classes.", | 
 | 
                                         refSpec.toString()));  | 
 | 
        } else { | 
 | 
            return super.errorMessageFor( e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public String toString() { | 
 | 
        StringBuffer buffer = new StringBuffer(refSpec.toString());  | 
 | 
        if (isMethodBreakpoint()) { | 
 | 
            buffer.append('.'); | 
 | 
            buffer.append(methodId);  | 
 | 
            if (methodArgs != null) { | 
 | 
                boolean first = true;  | 
 | 
                buffer.append('('); | 
 | 
                for (String arg : methodArgs) { | 
 | 
                    if (!first) { | 
 | 
                        buffer.append(','); | 
 | 
                    }  | 
 | 
                    buffer.append(arg);  | 
 | 
                    first = false;  | 
 | 
                }  | 
 | 
                buffer.append(")"); | 
 | 
            }  | 
 | 
        } else { | 
 | 
            buffer.append(':'); | 
 | 
            buffer.append(lineNumber);  | 
 | 
        }  | 
 | 
        return MessageOutput.format("breakpoint", buffer.toString()); | 
 | 
    }  | 
 | 
 | 
 | 
    private Location location(ReferenceType refType) throws  | 
 | 
                                    AmbiguousMethodException,  | 
 | 
                                    AbsentInformationException,  | 
 | 
                                    NoSuchMethodException,  | 
 | 
                                    LineNotFoundException { | 
 | 
        Location location = null;  | 
 | 
        if (isMethodBreakpoint()) { | 
 | 
            Method method = findMatchingMethod(refType);  | 
 | 
            location = method.location();  | 
 | 
        } else { | 
 | 
              | 
 | 
            List<Location> locs = refType.locationsOfLine(lineNumber());  | 
 | 
            if (locs.size() == 0) { | 
 | 
                throw new LineNotFoundException();  | 
 | 
            }  | 
 | 
              | 
 | 
            location = locs.get(0);  | 
 | 
            if (location.method() == null) { | 
 | 
                throw new LineNotFoundException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return location;  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean isValidMethodName(String s) { | 
 | 
        return isJavaIdentifier(s) ||  | 
 | 
               s.equals("<init>") || | 
 | 
               s.equals("<clinit>"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean compareArgTypes(Method method, List<String> nameList) { | 
 | 
        List<String> argTypeNames = method.argumentTypeNames();  | 
 | 
 | 
 | 
          | 
 | 
        if (argTypeNames.size() != nameList.size()) { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        int nTypes = argTypeNames.size();  | 
 | 
        for (int i = 0; i < nTypes; ++i) { | 
 | 
            String comp1 = argTypeNames.get(i);  | 
 | 
            String comp2 = nameList.get(i);  | 
 | 
            if (! comp1.equals(comp2)) { | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (i != nTypes - 1 ||  | 
 | 
                    !method.isVarArgs()  ||  | 
 | 
                    !comp2.endsWith("...")) { | 
 | 
                    return false;  | 
 | 
                }  | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                int comp1Length = comp1.length();  | 
 | 
                if (comp1Length + 1 != comp2.length()) { | 
 | 
                      | 
 | 
                    return false;  | 
 | 
                }  | 
 | 
                  | 
 | 
                if (!comp1.regionMatches(0, comp2, 0, comp1Length - 2)) { | 
 | 
                    return false;  | 
 | 
                }  | 
 | 
                  | 
 | 
                return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private String normalizeArgTypeName(String name) { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        int i = 0;  | 
 | 
        StringBuffer typePart = new StringBuffer();  | 
 | 
        StringBuffer arrayPart = new StringBuffer();  | 
 | 
        name = name.trim();  | 
 | 
        int nameLength = name.length();  | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        boolean isVarArgs = name.endsWith("..."); | 
 | 
        if (isVarArgs) { | 
 | 
            nameLength -= 3;  | 
 | 
        }  | 
 | 
        while (i < nameLength) { | 
 | 
            char c = name.charAt(i);  | 
 | 
            if (Character.isWhitespace(c) || c == '[') { | 
 | 
                break;        | 
 | 
            }  | 
 | 
            typePart.append(c);  | 
 | 
            i++;  | 
 | 
        }  | 
 | 
        while (i < nameLength) { | 
 | 
            char c = name.charAt(i);  | 
 | 
            if ( (c == '[') || (c == ']')) { | 
 | 
                arrayPart.append(c);  | 
 | 
            } else if (!Character.isWhitespace(c)) { | 
 | 
                throw new IllegalArgumentException  | 
 | 
                    (MessageOutput.format("Invalid argument type name")); | 
 | 
            }  | 
 | 
            i++;  | 
 | 
        }  | 
 | 
        name = typePart.toString();  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if ((name.indexOf('.') == -1) || name.startsWith("*.")) { | 
 | 
            try { | 
 | 
                ReferenceType argClass = Env.getReferenceTypeFromToken(name);  | 
 | 
                if (argClass != null) { | 
 | 
                    name = argClass.name();  | 
 | 
                }  | 
 | 
            } catch (IllegalArgumentException e) { | 
 | 
                // We'll try the name as is  | 
 | 
            }  | 
 | 
        }  | 
 | 
        name += arrayPart.toString();  | 
 | 
        if (isVarArgs) { | 
 | 
            name += "...";  | 
 | 
        }  | 
 | 
        return name;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Method findMatchingMethod(ReferenceType refType)  | 
 | 
                                        throws AmbiguousMethodException,  | 
 | 
                                               NoSuchMethodException { | 
 | 
 | 
 | 
          | 
 | 
        List<String> argTypeNames = null;  | 
 | 
        if (methodArgs() != null) { | 
 | 
            argTypeNames = new ArrayList<String>(methodArgs().size());  | 
 | 
            for (String name : methodArgs()) { | 
 | 
                name = normalizeArgTypeName(name);  | 
 | 
                argTypeNames.add(name);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        // Check each method in the class for matches  | 
 | 
        Method firstMatch = null;    | 
 | 
        Method exactMatch = null;    | 
 | 
        int matchCount = 0;          | 
 | 
        for (Method candidate : refType.methods()) { | 
 | 
            if (candidate.name().equals(methodName())) { | 
 | 
                matchCount++;  | 
 | 
 | 
 | 
                  | 
 | 
                if (matchCount == 1) { | 
 | 
                    firstMatch = candidate;  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                if ((argTypeNames != null)  | 
 | 
                        && compareArgTypes(candidate, argTypeNames) == true) { | 
 | 
                    exactMatch = candidate;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        Method method = null;  | 
 | 
        if (exactMatch != null) { | 
 | 
              | 
 | 
            method = exactMatch;  | 
 | 
        } else if ((argTypeNames == null) && (matchCount > 0)) { | 
 | 
              | 
 | 
            if (matchCount == 1) { | 
 | 
                method = firstMatch;         | 
 | 
            } else { | 
 | 
                throw new AmbiguousMethodException();  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            throw new NoSuchMethodException(methodName());  | 
 | 
        }  | 
 | 
        return method;  | 
 | 
    }  | 
 | 
}  |