/******************************************************************************* * Copyright (c) 2008, 2012 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences 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: * Institute for Software - initial API and implementation * Sergey Prigogin (Google) * Marc-Andre Laperle *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.implementmethod; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.internal.ui.editor.SourceHeaderPartnerFinder; import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; import org.eclipse.cdt.internal.ui.refactoring.utils.DefinitionFinder; import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper; /** * Finds the information that are needed to tell where a method definition of a certain * method declaration should be inserted. * * @author Mirko Stocker, Lukas Felber */ public class MethodDefinitionInsertLocationFinder { // We cache DefinitionFinder.getDefinition results because refactorings like Implement Method // might want to find multiple insert locations in the same translation unit. This prevents // many redundant calls to DefinitionFinder.getDefinition and speeds up the process quite // a bit. Unfortunately, this has the minor side-effect or having to instantiate this class. Map<IASTSimpleDeclaration, IASTName> cachedDeclarationToDefinition = new HashMap<IASTSimpleDeclaration, IASTName>(); public InsertLocation find(ITranslationUnit declarationTu, IASTFileLocation methodDeclarationLocation, IASTNode parent, CRefactoringContext refactoringContext, IProgressMonitor pm) throws CoreException { IASTDeclaration[] declarations = NodeHelper.getDeclarations(parent); InsertLocation insertLocation = new InsertLocation(); Collection<IASTSimpleDeclaration> allPreviousSimpleDeclarationsFromClassInReverseOrder = getAllPreviousSimpleDeclarationsFromClassInReverseOrder(declarations, methodDeclarationLocation, pm); Collection<IASTSimpleDeclaration> allFollowingSimpleDeclarationsFromClass = getAllFollowingSimpleDeclarationsFromClass(declarations, methodDeclarationLocation, pm); for (IASTSimpleDeclaration simpleDeclaration : allPreviousSimpleDeclarationsFromClassInReverseOrder) { if (pm != null && pm.isCanceled()) { throw new OperationCanceledException(); } IASTName definition = null; if (cachedDeclarationToDefinition.containsKey(simpleDeclaration)) { definition = cachedDeclarationToDefinition.get(simpleDeclaration); } else { IASTName name = simpleDeclaration.getDeclarators()[0].getName(); definition = DefinitionFinder.getDefinition(name, refactoringContext, pm); if (definition != null) { cachedDeclarationToDefinition.put(simpleDeclaration, definition); } } if (definition != null) { insertLocation.setNodeToInsertAfter(findFirstSurroundingParentFunctionNode( definition), definition.getTranslationUnit().getOriginatingTranslationUnit()); } } for (IASTSimpleDeclaration simpleDeclaration : allFollowingSimpleDeclarationsFromClass) { if (pm != null && pm.isCanceled()) { throw new OperationCanceledException(); } IASTName definition = null; if (cachedDeclarationToDefinition.containsKey(simpleDeclaration)) { definition = cachedDeclarationToDefinition.get(simpleDeclaration); } else { IASTName name = simpleDeclaration.getDeclarators()[0].getName(); definition = DefinitionFinder.getDefinition(name, refactoringContext, pm); if (definition != null) { cachedDeclarationToDefinition.put(simpleDeclaration, definition); } } if (definition != null) { insertLocation.setNodeToInsertBefore(findFirstSurroundingParentFunctionNode(definition), definition.getTranslationUnit().getOriginatingTranslationUnit()); } } if (insertLocation.getTranslationUnit() == null) { if (declarationTu.isHeaderUnit()) { ITranslationUnit partner = SourceHeaderPartnerFinder.getPartnerTranslationUnit( declarationTu, refactoringContext); if (partner != null) { insertLocation.setParentNode(refactoringContext.getAST(partner, null), partner); } } else { insertLocation.setParentNode(parent.getTranslationUnit(), declarationTu); } } return insertLocation; } private static IASTNode findFunctionDefinitionInParents(IASTNode node) { if (node == null) { return null; } else if (node instanceof IASTFunctionDefinition) { if (node.getParent() instanceof ICPPASTTemplateDeclaration) { node = node.getParent(); } return node; } return findFunctionDefinitionInParents(node.getParent()); } private static IASTNode findFirstSurroundingParentFunctionNode(IASTNode definition) { IASTNode functionDefinitionInParents = findFunctionDefinitionInParents(definition); if (functionDefinitionInParents == null) { return null; } if (functionDefinitionInParents.getNodeLocations().length == 0) { return null; } return functionDefinitionInParents; } /** * Searches the given class for all IASTSimpleDeclarations occurring before 'method' * and returns them in reverse order. * * @param declarations to be searched * @param methodPosition on which the search aborts * @param pm * @return all declarations, sorted in reverse order */ private static Collection<IASTSimpleDeclaration> getAllPreviousSimpleDeclarationsFromClassInReverseOrder( IASTDeclaration[] declarations, IASTFileLocation methodPosition, IProgressMonitor pm) { ArrayList<IASTSimpleDeclaration> outputDeclarations = new ArrayList<IASTSimpleDeclaration>(); if (declarations.length >= 0) { for (IASTDeclaration decl : declarations) { if (pm != null && pm.isCanceled()) { return outputDeclarations; } if (decl.getFileLocation().getStartingLineNumber() >= methodPosition.getStartingLineNumber()) { break; } if (isMemberFunctionDeclaration(decl)) { outputDeclarations.add((IASTSimpleDeclaration) decl); } } } Collections.reverse(outputDeclarations); return outputDeclarations; } private static Collection<IASTSimpleDeclaration> getAllFollowingSimpleDeclarationsFromClass( IASTDeclaration[] declarations, IASTFileLocation methodPosition, IProgressMonitor pm) { ArrayList<IASTSimpleDeclaration> outputDeclarations = new ArrayList<IASTSimpleDeclaration>(); if (declarations.length >= 0) { for (IASTDeclaration decl : declarations) { if (pm != null && pm.isCanceled()) { return outputDeclarations; } if (isMemberFunctionDeclaration(decl) && decl.getFileLocation().getStartingLineNumber() > methodPosition.getStartingLineNumber() ) { outputDeclarations.add((IASTSimpleDeclaration) decl); } } } return outputDeclarations; } private static boolean isMemberFunctionDeclaration(IASTDeclaration decl) { return decl instanceof IASTSimpleDeclaration && ((IASTSimpleDeclaration) decl).getDeclarators().length > 0 && ((IASTSimpleDeclaration) decl).getDeclarators()[0] instanceof IASTFunctionDeclarator; } }