/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.sorround; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.Assignment; import org.eclipse.che.ide.ext.java.jdt.core.dom.Block; import org.eclipse.che.ide.ext.java.jdt.core.dom.CatchClause; import org.eclipse.che.ide.ext.java.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ExpressionStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.IExtendedModifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.Modifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Statement; import org.eclipse.che.ide.ext.java.jdt.core.dom.TryStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; import org.eclipse.che.ide.ext.java.jdt.core.dom.UnionType; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.core.dom.CodeScopeBuilder; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ASTResolving; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.Selection; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.code.CompilationUnitChange; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.util.SelectionAwareSourceRangeComputer; import org.eclipse.che.ide.ext.java.jdt.internal.corext.util.Strings; import org.eclipse.che.ide.ext.java.jdt.refactoring.Change; import org.eclipse.che.ide.ext.java.jdt.refactoring.Refactoring; import org.eclipse.che.ide.ext.java.jdt.refactoring.RefactoringStatus; import org.eclipse.che.ide.ext.java.jdt.refactoring.TextFileChange; import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.edits.MultiTextEdit; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEdit; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEditGroup; import org.eclipse.che.ide.runtime.CoreException; import org.eclipse.che.ide.runtime.OperationCanceledException; import org.eclipse.che.ide.api.text.BadLocationException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Surround a set of statements with a try/catch block or a try/multi-catch block. * <p/> * Special case: * <p/> * URL url= file.toURL(); * <p/> * In this case the variable declaration statement gets convert into a * declaration without initializer. So the body of the try/catch block * only consists of new assignments. In this case we can't move the * selected nodes (e.g. the declaration) into the try block. */ public class SurroundWithTryCatchRefactoring extends Refactoring { public static final String GROUP_EXC_TYPE = "exc_type"; //$NON-NLS-1$ public static final String GROUP_EXC_NAME = "exc_name"; //$NON-NLS-1$ private Selection fSelection; private SurroundWithTryCatchAnalyzer fAnalyzer; private boolean fLeaveDirty; private CompilationUnit fRootNode; private ASTRewrite fRewriter; private ImportRewrite fImportRewrite; private CodeScopeBuilder.Scope fScope; private ASTNode[] fSelectedNodes; private final boolean fIsMultiCatch; private final Document document; private SurroundWithTryCatchRefactoring(Document document, Selection selection, boolean isMultiCatch) { this.document = document; fSelection = selection; fIsMultiCatch = isMultiCatch; fLeaveDirty = false; } // public static SurroundWithTryCatchRefactoring create(ITextSelection selection) // { // return create(cu, selection, false); // } public static SurroundWithTryCatchRefactoring create(Document document, int offset, int length) { return create(document, offset, length, false); } // public static SurroundWithTryCatchRefactoring create(ICompilationUnit cu, ITextSelection selection, // boolean isMultiCatch) // { // return new SurroundWithTryCatchRefactoring(cu, Selection.createFromStartLength(selection.getOffset(), // selection.getLength()), isMultiCatch); // } public static SurroundWithTryCatchRefactoring create(Document document, int offset, int length, boolean isMultiCatch) { return new SurroundWithTryCatchRefactoring(document, Selection.createFromStartLength(offset, length), isMultiCatch); } // public LinkedProposalModel getLinkedProposalModel() // { // return fLinkedProposalModel; // } public void setLeaveDirty(boolean leaveDirty) { fLeaveDirty = leaveDirty; } public boolean stopExecution() { if (fAnalyzer == null) { return true; } ITypeBinding[] exceptions = fAnalyzer.getExceptions(); return exceptions == null || exceptions.length == 0; } /* non Java-doc * @see IRefactoring#getName() */ @Override public String getName() { return RefactoringCoreMessages.INSTANCE.SurroundWithTryCatchRefactoring_name(); } public RefactoringStatus checkActivationBasics(Document document, CompilationUnit rootNode) throws CoreException { RefactoringStatus result = new RefactoringStatus(); fRootNode = rootNode; fAnalyzer = new SurroundWithTryCatchAnalyzer(document, fSelection); fRootNode.accept(fAnalyzer); result.merge(fAnalyzer.getStatus()); ITypeBinding[] exceptions = fAnalyzer.getExceptions(); if (fIsMultiCatch && (exceptions == null || exceptions.length <= 1)) { result.merge(RefactoringStatus.createWarningStatus(RefactoringCoreMessages.INSTANCE .SurroundWithTryCatchRefactoring_notMultipleexceptions())); } return result; } /* * @see Refactoring#checkActivation(IProgressMonitor) */ @Override public RefactoringStatus checkInitialConditions() throws CoreException { CompilationUnit rootNode = ASTResolving.createQuickFixAST(document); return checkActivationBasics(document, rootNode); } // /* // * @see Refactoring#checkInput(IProgressMonitor) // */ // @Override // public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException // { // return Checks // .validateModifiesFiles(ResourceUtil.getFiles(new ICompilationUnit[]{fCUnit}), getValidationContext()); // } /* non Java-doc * @see IRefactoring#createChange(IProgressMonitor) */ @Override public Change createChange() throws CoreException { final String NN = ""; //$NON-NLS-1$ final CompilationUnitChange result = new CompilationUnitChange(getName(), document); if (fLeaveDirty) { result.setSaveMode(TextFileChange.LEAVE_DIRTY); } MultiTextEdit root = new MultiTextEdit(); result.setEdit(root); fRewriter = ASTRewrite.create(fAnalyzer.getEnclosingBodyDeclaration().getAST()); fRewriter.setTargetSourceRangeComputer(new SelectionAwareSourceRangeComputer(fAnalyzer.getSelectedNodes(), document, fSelection.getOffset(), fSelection.getLength())); fImportRewrite = StubUtility.createImportRewrite(document, fRootNode, true); // fLinkedProposalModel = new LinkedProposalModel(); fScope = CodeScopeBuilder.perform(fAnalyzer.getEnclosingBodyDeclaration(), fSelection).findScope( fSelection.getOffset(), fSelection.getLength()); fScope.setCursor(fSelection.getOffset()); fSelectedNodes = fAnalyzer.getSelectedNodes(); try { createTryCatchStatement(document, "\n"); } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (fImportRewrite.hasRecordedChanges()) { TextEdit edit = fImportRewrite.rewriteImports(); root.addChild(edit); result.addTextEditGroup(new TextEditGroup(NN, new TextEdit[]{edit})); } TextEdit change = fRewriter.rewriteAST(document, WorkerMessageHandler.get().getOptions()); root.addChild(change); result.addTextEditGroup(new TextEditGroup(NN, new TextEdit[]{change})); return result; } private AST getAST() { return fRootNode.getAST(); } private void createTryCatchStatement(Document document, String lineDelimiter) throws CoreException, BadLocationException { List<Statement> result = new ArrayList<Statement>(1); TryStatement tryStatement = getAST().newTryStatement(); ITypeBinding[] exceptions = fAnalyzer.getExceptions(); ImportRewriteContext context = new ContextSensitiveImportRewriteContext(fAnalyzer.getEnclosingBodyDeclaration(), fImportRewrite); if (!fIsMultiCatch) { for (int i = 0; i < exceptions.length; i++) { ITypeBinding exception = exceptions[i]; String type = fImportRewrite.addImport(exception, context); CatchClause catchClause = getAST().newCatchClause(); tryStatement.catchClauses().add(catchClause); SingleVariableDeclaration decl = getAST().newSingleVariableDeclaration(); String varName = StubUtility.getExceptionVariableName(); String name = fScope.createName(varName, false); decl.setName(getAST().newSimpleName(name)); decl.setType(ASTNodeFactory.newType(getAST(), type)); catchClause.setException(decl); Statement st = getCatchBody(type, name, lineDelimiter); if (st != null) { catchClause.getBody().statements().add(st); } // fLinkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition( // fRewriter.track(decl.getType()), i == 0); // fLinkedProposalModel.getPositionGroup(GROUP_EXC_NAME + i, true).addPosition( // fRewriter.track(decl.getName()), false); } } else { CatchClause catchClause = getAST().newCatchClause(); SingleVariableDeclaration decl = getAST().newSingleVariableDeclaration(); String varName = StubUtility.getExceptionVariableName(); String name = fScope.createName(varName, false); decl.setName(getAST().newSimpleName(name)); UnionType unionType = getAST().newUnionType(); List<Type> types = unionType.types(); for (int i = 0; i < exceptions.length; i++) { ITypeBinding exception = exceptions[i]; Type type = fImportRewrite.addImport(exception, getAST(), context); types.add(type); // fLinkedProposalModel.getPositionGroup(GROUP_EXC_TYPE + i, true).addPosition(fRewriter.track(type), i == 0); } decl.setType(unionType); catchClause.setException(decl); // fLinkedProposalModel.getPositionGroup(GROUP_EXC_NAME + 0, true).addPosition(fRewriter.track(decl.getName()), // false); Statement st = getCatchBody("Exception", name, lineDelimiter); //$NON-NLS-1$ if (st != null) { catchClause.getBody().statements().add(st); } tryStatement.catchClauses().add(catchClause); } List<ASTNode> variableDeclarations = getSpecialVariableDeclarationStatements(); ListRewrite statements = fRewriter.getListRewrite(tryStatement.getBody(), Block.STATEMENTS_PROPERTY); boolean selectedNodeRemoved = false; ASTNode expressionStatement = null; for (int i = 0; i < fSelectedNodes.length; i++) { ASTNode node = fSelectedNodes[i]; if (node instanceof VariableDeclarationStatement && variableDeclarations.contains(node)) { AST ast = getAST(); VariableDeclarationStatement statement = (VariableDeclarationStatement)node; // Create a copy and remove the initializer VariableDeclarationStatement copy = (VariableDeclarationStatement)ASTNode.copySubtree(ast, statement); List<IExtendedModifier> modifiers = copy.modifiers(); for (Iterator<IExtendedModifier> iter = modifiers.iterator(); iter.hasNext(); ) { IExtendedModifier modifier = iter.next(); if (modifier.isModifier() && Modifier.isFinal(((Modifier)modifier).getKeyword().toFlagValue())) { iter.remove(); } } List<VariableDeclarationFragment> fragments = copy.fragments(); for (Iterator<VariableDeclarationFragment> iter = fragments.iterator(); iter.hasNext(); ) { VariableDeclarationFragment fragment = iter.next(); fragment.setInitializer(null); } CompilationUnit root = (CompilationUnit)statement.getRoot(); int extendedStart = root.getExtendedStartPosition(statement); // we have a leading comment and the comment is covered by the selection if (extendedStart != statement.getStartPosition() && extendedStart >= fSelection.getOffset()) { String commentToken = document.get(extendedStart, statement.getStartPosition() - extendedStart); commentToken = Strings.trimTrailingTabsAndSpaces(commentToken); Type type = statement.getType(); String typeName = document.get(type.getStartPosition(), type.getLength()); copy.setType((Type)fRewriter.createStringPlaceholder(commentToken + typeName, type.getNodeType())); } result.add(copy); // convert the fragments into expression statements fragments = statement.fragments(); if (!fragments.isEmpty()) { List<ExpressionStatement> newExpressionStatements = new ArrayList<ExpressionStatement>(); for (Iterator<VariableDeclarationFragment> iter = fragments.iterator(); iter.hasNext(); ) { VariableDeclarationFragment fragment = iter.next(); Expression initializer = fragment.getInitializer(); if (initializer != null) { Assignment assignment = ast.newAssignment(); assignment.setLeftHandSide((Expression)fRewriter.createCopyTarget(fragment.getName())); assignment.setRightHandSide((Expression)fRewriter.createCopyTarget(initializer)); newExpressionStatements.add(ast.newExpressionStatement(assignment)); } } if (!newExpressionStatements.isEmpty()) { if (fSelectedNodes.length == 1) { expressionStatement = fRewriter.createGroupNode(newExpressionStatements.toArray(new ASTNode[newExpressionStatements .size()])); } else { fRewriter.replace(statement, fRewriter.createGroupNode(newExpressionStatements .toArray(new ASTNode[newExpressionStatements .size()])), null); } } else { fRewriter.remove(statement, null); selectedNodeRemoved = true; } } else { fRewriter.remove(statement, null); selectedNodeRemoved = true; } } } result.add(tryStatement); ASTNode replacementNode; if (result.size() == 1) { replacementNode = result.get(0); } else { replacementNode = fRewriter.createGroupNode(result.toArray(new ASTNode[result.size()])); } if (fSelectedNodes.length == 1) { if (expressionStatement != null) { statements.insertLast(expressionStatement, null); } else { if (!selectedNodeRemoved) { statements.insertLast(fRewriter.createMoveTarget(fSelectedNodes[0]), null); } } fRewriter.replace(fSelectedNodes[0], replacementNode, null); } else { ListRewrite source = fRewriter.getListRewrite(fSelectedNodes[0].getParent(), (ChildListPropertyDescriptor)fSelectedNodes[0].getLocationInParent()); ASTNode toMove = source .createMoveTarget(fSelectedNodes[0], fSelectedNodes[fSelectedNodes.length - 1], replacementNode, null); statements.insertLast(toMove, null); } } private List<ASTNode> getSpecialVariableDeclarationStatements() { List<ASTNode> result = new ArrayList<ASTNode>(3); VariableDeclaration[] locals = fAnalyzer.getAffectedLocals(); for (int i = 0; i < locals.length; i++) { ASTNode parent = locals[i].getParent(); if (parent instanceof VariableDeclarationStatement && !result.contains(parent)) { result.add(parent); } } return result; } private Statement getCatchBody(String type, String name, String lineSeparator) throws CoreException { String s = StubUtility.getCatchBodyContent(type, name, fSelectedNodes[0], lineSeparator); if (s == null) { return null; } else { return (Statement)fRewriter.createStringPlaceholder(s, ASTNode.RETURN_STATEMENT); } } /** {@inheritDoc} */ @Override public RefactoringStatus checkFinalConditions() throws CoreException, OperationCanceledException { // TODO Auto-generated method stub return null; } }