/*******************************************************************************
* Copyright (c) 2008, 2011 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 - do not search for definition insert location twice.
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.gettersandsetters;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
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.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ContainerNode;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring2;
import org.eclipse.cdt.internal.ui.refactoring.AddDeclarationNodeToClassChange;
import org.eclipse.cdt.internal.ui.refactoring.Container;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.RefactoringASTCache;
import org.eclipse.cdt.internal.ui.refactoring.implementmethod.InsertLocation;
import org.eclipse.cdt.internal.ui.refactoring.implementmethod.MethodDefinitionInsertLocationFinder;
import org.eclipse.cdt.internal.ui.refactoring.utils.Checks;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.VisibilityEnum;
/**
* @author Thomas Corbat
*/
public class GenerateGettersAndSettersRefactoring extends CRefactoring2 {
private final class CompositeTypeSpecFinder extends ASTVisitor {
private final int start;
private final Container<IASTCompositeTypeSpecifier> container;
{
shouldVisitDeclSpecifiers = true;
}
private CompositeTypeSpecFinder(int start, Container<IASTCompositeTypeSpecifier> container) {
this.start = start;
this.container = container;
}
@Override
public int visit(IASTDeclSpecifier declSpec) {
if (declSpec instanceof IASTCompositeTypeSpecifier) {
IASTFileLocation loc = declSpec.getFileLocation();
if (start > loc.getNodeOffset() && start < loc.getNodeOffset() + loc.getNodeLength()) {
container.setObject((IASTCompositeTypeSpecifier) declSpec);
return ASTVisitor.PROCESS_ABORT;
}
}
return super.visit(declSpec);
}
}
private static final String MEMBER_DECLARATION = "MEMBER_DECLARATION"; //$NON-NLS-1$
private final GetterSetterContext context;
private InsertLocation definitionInsertLocation;
public GenerateGettersAndSettersRefactoring(ICElement element, ISelection selection,
ICProject project, RefactoringASTCache astCache) {
super(element, selection, project, astCache);
context = new GetterSetterContext();
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
SubMonitor sm = SubMonitor.convert(pm, 10);
RefactoringStatus status = super.checkInitialConditions(sm.newChild(6));
if (status.hasError()) {
return status;
}
if (!initStatus.hasFatalError()) {
initRefactoring(pm);
if (context.existingFields.isEmpty()) {
initStatus.addFatalError(Messages.GenerateGettersAndSettersRefactoring_NoFields);
}
}
return initStatus;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm,
CheckConditionsContext checkContext) throws CoreException, OperationCanceledException {
RefactoringStatus result = new RefactoringStatus();
if (!context.isImplementationInHeader()) {
findDefinitionInsertLocation(pm);
if (definitionInsertLocation == null || tu.equals(definitionInsertLocation.getTranslationUnit())) {
result.addInfo(Messages.GenerateGettersAndSettersRefactoring_NoImplFile);
}
}
Checks.addModifiedFilesToChecker(getAllFilesToModify(), checkContext);
return result;
}
private IFile[] getAllFilesToModify() {
List<IFile> files = new ArrayList<IFile>(2);
IFile file = (IFile) tu.getResource();
if (file != null) {
files.add(file);
}
if (definitionInsertLocation != null) {
file = definitionInsertLocation.getFile();
if (file != null) {
files.add(file);
}
}
return files.toArray(new IFile[files.size()]);
}
private void initRefactoring(IProgressMonitor pm) throws OperationCanceledException, CoreException {
IASTTranslationUnit ast = astCache.getAST(tu, null);
context.selectedName = getSelectedName(ast);
IASTCompositeTypeSpecifier compositeTypeSpecifier = null;
if (context.selectedName != null) {
compositeTypeSpecifier = getCompositeTypeSpecifier(context.selectedName);
} else {
compositeTypeSpecifier = findCurrentCompositeTypeSpecifier(ast);
}
if (compositeTypeSpecifier != null) {
findDeclarations(compositeTypeSpecifier);
} else {
initStatus.addFatalError(Messages.GenerateGettersAndSettersRefactoring_NoCassDefFound);
}
}
private IASTCompositeTypeSpecifier findCurrentCompositeTypeSpecifier(IASTTranslationUnit ast)
throws OperationCanceledException, CoreException {
final int start = selectedRegion.getOffset();
Container<IASTCompositeTypeSpecifier> container = new Container<IASTCompositeTypeSpecifier>();
ast.accept(new CompositeTypeSpecFinder(start, container));
return container.getObject();
}
private IASTCompositeTypeSpecifier getCompositeTypeSpecifier(IASTName selectedName) {
IASTNode node = selectedName;
while(node != null && !(node instanceof IASTCompositeTypeSpecifier)) {
node = node.getParent();
}
return (IASTCompositeTypeSpecifier) node;
}
private IASTName getSelectedName(IASTTranslationUnit ast) {
List<IASTName> names = findAllMarkedNames(ast);
if (names.size() < 1) {
return null;
}
return names.get(names.size() - 1);
}
protected void findDeclarations(IASTCompositeTypeSpecifier compositeTypeSpecifier) {
compositeTypeSpecifier.accept(new ASTVisitor() {
{
shouldVisitDeclarations = true;
}
@Override
public int visit(IASTDeclaration declaration) {
if (declaration instanceof IASTSimpleDeclaration) {
IASTSimpleDeclaration fieldDeclaration = (IASTSimpleDeclaration) declaration;
ASTNodeProperty props = fieldDeclaration.getPropertyInParent();
if (props.getName().contains(MEMBER_DECLARATION)) {
final IASTDeclarator[] declarators = fieldDeclaration.getDeclarators();
if (declarators.length > 0) {
IASTDeclarator innermostDeclarator = declarators[0];
while (innermostDeclarator.getNestedDeclarator() != null) {
innermostDeclarator = innermostDeclarator.getNestedDeclarator();
}
if ((innermostDeclarator instanceof IASTFunctionDeclarator)) {
context.existingFunctionDeclarations.add(fieldDeclaration);
} else if (fieldDeclaration.isPartOfTranslationUnitFile()) {
context.existingFields.add(fieldDeclaration);
}
}
}
}
if (declaration instanceof IASTFunctionDefinition) {
IASTFunctionDefinition functionDefinition = (IASTFunctionDefinition) declaration;
ASTNodeProperty props = functionDefinition.getPropertyInParent();
if (props.getName().contains(MEMBER_DECLARATION)) {
context.existingFunctionDefinitions.add(functionDefinition);
}
}
return super.visit(declaration);
}
});
}
@Override
protected void collectModifications(IProgressMonitor pm,ModificationCollector collector)
throws CoreException, OperationCanceledException {
List<IASTNode> getterAndSetters = new ArrayList<IASTNode>();
List<IASTFunctionDefinition> definitions = new ArrayList<IASTFunctionDefinition>();
for (GetterSetterInsertEditProvider currentProvider : context.selectedFunctions) {
if (context.isImplementationInHeader()) {
getterAndSetters.add(currentProvider.getFunctionDefinition(false));
} else {
getterAndSetters.add(currentProvider.getFunctionDeclaration());
definitions.add(currentProvider.getFunctionDefinition(true));
}
}
if (!context.isImplementationInHeader()) {
addDefinition(collector, definitions, pm);
}
ICPPASTCompositeTypeSpecifier classDefinition =
(ICPPASTCompositeTypeSpecifier) context.existingFields.get(context.existingFields.size() - 1).getParent();
AddDeclarationNodeToClassChange.createChange(classDefinition, VisibilityEnum.v_public,
getterAndSetters, false, collector);
}
private void addDefinition(ModificationCollector collector, List<IASTFunctionDefinition> definitions, IProgressMonitor pm)
throws CoreException {
findDefinitionInsertLocation(pm);
IASTNode parent = definitionInsertLocation.getParentOfNodeToInsertBefore();
IASTTranslationUnit ast = parent.getTranslationUnit();
ASTRewrite rewrite = collector.rewriterForTranslationUnit(ast);
IASTNode nodeToInsertBefore = definitionInsertLocation.getNodeToInsertBefore();
ContainerNode cont = new ContainerNode();
for (IASTFunctionDefinition functionDefinition : definitions) {
cont.addNode(functionDefinition);
}
rewrite = rewrite.insertBefore(parent, nodeToInsertBefore, cont, null);
}
public GetterSetterContext getContext() {
return context;
}
private void findDefinitionInsertLocation(IProgressMonitor pm) throws CoreException {
if (definitionInsertLocation != null) {
return;
}
IASTSimpleDeclaration decl = context.existingFields.get(0);
MethodDefinitionInsertLocationFinder methodDefinitionInsertLocationFinder = new MethodDefinitionInsertLocationFinder();
InsertLocation location = methodDefinitionInsertLocationFinder.find(
tu, decl.getFileLocation(), decl.getParent(), astCache, pm);
if (location.getFile() == null || NodeHelper.isContainedInTemplateDeclaration(decl)) {
location.setNodeToInsertAfter(NodeHelper.findTopLevelParent(decl), tu);
}
definitionInsertLocation = location;
}
@Override
protected RefactoringDescriptor getRefactoringDescriptor() {
// TODO egraf add Descriptor
return null;
}
}