| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.core.hcr; |
| |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFileState; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTMatcher; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| |
| /** |
| * A <code>CompilationUnitDelta</code> represents the source code changes between |
| * a CU in the workspace and the same CU at some point in the past |
| * (from the local history). |
| * <p> |
| * This functionality is used in the context of Hot Code Replace |
| * to determine which stack frames are affected (and need to be dropped) |
| * by a class reload in the Java VM. |
| * <p> |
| * Typically a <code>CompilationUnitDelta</code> object is generated for a CU |
| * when the associated class is replaced in the VM. |
| */ |
| public class CompilationUnitDelta { |
| |
| /** |
| * AST of the current source code |
| */ |
| private CompilationUnit fCurrentAst; |
| /** |
| * AST of the previous source code |
| */ |
| private CompilationUnit fPrevAst; |
| |
| /** |
| * AST parser |
| */ |
| private ASTParser fParser = null; |
| |
| /** |
| * AST matcher |
| */ |
| private ASTMatcher fMatcher = null; |
| |
| private boolean fHasHistory= false; |
| |
| /** |
| * Creates a new <code>CompilationUnitDelta object that calculates and stores |
| * the changes of the given CU since some point in time. |
| */ |
| public CompilationUnitDelta(ICompilationUnit cu, long timestamp) throws CoreException { |
| |
| if (cu.isWorkingCopy()) { |
| cu= cu.getPrimary(); |
| } |
| |
| // find underlying file |
| IFile file= (IFile) cu.getUnderlyingResource(); |
| |
| // get available editions |
| IFileState[] states= file.getHistory(null); |
| if (states == null || states.length <= 0) { |
| return; |
| } |
| fHasHistory= true; |
| |
| IFileState found= null; |
| // find edition just before the given time stamp |
| for (int i= 0; i < states.length; i++) { |
| IFileState state= states[i]; |
| long d= state.getModificationTime(); |
| if (d < timestamp) { |
| found= state; |
| break; |
| } |
| } |
| |
| if (found == null) { |
| found= states[states.length-1]; |
| } |
| |
| InputStream oldContents= null; |
| InputStream newContents= null; |
| try { |
| oldContents= found.getContents(); |
| newContents= file.getContents(); |
| } catch (CoreException ex) { |
| return; |
| } |
| |
| fPrevAst = parse(oldContents, cu); |
| fCurrentAst = parse(newContents, cu); |
| } |
| |
| /** |
| * Returns <code>true</code> |
| * <ul> |
| * <li>if the source of the given member has been changed, or |
| * <li>if the element has been deleted, or |
| * <li>if the element has been newly created |
| * </ul> |
| * after the initial timestamp. |
| */ |
| public boolean hasChanged(String methodName, String signature) { |
| if (!fHasHistory) { |
| return false; // optimistic: we have no history, so assume that member hasn't changed |
| } |
| if (fPrevAst == null || fCurrentAst == null) { |
| return true; // pessimistic: unable to build parse trees |
| } |
| MethodSearchVisitor visitor = new MethodSearchVisitor(); |
| MethodDeclaration prev = findMethod(fPrevAst, visitor, methodName, signature); |
| if (prev != null) { |
| MethodDeclaration curr = findMethod(fCurrentAst, visitor, methodName, signature); |
| if (curr != null) { |
| return !getMatcher().match(prev, curr); |
| } |
| } |
| return true; |
| } |
| |
| private MethodDeclaration findMethod(CompilationUnit cu, MethodSearchVisitor visitor, String name, String signature) { |
| visitor.setTargetMethod(name, signature); |
| cu.accept(visitor); |
| return visitor.getMatch(); |
| } |
| |
| //---- private stuff ---------------------------------------------------------------- |
| |
| /** |
| * Parses the given input stream and returns a tree of JavaNodes |
| * or a null in case of failure. |
| */ |
| private CompilationUnit parse(InputStream input, ICompilationUnit cu) { |
| |
| char[] buffer= readString(input); |
| if (buffer != null) { |
| if (fParser == null) { |
| fParser = ASTParser.newParser(AST.JLS3); |
| } |
| fParser.setSource(buffer); |
| fParser.setProject(cu.getJavaProject()); |
| fParser.setResolveBindings(true); |
| fParser.setKind(ASTParser.K_COMPILATION_UNIT); |
| fParser.setUnitName(cu.getElementName()); |
| return (CompilationUnit) fParser.createAST(null); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns an AST matcher |
| * |
| * @return AST matcher |
| */ |
| private ASTMatcher getMatcher() { |
| if (fMatcher == null) { |
| fMatcher = new ASTMatcher(); |
| } |
| return fMatcher; |
| } |
| |
| /** |
| * Returns null if an error occurred. |
| */ |
| private char[] readString(InputStream is) { |
| if (is == null) { |
| return null; |
| } |
| BufferedReader reader= null; |
| try { |
| StringBuffer buffer= new StringBuffer(); |
| char[] part= new char[2048]; |
| int read= 0; |
| reader= new BufferedReader(new InputStreamReader(is, ResourcesPlugin.getEncoding())); |
| |
| while ((read= reader.read(part)) != -1) { |
| buffer.append(part, 0, read); |
| } |
| |
| char[] b= new char[buffer.length()]; |
| buffer.getChars(0, b.length, b, 0); |
| return b; |
| |
| } catch (IOException ex) { |
| } finally { |
| if (reader != null) { |
| try { |
| reader.close(); |
| } catch (IOException ex) { |
| } |
| } |
| } |
| return null; |
| } |
| } |