001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.surround;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.List;
015:
016: import org.eclipse.text.edits.MultiTextEdit;
017: import org.eclipse.text.edits.TextEdit;
018: import org.eclipse.text.edits.TextEditGroup;
019:
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IProgressMonitor;
022: import org.eclipse.core.runtime.NullProgressMonitor;
023:
024: import org.eclipse.jface.text.ITextSelection;
025:
026: import org.eclipse.ltk.core.refactoring.Change;
027: import org.eclipse.ltk.core.refactoring.Refactoring;
028: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
029: import org.eclipse.ltk.core.refactoring.TextFileChange;
030:
031: import org.eclipse.jdt.core.ICompilationUnit;
032: import org.eclipse.jdt.core.JavaModelException;
033: import org.eclipse.jdt.core.dom.AST;
034: import org.eclipse.jdt.core.dom.ASTNode;
035: import org.eclipse.jdt.core.dom.Assignment;
036: import org.eclipse.jdt.core.dom.Block;
037: import org.eclipse.jdt.core.dom.CatchClause;
038: import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
039: import org.eclipse.jdt.core.dom.CompilationUnit;
040: import org.eclipse.jdt.core.dom.Expression;
041: import org.eclipse.jdt.core.dom.IExtendedModifier;
042: import org.eclipse.jdt.core.dom.ITypeBinding;
043: import org.eclipse.jdt.core.dom.IVariableBinding;
044: import org.eclipse.jdt.core.dom.Modifier;
045: import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
046: import org.eclipse.jdt.core.dom.Statement;
047: import org.eclipse.jdt.core.dom.TryStatement;
048: import org.eclipse.jdt.core.dom.Type;
049: import org.eclipse.jdt.core.dom.VariableDeclaration;
050: import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
051: import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
052: import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
053: import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
054: import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
055:
056: import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
057: import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
058: import org.eclipse.jdt.internal.corext.dom.CodeScopeBuilder;
059: import org.eclipse.jdt.internal.corext.dom.Selection;
060: import org.eclipse.jdt.internal.corext.refactoring.Checks;
061: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
062: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
063: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
064: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
065: import org.eclipse.jdt.internal.corext.refactoring.util.SelectionAwareSourceRangeComputer;
066: import org.eclipse.jdt.internal.corext.util.Strings;
067:
068: /**
069: * Surround a set of statements with a try/catch block.
070: *
071: * Special case:
072: *
073: * URL url= file.toURL();
074: *
075: * In this case the variable declaration statement gets convert into a
076: * declaration without initializer. So the body of the try/catch block
077: * only consists of new assignments. In this case we can't move the
078: * selected nodes (e.g. the declaration) into the try block.
079: */
080: public class SurroundWithTryCatchRefactoring extends Refactoring {
081:
082: private Selection fSelection;
083: private ISurroundWithTryCatchQuery fQuery;
084: private SurroundWithTryCatchAnalyzer fAnalyzer;
085: private boolean fLeaveDirty;
086:
087: private ICompilationUnit fCUnit;
088: private CompilationUnit fRootNode;
089: private ASTRewrite fRewriter;
090: private ImportRewrite fImportRewrite;
091: private CodeScopeBuilder.Scope fScope;
092: private ASTNode[] fSelectedNodes;
093:
094: private SurroundWithTryCatchRefactoring(ICompilationUnit cu,
095: Selection selection, ISurroundWithTryCatchQuery query) {
096: fCUnit = cu;
097: fSelection = selection;
098: fQuery = query;
099: fLeaveDirty = false;
100: }
101:
102: public static SurroundWithTryCatchRefactoring create(
103: ICompilationUnit cu, ITextSelection selection,
104: ISurroundWithTryCatchQuery query) {
105: return new SurroundWithTryCatchRefactoring(cu, Selection
106: .createFromStartLength(selection.getOffset(), selection
107: .getLength()), query);
108: }
109:
110: public static SurroundWithTryCatchRefactoring create(
111: ICompilationUnit cu, int offset, int length,
112: ISurroundWithTryCatchQuery query) {
113: return new SurroundWithTryCatchRefactoring(cu, Selection
114: .createFromStartLength(offset, length), query);
115: }
116:
117: public void setLeaveDirty(boolean leaveDirty) {
118: fLeaveDirty = leaveDirty;
119: }
120:
121: public boolean stopExecution() {
122: if (fAnalyzer == null)
123: return true;
124: ITypeBinding[] exceptions = fAnalyzer.getExceptions();
125: return exceptions == null || exceptions.length == 0;
126: }
127:
128: /* non Java-doc
129: * @see IRefactoring#getName()
130: */
131: public String getName() {
132: return RefactoringCoreMessages.SurroundWithTryCatchRefactoring_name;
133: }
134:
135: public RefactoringStatus checkActivationBasics(
136: CompilationUnit rootNode) throws JavaModelException {
137: RefactoringStatus result = new RefactoringStatus();
138: fRootNode = rootNode;
139:
140: fAnalyzer = new SurroundWithTryCatchAnalyzer(fCUnit,
141: fSelection, fQuery);
142: fRootNode.accept(fAnalyzer);
143: result.merge(fAnalyzer.getStatus());
144: return result;
145: }
146:
147: /*
148: * @see Refactoring#checkActivation(IProgressMonitor)
149: */
150: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
151: throws CoreException {
152: CompilationUnit rootNode = new RefactoringASTParser(AST.JLS3)
153: .parse(fCUnit, true, pm);
154: return checkActivationBasics(rootNode);
155: }
156:
157: /*
158: * @see Refactoring#checkInput(IProgressMonitor)
159: */
160: public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
161: throws CoreException {
162: return Checks.validateModifiesFiles(ResourceUtil
163: .getFiles(new ICompilationUnit[] { fCUnit }),
164: getValidationContext());
165: }
166:
167: /* non Java-doc
168: * @see IRefactoring#createChange(IProgressMonitor)
169: */
170: public Change createChange(IProgressMonitor pm)
171: throws CoreException {
172: final String NN = ""; //$NON-NLS-1$
173: if (pm == null)
174: pm = new NullProgressMonitor();
175: pm.beginTask(NN, 2);
176: try {
177: final CompilationUnitChange result = new CompilationUnitChange(
178: getName(), fCUnit);
179: if (fLeaveDirty)
180: result.setSaveMode(TextFileChange.LEAVE_DIRTY);
181: MultiTextEdit root = new MultiTextEdit();
182: result.setEdit(root);
183: fRewriter = ASTRewrite.create(fAnalyzer
184: .getEnclosingBodyDeclaration().getAST());
185: fRewriter
186: .setTargetSourceRangeComputer(new SelectionAwareSourceRangeComputer(
187: fAnalyzer.getSelectedNodes(), fCUnit
188: .getBuffer(), fSelection
189: .getOffset(), fSelection
190: .getLength()));
191: fImportRewrite = StubUtility.createImportRewrite(fRootNode,
192: true);
193:
194: fScope = CodeScopeBuilder
195: .perform(fAnalyzer.getEnclosingBodyDeclaration(),
196: fSelection).findScope(
197: fSelection.getOffset(),
198: fSelection.getLength());
199: fScope.setCursor(fSelection.getOffset());
200:
201: fSelectedNodes = fAnalyzer.getSelectedNodes();
202:
203: createTryCatchStatement(fCUnit.getBuffer(), fCUnit
204: .findRecommendedLineSeparator());
205:
206: if (fImportRewrite.hasRecordedChanges()) {
207: TextEdit edit = fImportRewrite.rewriteImports(null);
208: root.addChild(edit);
209: result.addTextEditGroup(new TextEditGroup(NN,
210: new TextEdit[] { edit }));
211: }
212: TextEdit change = fRewriter.rewriteAST();
213: root.addChild(change);
214: result.addTextEditGroup(new TextEditGroup(NN,
215: new TextEdit[] { change }));
216: return result;
217: } finally {
218: pm.done();
219: }
220: }
221:
222: private AST getAST() {
223: return fRootNode.getAST();
224: }
225:
226: private void createTryCatchStatement(
227: org.eclipse.jdt.core.IBuffer buffer, String lineDelimiter)
228: throws CoreException {
229: List result = new ArrayList(1);
230: TryStatement tryStatement = getAST().newTryStatement();
231: ITypeBinding[] exceptions = fAnalyzer.getExceptions();
232: for (int i = 0; i < exceptions.length; i++) {
233: ITypeBinding exception = exceptions[i];
234: String type = fImportRewrite.addImport(exception);
235: CatchClause catchClause = getAST().newCatchClause();
236: tryStatement.catchClauses().add(catchClause);
237: SingleVariableDeclaration decl = getAST()
238: .newSingleVariableDeclaration();
239: String varName = StubUtility
240: .getExceptionVariableName(fCUnit.getJavaProject());
241:
242: String name = fScope.createName(varName, false);
243: decl.setName(getAST().newSimpleName(name));
244: decl.setType(ASTNodeFactory.newType(getAST(), type));
245: catchClause.setException(decl);
246: Statement st = getCatchBody(type, name, lineDelimiter);
247: if (st != null) {
248: catchClause.getBody().statements().add(st);
249: }
250: }
251: List variableDeclarations = getSpecialVariableDeclarationStatements();
252: ListRewrite statements = fRewriter.getListRewrite(tryStatement
253: .getBody(), Block.STATEMENTS_PROPERTY);
254: boolean selectedNodeRemoved = false;
255: ASTNode expressionStatement = null;
256: for (int i = 0; i < fSelectedNodes.length; i++) {
257: ASTNode node = fSelectedNodes[i];
258: if (node instanceof VariableDeclarationStatement
259: && variableDeclarations.contains(node)) {
260: AST ast = getAST();
261: VariableDeclarationStatement statement = (VariableDeclarationStatement) node;
262: // Create a copy and remove the initializer
263: VariableDeclarationStatement copy = (VariableDeclarationStatement) ASTNode
264: .copySubtree(ast, statement);
265: List modifiers = copy.modifiers();
266: for (Iterator iter = modifiers.iterator(); iter
267: .hasNext();) {
268: IExtendedModifier modifier = (IExtendedModifier) iter
269: .next();
270: if (modifier.isModifier()
271: && Modifier.isFinal(((Modifier) modifier)
272: .getKeyword().toFlagValue())) {
273: iter.remove();
274: }
275: }
276: List fragments = copy.fragments();
277: for (Iterator iter = fragments.iterator(), original = statement
278: .fragments().iterator(); iter.hasNext();) {
279: VariableDeclarationFragment fragment = (VariableDeclarationFragment) iter
280: .next();
281: IVariableBinding binding = ((VariableDeclarationFragment) original
282: .next()).resolveBinding();
283: // If we want to initialize the new local then we should do a flow analysis upfront
284: // to decide if the first access is a read or write.
285: if (true /* binding == null */) {
286: fragment.setInitializer(null);
287: } else {
288: fragment.setInitializer(ASTNodeFactory
289: .newDefaultExpression(ast, binding
290: .getType()));
291: }
292: }
293: CompilationUnit root = (CompilationUnit) statement
294: .getRoot();
295: int extendedStart = root
296: .getExtendedStartPosition(statement);
297: // we have a leading comment and the comment is covered by the selection
298: if (extendedStart != statement.getStartPosition()
299: && extendedStart >= fSelection.getOffset()) {
300: String commentToken = buffer.getText(extendedStart,
301: statement.getStartPosition()
302: - extendedStart);
303: commentToken = Strings
304: .trimTrailingTabsAndSpaces(commentToken);
305: Type type = statement.getType();
306: String typeName = buffer.getText(type
307: .getStartPosition(), type.getLength());
308: copy.setType((Type) fRewriter
309: .createStringPlaceholder(commentToken
310: + typeName, type.getNodeType()));
311: }
312: result.add(copy);
313: // convert the fragments into expression statements
314: fragments = statement.fragments();
315: if (!fragments.isEmpty()) {
316: List newExpressionStatements = new ArrayList();
317: for (Iterator iter = fragments.iterator(); iter
318: .hasNext();) {
319: VariableDeclarationFragment fragment = (VariableDeclarationFragment) iter
320: .next();
321: Expression initializer = fragment
322: .getInitializer();
323: if (initializer != null) {
324: Assignment assignment = ast.newAssignment();
325: assignment
326: .setLeftHandSide((Expression) fRewriter
327: .createCopyTarget(fragment
328: .getName()));
329: assignment
330: .setRightHandSide((Expression) fRewriter
331: .createCopyTarget(initializer));
332: newExpressionStatements
333: .add(ast
334: .newExpressionStatement(assignment));
335: }
336: }
337: if (!newExpressionStatements.isEmpty()) {
338: if (fSelectedNodes.length == 1) {
339: expressionStatement = fRewriter
340: .createGroupNode((ASTNode[]) newExpressionStatements
341: .toArray(new ASTNode[newExpressionStatements
342: .size()]));
343: } else {
344: fRewriter
345: .replace(
346: statement,
347: fRewriter
348: .createGroupNode((ASTNode[]) newExpressionStatements
349: .toArray(new ASTNode[newExpressionStatements
350: .size()])),
351: null);
352: }
353: } else {
354: fRewriter.remove(statement, null);
355: selectedNodeRemoved = true;
356: }
357: } else {
358: fRewriter.remove(statement, null);
359: selectedNodeRemoved = true;
360: }
361: }
362: }
363: result.add(tryStatement);
364: ASTNode replacementNode;
365: if (result.size() == 1) {
366: replacementNode = (ASTNode) result.get(0);
367: } else {
368: replacementNode = fRewriter
369: .createGroupNode((ASTNode[]) result
370: .toArray(new ASTNode[result.size()]));
371: }
372: if (fSelectedNodes.length == 1) {
373: if (expressionStatement != null) {
374: statements.insertLast(expressionStatement, null);
375: } else {
376: if (!selectedNodeRemoved)
377: statements.insertLast(fRewriter
378: .createMoveTarget(fSelectedNodes[0]), null);
379: }
380: fRewriter.replace(fSelectedNodes[0], replacementNode, null);
381: } else {
382: ListRewrite source = fRewriter.getListRewrite(
383: fSelectedNodes[0].getParent(),
384: (ChildListPropertyDescriptor) fSelectedNodes[0]
385: .getLocationInParent());
386: ASTNode toMove = source.createMoveTarget(fSelectedNodes[0],
387: fSelectedNodes[fSelectedNodes.length - 1],
388: replacementNode, null);
389: statements.insertLast(toMove, null);
390: }
391: }
392:
393: private List getSpecialVariableDeclarationStatements() {
394: List result = new ArrayList(3);
395: VariableDeclaration[] locals = fAnalyzer.getAffectedLocals();
396: for (int i = 0; i < locals.length; i++) {
397: ASTNode parent = locals[i].getParent();
398: if (parent instanceof VariableDeclarationStatement
399: && !result.contains(parent))
400: result.add(parent);
401: }
402: return result;
403:
404: }
405:
406: private Statement getCatchBody(String type, String name,
407: String lineSeparator) throws CoreException {
408: String s = StubUtility.getCatchBodyContent(fCUnit, type, name,
409: fSelectedNodes[0], lineSeparator);
410: if (s == null) {
411: return null;
412: } else {
413: return (Statement) fRewriter.createStringPlaceholder(s,
414: ASTNode.RETURN_STATEMENT);
415: }
416: }
417: }
|