blob: 2f44878efdb465b904360c33e1b5c17f23d70625 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.reorg;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.participants.ReorgExecutionLog;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.corext.refactoring.IRefactoringSearchRequestor;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
import org.eclipse.jdt.internal.corext.refactoring.nls.changes.CreateTextFileChange;
import org.eclipse.jdt.internal.corext.refactoring.rename.TypeOccurrenceCollector;
import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
import org.eclipse.jdt.internal.corext.util.JavaElementResourceMapping;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.SearchUtils;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
public final class CreateCopyOfCompilationUnitChange extends CreateTextFileChange {
private static TextChangeManager createChangeManager(IProgressMonitor monitor, ICompilationUnit copy, String newName) throws CoreException {
TextChangeManager manager= new TextChangeManager();
SearchResultGroup refs= getReferences(copy, monitor);
if (refs == null)
return manager;
if (refs.getCompilationUnit() == null)
return manager;
String name= RefactoringCoreMessages.CopyRefactoring_update_ref;
for (SearchMatch searchResult : refs.getSearchResults()) {
if (searchResult.getAccuracy() == SearchMatch.A_INACCURATE)
continue;
int offset= searchResult.getOffset();
int length= searchResult.getLength();
TextChangeCompatibility.addTextEdit(manager.get(copy), name, new ReplaceEdit(offset, length, newName));
}
return manager;
}
private static SearchPattern createSearchPattern(IType type) throws JavaModelException {
SearchPattern pattern= SearchPattern.createPattern(type, IJavaSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
IMethod[] constructors= JavaElementUtil.getAllConstructors(type);
if (constructors.length == 0)
return pattern;
SearchPattern constructorDeclarationPattern= RefactoringSearchEngine.createOrPattern(constructors, IJavaSearchConstants.DECLARATIONS);
return SearchPattern.createOrPattern(pattern, constructorDeclarationPattern);
}
private static String getCopiedFileSource(IProgressMonitor monitor, ICompilationUnit unit, String newTypeName) throws CoreException {
ICompilationUnit copy= unit.getPrimary().getWorkingCopy(null);
try {
TextChangeManager manager= createChangeManager(monitor, copy, newTypeName);
String result= manager.get(copy).getPreviewContent(new NullProgressMonitor());
return result;
} finally {
copy.discardWorkingCopy();
}
}
private static SearchResultGroup getReferences(final ICompilationUnit copy, IProgressMonitor monitor) throws JavaModelException {
final ICompilationUnit[] copies= new ICompilationUnit[] { copy};
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(copies);
final IType type= copy.findPrimaryType();
if (type == null)
return null;
SearchPattern pattern= createSearchPattern(type);
final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(pattern);
engine.setScope(scope);
engine.setWorkingCopies(copies);
engine.setRequestor(new IRefactoringSearchRequestor() {
TypeOccurrenceCollector fTypeOccurrenceCollector= new TypeOccurrenceCollector(type);
@Override
public SearchMatch acceptSearchMatch(SearchMatch match) {
try {
return fTypeOccurrenceCollector.acceptSearchMatch2(copy, match);
} catch (CoreException e) {
JavaPlugin.log(e);
return null;
}
}
});
engine.searchPattern(monitor);
// Assert.isTrue(results.length <= 1);
// just 1 file or none, but inaccurate matches can play bad here (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=106127)
for (Object result : engine.getResults()) {
SearchResultGroup group= (SearchResultGroup) result;
if (group.getCompilationUnit().equals(copy))
return group;
}
return null;
}
private final INewNameQuery fNameQuery;
private final ICompilationUnit fOldCu;
public CreateCopyOfCompilationUnitChange(IPath path, String source, ICompilationUnit oldCu, INewNameQuery nameQuery) {
super(path, source, null, "java"); //$NON-NLS-1$
fOldCu= oldCu;
fNameQuery= nameQuery;
setEncoding(oldCu);
}
@Override
public String getName() {
String cuName= BasicElementLabels.getResourceName(fOldCu.getElementName());
String cuContainerName= BasicElementLabels.getPathLabel(fOldCu.getParent().getPath(), false);
return Messages.format(RefactoringCoreMessages.CreateCopyOfCompilationUnitChange_create_copy, new String[] { cuName, cuContainerName});
}
@Override
protected IFile getOldFile(IProgressMonitor monitor) throws OperationCanceledException {
try {
monitor.beginTask("", 12); //$NON-NLS-1$
String oldSource= super.getSource();
IPath oldPath= super.getPath();
String newTypeName= fNameQuery.getNewName();
try {
String newSource= getCopiedFileSource(new SubProgressMonitor(monitor, 9), fOldCu, newTypeName);
setSource(newSource);
setPath(fOldCu.getResource().getParent().getFullPath().append(JavaModelUtil.getRenamedCUName(fOldCu, newTypeName)));
return super.getOldFile(new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
setSource(oldSource);
setPath(oldPath);
return super.getOldFile(new SubProgressMonitor(monitor, 2));
}
} finally {
monitor.done();
}
}
private void markAsExecuted(ICompilationUnit unit, ResourceMapping mapping) {
ReorgExecutionLog log= getAdapter(ReorgExecutionLog.class);
if (log != null) {
log.markAsProcessed(unit);
log.markAsProcessed(mapping);
}
}
@Override
public Change perform(IProgressMonitor monitor) throws CoreException {
ResourceMapping mapping= JavaElementResourceMapping.create(fOldCu);
final Change result= super.perform(monitor);
markAsExecuted(fOldCu, mapping);
return result;
}
private void setEncoding(ICompilationUnit unit) {
IResource resource= unit.getResource();
// no file so the encoding is taken from the target
if (!(resource instanceof IFile))
return;
IFile file= (IFile) resource;
try {
String encoding= file.getCharset(false);
if (encoding != null) {
setEncoding(encoding, true);
} else {
encoding= file.getCharset(true);
if (encoding != null) {
setEncoding(encoding, false);
}
}
} catch (CoreException e) {
// Take encoding from target
}
}
}