/*******************************************************************************
* Copyright (c) 2005, 2015 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
* Sergey Prigogin (Google)
******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.rename;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.ui.refactoring.changes.CCompositeChange;
import org.eclipse.cdt.internal.ui.refactoring.changes.RenameTranslationUnitChange;
import org.eclipse.cdt.internal.ui.util.NameComposer;
/**
* Processor adding constructor and destructor to the bindings to be renamed.
*/
public class CRenameClassProcessor extends CRenameTypeProcessor {
private final List<Change> tuRenames = new ArrayList<>();
public CRenameClassProcessor(CRenameProcessor processor, String kind) {
super(processor, kind);
}
@Override
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
Change change = super.createChange(pm);
if (tuRenames.isEmpty())
return change;
CompositeChange compositeChange;
if (change instanceof CompositeChange) {
compositeChange = (CompositeChange) change;
} else {
compositeChange = new CCompositeChange(""); //$NON-NLS-1$
compositeChange.markAsSynthetic();
compositeChange.add(change);
}
for (Change tuRename : tuRenames) {
compositeChange.add(tuRename);
}
return compositeChange;
}
@Override
protected IBinding[] getBindingsToBeRenamed(RefactoringStatus status) {
tuRenames.clear();
CRefactoringArgument argument= getArgument();
IBinding binding= argument.getBinding();
ArrayList<IBinding> bindings= new ArrayList<>();
if (binding != null) {
recordRename(binding);
bindings.add(binding);
}
if (binding instanceof ICPPClassType) {
ICPPClassType ctype= (ICPPClassType) binding;
ICPPConstructor[] ctors= ctype.getConstructors();
if (ctors != null) {
ArrayUtil.addAll(bindings, ctors);
}
IScope scope= ctype.getCompositeScope();
if (scope != null) {
IBinding[] dtors= scope.find("~" + argument.getName(), argument.getTranslationUnit()); //$NON-NLS-1$
if (dtors != null) {
ArrayUtil.addAll(bindings, dtors);
}
}
renameTranslationUnits(ctype);
}
return bindings.toArray(new IBinding[bindings.size()]);
}
private void renameTranslationUnits(ICPPBinding binding) {
IIndex index = getIndex();
if (index == null) {
return;
}
try {
index.acquireReadLock();
Set<IIndexFileLocation> locations = new HashSet<>();
IIndexName[] names = index.findNames(binding, IIndex.FIND_DEFINITIONS);
for (IIndexName name : names) {
locations.add(name.getFile().getLocation());
}
if (locations.size() != 1)
return;
IIndexFileLocation location = locations.iterator().next();
String fullPath = location.getFullPath();
if (fullPath == null)
return;
IPath headerPath = new Path(fullPath);
IResource file = ResourcesPlugin.getWorkspace().getRoot().findMember(headerPath);
if (file == null || file.getType() != IResource.FILE)
return;
IProject project = getProject();
int headerCapitalization = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_HEADER_CAPITALIZATION, project,
PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL);
String headerWordDelimiter = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_HEADER_WORD_DELIMITER, project, ""); //$NON-NLS-1$
int sourceCapitalization = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_SOURCE_CAPITALIZATION, project,
PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL);
String sourceWordDelimiter = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_SOURCE_WORD_DELIMITER, project, ""); //$NON-NLS-1$
String headerName = headerPath.lastSegment();
String className = binding.getName();
NameComposer nameComposer = NameComposer.createByExample(className, headerName,
headerCapitalization, headerWordDelimiter);
if (nameComposer == null)
return;
String newClassName = getReplacementText();
String newHeaderName = nameComposer.compose(newClassName);
if (!newHeaderName.equals(headerName)) {
renameTranslationUnit((IFile) file, newHeaderName);
}
IIndexInclude[] includedBy = index.findIncludedBy(names[0].getFile());
for (IIndexInclude include : includedBy) {
location = include.getIncludedByLocation();
fullPath = location.getFullPath();
if (fullPath == null)
continue;
IPath filePath = new Path(fullPath);
file = ResourcesPlugin.getWorkspace().getRoot().findMember(filePath);
if (file != null && file.getType() == IResource.FILE) {
String fileName = filePath.lastSegment();
if (CoreModel.isValidHeaderUnitName(project, fileName)) {
nameComposer = NameComposer.createByExample(className, fileName,
headerCapitalization, headerWordDelimiter);
} else {
nameComposer = NameComposer.createByExample(className, fileName,
sourceCapitalization, sourceWordDelimiter);
}
if (nameComposer != null) {
String newName = nameComposer.compose(newClassName);
if (!newName.equals(fileName)) {
renameTranslationUnit((IFile) file, newName);
}
}
}
}
} catch (CoreException e) {
CUIPlugin.log(e);
return;
} catch (InterruptedException e) {
return; // Ignore.
} finally {
index.releaseReadLock();
}
}
protected void renameTranslationUnit(IFile file, String newName) {
ICElement elem = CoreModel.getDefault().create(file);
if (elem instanceof ITranslationUnit) {
tuRenames.add(new RenameTranslationUnitChange((ITranslationUnit) elem, newName));
getRenameModifications().rename(file, new RenameArguments(newName, true));
}
}
protected IProject getProject() {
IFile file= getArgument().getSourceFile();
if (file == null)
return null;
return file.getProject();
}
}