blob: 020c80187f44f542d8506449642840d9deead61e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.refactoring.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
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.TextFileChange;
import org.eclipse.ltk.core.refactoring.participants.CopyArguments;
import org.eclipse.ltk.core.refactoring.participants.CopyParticipant;
import org.eclipse.ltk.core.refactoring.participants.DeleteArguments;
import org.eclipse.ltk.core.refactoring.participants.DeleteParticipant;
import org.eclipse.ltk.core.refactoring.participants.MoveArguments;
import org.eclipse.ltk.core.refactoring.participants.MoveParticipant;
import org.eclipse.ltk.core.refactoring.participants.ParticipantManager;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
import org.eclipse.ltk.core.refactoring.participants.ReorgExecutionLog;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
import org.eclipse.ltk.core.refactoring.resource.DeleteResourceChange;
import org.eclipse.ltk.core.refactoring.resource.Resources;
import org.eclipse.osgi.util.NLS;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.text.core.BasicTextRegion;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.ecommons.io.FileUtil;
import org.eclipse.statet.ecommons.text.BasicHeuristicTokenScanner;
import org.eclipse.statet.ecommons.text.TextUtil;
import org.eclipse.statet.internal.ltk.refactoring.core.Messages;
import org.eclipse.statet.ltk.core.LTK;
import org.eclipse.statet.ltk.core.LTKUtils;
import org.eclipse.statet.ltk.model.core.ElementSet;
import org.eclipse.statet.ltk.model.core.elements.IModelElement;
import org.eclipse.statet.ltk.model.core.elements.ISourceElement;
import org.eclipse.statet.ltk.model.core.elements.ISourceStructElement;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.ltk.model.core.elements.IWorkspaceSourceUnit;
import org.eclipse.statet.ltk.model.core.util.ElementComparator;
/**
* Provides common functions for refacotring.
* Can be extended to adapt to language specific peculiarity.
*/
public abstract class RefactoringAdapter {
private static final Comparator<IModelElement> MODELELEMENT_SORTER= new ElementComparator();
private final String modelTypeId;
public RefactoringAdapter(final String modelTypeId) {
this.modelTypeId= modelTypeId;
}
public Comparator<IModelElement> getModelElementComparator() {
return MODELELEMENT_SORTER;
}
public String getModelTypeId() {
return this.modelTypeId;
}
public boolean isSupportedModelType(final String typeId) {
return (this.modelTypeId == typeId);
}
public abstract String getPluginIdentifier();
public abstract boolean isCommentContent(final ITypedRegion partition);
public abstract BasicHeuristicTokenScanner getScanner(final ISourceUnit su);
/**
* - Sort elements
* - Removes nested children.
*
* @param elements must be sorted by unit and order
* @return
*/
public ISourceStructElement[] checkElements(final ISourceStructElement[] elements) {
if (elements.length <= 1) {
return elements;
}
Arrays.sort(elements, getModelElementComparator());
ISourceStructElement last= elements[0];
ISourceUnit unitOfLast= last.getSourceUnit();
int endOfLast= last.getSourceRange().getEndOffset();
final List<ISourceStructElement> checked= new ArrayList<>(elements.length);
for (final ISourceStructElement element : elements) {
final ISourceUnit unit= element.getSourceUnit();
final int end= last.getSourceRange().getEndOffset();
if (unit != unitOfLast) {
checked.add(element);
last= element;
unitOfLast= unit;
endOfLast= end;
continue;
}
if (end > endOfLast) {
checked.add(element);
last= element;
endOfLast= end;
continue;
}
// is child, ignore
continue;
}
return checked.toArray(new ISourceStructElement[checked.size()]);
}
public IRegion getContinuousSourceRange(final ISourceStructElement[] elements) {
if (elements == null || elements.length == 0) {
return null;
}
final ISourceUnit su= elements[0].getSourceUnit();
if (su == null) {
return null;
}
final AbstractDocument doc= su.getDocument(null);
if (doc == null) {
return null;
}
// check if no other code is between the elements
// and create one single range including comments at line end
try {
final BasicHeuristicTokenScanner scanner= getScanner(su);
scanner.configure(doc);
final int start= elements[0].getSourceRange().getStartOffset();
int end= elements[0].getSourceRange().getEndOffset();
for (int i= 1; i < elements.length; i++) {
if (elements[i].getSourceUnit() != su) {
return null;
}
final int elementStart= elements[i].getSourceRange().getStartOffset();
final int elementEnd= elementStart + elements[i].getSourceRange().getLength();
if (elementEnd <= end) {
continue;
}
int match;
while (end < elementStart &&
(match= scanner.findAnyNonBlankForward(end, elementStart, true)) >= 0) {
final ITypedRegion partition= doc.getPartition(scanner.getDocumentPartitioning(), match, false);
if (isCommentContent(partition)) {
end= partition.getOffset() + partition.getLength();
}
else {
return null;
}
}
end= elementEnd;
}
final IRegion lastLine= doc.getLineInformationOfOffset(end);
final int match= scanner.findAnyNonBlankForward(end, lastLine.getOffset()+lastLine.getLength(), true);
if (match >= 0) {
final ITypedRegion partition= doc.getPartition(scanner.getDocumentPartitioning(), match, false);
if (isCommentContent(partition)) {
end= partition.getOffset() + partition.getLength();
}
}
return new Region(start, end-start);
}
catch (final BadPartitioningException e) {
}
catch (final BadLocationException e) {
}
return null;
}
public String getSourceCodeStringedTogether(final ISourceStructElement[] sourceElements,
final IProgressMonitor monitor) throws CoreException {
return getSourceCodeStringedTogether(new ElementSet((Object[]) sourceElements), monitor);
}
public String getSourceCodeStringedTogether(final ElementSet sourceElements,
final IProgressMonitor monitor) throws CoreException {
final SubMonitor m= SubMonitor.convert(monitor);
ISourceUnit lastUnit= null;
BasicHeuristicTokenScanner scanner= null;
try {
sourceElements.removeElementsWithAncestorsOnList();
Collections.sort(sourceElements.getModelElements(), getModelElementComparator());
final String lineDelimiter= TextUtil.getPlatformLineDelimiter();
AbstractDocument doc= null;
final List<IModelElement> modelElements= sourceElements.getModelElements();
int workRemaining= modelElements.size() * 3 + 1;
final StringBuilder sb= new StringBuilder(modelElements.size() * 100);
final List<String> codeFragments= new ArrayList<>();
for (final IModelElement element : modelElements) {
m.setWorkRemaining(workRemaining); workRemaining-= 3;
final ISourceUnit su= LTKUtils.getSourceUnit(element);
if (su != lastUnit) {
if (lastUnit != null) {
lastUnit.disconnect(m.newChild(1));
lastUnit= null;
}
su.connect(m.newChild(1));
lastUnit= su;
scanner= getScanner(su);
doc= su.getDocument(monitor);
}
getSourceCode((ISourceElement) element, doc, scanner, codeFragments);
for (final String s : codeFragments) {
sb.append(s);
sb.append(lineDelimiter);
}
codeFragments.clear();
m.worked(1);
}
return sb.toString();
}
catch (final BadLocationException e) {
throw new CoreException(failDocAnalyzation(e));
}
catch (final BadPartitioningException e) {
throw new CoreException(failDocAnalyzation(e));
}
finally {
if (lastUnit != null) {
m.setWorkRemaining(1);
lastUnit.disconnect(m.newChild(1));
lastUnit= null;
}
}
}
protected void getSourceCode(final ISourceElement element, final AbstractDocument doc,
final BasicHeuristicTokenScanner scanner, final List<String> codeFragments)
throws BadLocationException, BadPartitioningException {
final TextRegion range= expandElementRange(element, doc, scanner);
if (range != null && range.getLength() > 0) {
codeFragments.add(doc.get(range.getStartOffset(), range.getLength()));
}
}
public TextRegion expandElementRange(final ISourceElement element, final AbstractDocument document,
final BasicHeuristicTokenScanner scanner)
throws BadLocationException, BadPartitioningException {
final TextRegion sourceRange= element.getSourceRange();
int start= sourceRange.getStartOffset();
int end= start + sourceRange.getLength();
final TextRegion docRange= element.getDocumentationRange();
if (docRange != null) {
if (docRange.getStartOffset() < start) {
start= docRange.getStartOffset();
}
if (docRange.getEndOffset() > end) {
end= docRange.getEndOffset();
}
}
return expandSourceRange(start, end, document, scanner);
}
protected TextRegion expandSourceRange(final int start, int end, final AbstractDocument doc,
final BasicHeuristicTokenScanner scanner)
throws BadLocationException, BadPartitioningException {
scanner.configure(doc);
IRegion lastLineInfo;
int match;
lastLineInfo= doc.getLineInformationOfOffset(end);
match= scanner.findAnyNonBlankForward(end, lastLineInfo.getOffset()+lastLineInfo.getLength(), true);
if (match >= 0) {
final ITypedRegion partition= doc.getPartition(scanner.getDocumentPartitioning(),
match, false );
if (isCommentContent(partition)) {
end= partition.getOffset() + partition.getLength();
}
}
final int checkLine= doc.getLineOfOffset(end)+1;
if (checkLine < doc.getNumberOfLines()) {
final IRegion checkLineInfo= doc.getLineInformation(checkLine);
match= scanner.findAnyNonBlankForward(
end, checkLineInfo.getOffset()+checkLineInfo.getLength(), true);
if (match < 0) {
end= checkLineInfo.getOffset()+checkLineInfo.getLength();
}
}
return new BasicTextRegion(start, end);
}
public TextRegion expandWhitespaceBlock(final AbstractDocument document, final TextRegion region,
final BasicHeuristicTokenScanner scanner) throws BadLocationException {
scanner.configure(document);
final int firstLine= document.getLineOfOffset(region.getStartOffset());
int lastLine= document.getLineOfOffset(region.getEndOffset());
if (lastLine > firstLine && document.getLineOffset(lastLine) == region.getEndOffset()) {
lastLine--;
}
int result;
final int min= document.getLineOffset(firstLine);
final int max= document.getLineOffset(lastLine)+document.getLineLength(lastLine);
result= scanner.findAnyNonBlankForward(region.getEndOffset(), max, true);
final int end= (result >= 0) ? result : max;
result= scanner.findAnyNonBlankBackward(region.getStartOffset(), min, true);
if (result >= 0) {
return new BasicTextRegion(result + 1, end);
}
else {
return new BasicTextRegion(min, end);
}
}
public boolean canDelete(final ElementSet elements) {
if (elements.getInitialObjects().size() == 0) {
return false;
}
if (!elements.isOK()) {
return false;
}
for (final IModelElement element : elements.getModelElements()) {
if (!canDelete(element)) {
return false;
}
}
for (final IResource element : elements.getResources()) {
if (!canDelete(element)) {
return false;
}
}
return true;
}
public boolean canDelete(final IModelElement element) {
if (!element.exists()) {
return false;
}
// if ((element.getElementType() & IModelElement.MASK_C1) == IModelElement.PROJECT) {
// return false;
// }
if (!isSupportedModelType(element.getModelTypeId())) {
return false;
}
if (element.isReadOnly()) {
return false;
}
return true;
}
public boolean canDelete(final IResource resource) {
if (!resource.exists() || resource.isPhantom()) {
return false;
}
if (resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT) {
return false;
}
if (resource.getParent() != null) {
final ResourceAttributes attributes= resource.getParent().getResourceAttributes();
if (attributes != null && attributes.isReadOnly()) {
return false;
}
}
return true;
}
public boolean canInsert(final ElementSet elements, final RefactoringDestination to) {
if (to.getInitialObjects().get(0) instanceof ISourceElement) {
return canInsert(elements, (ISourceElement) to.getInitialObjects().get(0), to.getPosition());
}
return false;
}
protected boolean canInsert(final ElementSet elements, final ISourceElement to,
final RefactoringDestination.Position pos) {
if (elements.getInitialObjects().size() == 0) {
return false;
}
if (!elements.isOK()) {
return false;
}
if (!canInsertTo(to)) {
return false;
}
for (final IModelElement element : elements.getModelElements()) {
if (!canInsert(element)) {
return false;
}
}
for (final IResource element : elements.getResources()) {
// if (!canInsert(element, parent) {
return false;
// }
}
return !elements.includes(to);
}
protected boolean canInsert(final IModelElement element) {
if (!element.exists()) {
return false;
}
if (!isSupportedModelType(element.getModelTypeId())) {
return false;
}
return true;
}
public boolean canInsertTo(final RefactoringDestination destination) {
if (destination.getModelElements().size() != 1) {
return false;
}
return canInsertTo(destination.getModelElements().get(0));
}
protected boolean canInsertTo(final IModelElement element) {
if (!element.exists()) {
return false;
}
if (!isSupportedModelType(element.getModelTypeId())) {
return false;
}
if (element.isReadOnly()) {
return false;
}
return true;
}
public void checkInitialToModify(final RefactoringStatus result, final ElementSet elements) {
final Set<IResource> resources= new HashSet<>();
resources.addAll(elements.getResources());
for(final IModelElement element : elements.getModelElements()) {
final ISourceUnit su= LTKUtils.getSourceUnit(element);
if (su instanceof IWorkspaceSourceUnit) {
resources.add(((IWorkspaceSourceUnit) su).getResource());
continue;
}
result.addFatalError(Messages.Check_ElementNotInWS_message);
return;
}
result.merge(RefactoringStatus.create(
Resources.checkInSync(resources.toArray(new IResource[resources.size()]))
));
}
public void checkFinalToModify(final RefactoringStatus result, final ElementSet elements, final IProgressMonitor monitor) {
final Set<IResource> resources= new HashSet<>();
resources.addAll(elements.getResources());
for(final IModelElement element : elements.getModelElements()) {
final ISourceUnit su= LTKUtils.getSourceUnit(element);
if (su instanceof IWorkspaceSourceUnit) {
resources.add(((IWorkspaceSourceUnit) su).getResource());
continue;
}
result.addFatalError(Messages.Check_ElementNotInWS_message);
return;
}
final IResource[] array= resources.toArray(new IResource[resources.size()]);
result.merge(RefactoringStatus.create(Resources.checkInSync(array)));
result.merge(RefactoringStatus.create(Resources.makeCommittable(array, IWorkspace.VALIDATE_PROMPT)));
}
public void checkFinalToDelete(final RefactoringStatus result, final ElementSet elements) throws CoreException {
for (final IModelElement element : elements.getModelElements()) {
checkFinalToDelete(result, element);
}
for (final IResource element : elements.getResources()) {
checkFinalToDelete(result, element);
}
}
public void checkFinalToDelete(final RefactoringStatus result, final IResource element) throws CoreException {
if (element.getType() == IResource.FILE) {
warnIfDirty(result, (IFile) element);
return;
}
else {
element.accept(new IResourceVisitor() {
@Override
public boolean visit(final IResource visitedResource) throws CoreException {
if (visitedResource instanceof IFile) {
warnIfDirty(result, (IFile) visitedResource);
}
return true;
}
}, IResource.DEPTH_INFINITE, false);
}
}
public void checkFinalToDelete(final RefactoringStatus result, final IModelElement element) throws CoreException {
if ((element.getElementType() & IModelElement.MASK_C2) == IModelElement.C2_SOURCE_FILE) {
if (element instanceof IWorkspaceSourceUnit) {
checkFinalToDelete(result, ((IWorkspaceSourceUnit) element).getResource());
}
}
else if ((element.getElementType() & IModelElement.MASK_C1) == IModelElement.C1_BUNDLE
&& element instanceof ISourceStructElement) {
final List<? extends IModelElement> children= ((ISourceStructElement) element).getSourceChildren(null);
for (final IModelElement child : children) {
checkFinalToDelete(result, child);
}
}
}
public void warnIfDirty(final RefactoringStatus result, final IFile file) {
if (file == null || !file.exists()) {
return;
}
final ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
if (buffer != null && buffer.isDirty()) {
if (buffer.isStateValidated() && buffer.isSynchronized()) {
result.addWarning(NLS.bind(
Messages.Check_FileUnsavedChanges_message,
FileUtil.getFileUtil(file).getLabel()) );
} else {
result.addFatalError(NLS.bind(
Messages.Check_FileUnsavedChanges_message,
FileUtil.getFileUtil(file).getLabel()) );
}
}
}
public boolean confirmDeleteOfReadOnlyElements(final ElementSet elements, final Object queries) throws CoreException {
// TODO add query support
return hasReadOnlyElements(elements);
}
public boolean hasReadOnlyElements(final ElementSet elements) throws CoreException {
for (final IResource element : elements.getResources()) {
if (hasReadOnlyElements(element)) {
return true;
}
}
for (final IModelElement element : elements.getModelElements()) {
if (hasReadOnlyElements(element)) {
return true;
}
}
return false;
}
public boolean hasReadOnlyElements(final IResource element) throws CoreException {
if (isReadOnly(element)) {
return true;
}
if (element instanceof IContainer) {
final IResource[] members= ((IContainer) element).members(false);
for (final IResource member : members) {
if (hasReadOnlyElements(member)) {
return true;
}
}
}
return false;
}
public boolean hasReadOnlyElements(final IModelElement element) throws CoreException {
final ISourceUnit su= LTKUtils.getSourceUnit(element);
IResource resource= null;
if (su instanceof IWorkspaceSourceUnit) {
resource= ((IWorkspaceSourceUnit) su).getResource();
}
if (resource == null) {
resource= element.getAdapter(IResource.class);
}
if (resource != null) {
return hasReadOnlyElements(resource);
}
return false;
}
public boolean isReadOnly(final IResource element) {
final ResourceAttributes attributes= element.getResourceAttributes();
if (attributes != null) {
return attributes.isReadOnly();
}
return false;
}
public void addParticipantsToDelete(final ElementSet elementsToDelete,
final List<RefactoringParticipant> list,
final RefactoringStatus status, final RefactoringProcessor processor,
final SharableParticipants shared)
throws CoreException {
final String[] natures= ElementSet.getAffectedProjectNatures(elementsToDelete);
final DeleteArguments arguments= new DeleteArguments();
for (final IResource resource : elementsToDelete.getResources()) {
final DeleteParticipant[] deletes= ParticipantManager.loadDeleteParticipants(status,
processor, resource,
arguments, natures, shared);
list.addAll(Arrays.asList(deletes));
}
for (final IResource resource : elementsToDelete.getResourcesOwnedByElements()) {
final DeleteParticipant[] deletes= ParticipantManager.loadDeleteParticipants(status,
processor, resource,
arguments, natures, shared);
list.addAll(Arrays.asList(deletes));
}
for (final IModelElement element : elementsToDelete.getModelElements()) {
final DeleteParticipant[] deletes= ParticipantManager.loadDeleteParticipants(status,
processor, element,
arguments, natures, shared);
list.addAll(Arrays.asList(deletes));
}
}
public void addParticipantsToMove(final ElementSet elementsToMove,
final RefactoringDestination destination,
final List<RefactoringParticipant> list,
final RefactoringStatus status, final RefactoringProcessor processor,
final SharableParticipants shared, final ReorgExecutionLog executionLog)
throws CoreException {
final String[] natures= ElementSet.getAffectedProjectNatures(
ImCollections.newList(elementsToMove, destination) );
final MoveArguments mArguments= new MoveArguments(destination.getModelElements().get(0),
false );
// for (final IResource resource : elementsToCopy.getResources()) {
// final MoveParticipant[] deletes= ParticipantManager.loadMoveParticipants(status,
// processor, resource, arguments, natures, shared );
// list.addAll(Arrays.asList(deletes));
// }
// for (final IResource resource : elementsToCopy.getResourcesOwnedByElements()) {
// final MoveParticipant[] deletes= ParticipantManager.loadMoveParticipants(status,
// processor, resource, arguments, natures, shared );
// list.addAll(Arrays.asList(deletes));
// }
for (final IModelElement element : elementsToMove.getModelElements()) {
final MoveParticipant[] deletes= ParticipantManager.loadMoveParticipants(status,
processor, element, mArguments, natures, shared );
list.addAll(Arrays.asList(deletes));
}
}
public void addParticipantsToCopy(final ElementSet elementsToCopy,
final RefactoringDestination destination,
final List<RefactoringParticipant> list,
final RefactoringStatus status, final RefactoringProcessor processor,
final SharableParticipants shared, final ReorgExecutionLog executionLog)
throws CoreException {
final String[] natures= ElementSet.getAffectedProjectNatures(
ImCollections.newList(elementsToCopy, destination) );
final CopyArguments mArguments= new CopyArguments(destination.getModelElements().get(0),
executionLog );
// for (final IResource resource : elementsToCopy.getResources()) {
// final CopyParticipant[] deletes= ParticipantManager.loadCopyParticipants(status,
// processor, resource, arguments, natures, shared );
// list.addAll(Arrays.asList(deletes));
// }
// for (final IResource resource : elementsToCopy.getResourcesOwnedByElements()) {
// final CopyParticipant[] deletes= ParticipantManager.loadCopyParticipants(status,
// processor, resource, arguments, natures, shared);
// list.addAll(Arrays.asList(deletes));
// }
for (final IModelElement element : elementsToCopy.getModelElements()) {
final CopyParticipant[] deletes= ParticipantManager.loadCopyParticipants(status,
processor, element,
mArguments, natures, shared);
list.addAll(Arrays.asList(deletes));
}
}
public void buildDeltaToDelete(final ElementSet elements,
final IResourceChangeDescriptionFactory resourceDelta) {
for (final IResource resource : elements.getResources()) {
resourceDelta.delete(resource);
}
for (final IResource resource : elements.getResourcesOwnedByElements()) {
resourceDelta.delete(resource);
}
for (final IFile file : elements.getFilesContainingElements()) {
resourceDelta.change(file);
}
}
public void buildDeltaToModify(final ElementSet elements,
final IResourceChangeDescriptionFactory resourceDelta) {
for (final IResource resource : elements.getResources()) {
if (resource instanceof IFile) {
resourceDelta.change((IFile) resource);
}
}
for (final IResource resource : elements.getResourcesOwnedByElements()) {
if (resource instanceof IFile) {
resourceDelta.change((IFile) resource);
}
}
for (final IFile file : elements.getFilesContainingElements()) {
resourceDelta.change(file);
}
}
/**
* @param changeName the name of the change
* @param resources the resources to delete
* @param manager the text change manager
* @return the created change
* @throws CoreException
*/
public Change createChangeToDelete(final String changeName,
final ElementSet elementsToDelete,
final TextChangeManager manager, final IProgressMonitor monitor) throws CoreException {
final SubMonitor m= SubMonitor.convert(monitor, 2);
final CompositeChange result= new CompositeChange(changeName);
addChangesToDelete(result, elementsToDelete, manager, m.newChild(1));
result.addAll(manager.getAllChanges());
m.worked(1);
return result;
}
public Change createChangeToMove(final String changeName,
final ElementSet elementsToMove, final RefactoringDestination destination,
final TextChangeManager manager, final IProgressMonitor monitor) throws CoreException {
final SubMonitor m= SubMonitor.convert(monitor, 4);
final CompositeChange result= new CompositeChange(changeName);
final String code= getSourceCodeStringedTogether(elementsToMove, m.newChild(1));
addChangesToDelete(result, elementsToMove, manager, m.newChild(1));
addChangesToInsert(result, code, destination, manager, m.newChild(1));
result.addAll(manager.getAllChanges());
m.worked(1);
return result;
}
public Change createChangeToCopy(final String changeName,
final ElementSet elementsToMove, final RefactoringDestination destination,
final TextChangeManager manager, final IProgressMonitor monitor) throws CoreException {
final SubMonitor m= SubMonitor.convert(monitor, 3);
final CompositeChange result= new CompositeChange(changeName);
final String code= getSourceCodeStringedTogether(elementsToMove, m.newChild(1));
addChangesToInsert(result, code, destination, manager, m.newChild(1));
result.addAll(manager.getAllChanges());
m.worked(1);
return result;
}
public Change createChangeToInsert(final String changeName,
final String code, final RefactoringDestination destination,
final TextChangeManager manager,
final IProgressMonitor monitor) throws CoreException {
assert (changeName != null && code != null && destination != null && manager != null);
final SubMonitor m= SubMonitor.convert(monitor, 2);
final CompositeChange result= new CompositeChange(changeName);
addChangesToInsert(result, code, destination, manager, m.newChild(1));
result.addAll(manager.getAllChanges());
m.worked(1);
return result;
}
protected void addChangesToDelete(final CompositeChange result,
final ElementSet elements,
final TextChangeManager manager, final SubMonitor m) throws CoreException {
for (final IResource resource : elements.getResources()) {
result.add(createChangeToDelete(elements, resource));
}
final Map<ISourceUnit, List<IModelElement>> suSubChanges= new HashMap<>();
for (final IModelElement element : elements.getModelElements()) {
final IResource resource= elements.getOwningResource(element);
if (resource != null) {
result.add(createChangeToDelete(elements, resource));
}
else {
final ISourceUnit su= LTKUtils.getSourceUnit(element);
List<IModelElement> list= suSubChanges.get(su);
if (list == null) {
list= new ArrayList<>(1);
suSubChanges.put(su, list);
}
list.add(element);
}
}
if (!suSubChanges.isEmpty()) {
m.setWorkRemaining(suSubChanges.size() * 3);
for (final Map.Entry<ISourceUnit, List<IModelElement>> suChanges : suSubChanges.entrySet()) {
createChangeToDelete(elements, suChanges.getKey(), suChanges.getValue(), manager, m);
}
}
}
private void createChangeToDelete(final ElementSet elements,
final ISourceUnit su, final List<IModelElement> elementsInUnit,
final TextChangeManager manager, final SubMonitor m) throws CoreException {
if (!(su instanceof IWorkspaceSourceUnit)
|| ((IWorkspaceSourceUnit) su).getResource().getType() != IResource.FILE ) {
throw new IllegalArgumentException();
}
su.connect(m.newChild(1));
try {
final MultiTextEdit rootEdit= getRootEdit(manager, su);
final BasicHeuristicTokenScanner scanner= getScanner(su);
final AbstractDocument document= su.getDocument(null);
for (final IModelElement element : elementsInUnit) {
final ISourceElement member= (ISourceElement) element;
final TextRegion sourceRange= expandElementRange(member, document, scanner);
final DeleteEdit edit= new DeleteEdit(sourceRange.getStartOffset(), sourceRange.getLength());
rootEdit.addChild(edit);
}
m.worked(1);
}
catch (final BadLocationException e) {
throw new CoreException(failCreation(e));
}
catch (final BadPartitioningException e) {
throw new CoreException(failCreation(e));
}
finally {
su.disconnect(m.newChild(1));
}
}
protected Change createChangeToDelete(final ElementSet elements, final IModelElement element) throws CoreException {
final IResource resource= elements.getOwningResource(element);
if (resource != null) {
return createChangeToDelete(elements, resource);
}
throw new IllegalStateException();
}
protected Change createChangeToDelete(final ElementSet elements, final IResource resource) {
if (resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT) {
throw new IllegalStateException();
}
return new DeleteResourceChange(resource.getFullPath(), true);
}
protected Change createChangeToDelete(final ElementSet elements, final ISourceUnit su) throws CoreException {
if (su instanceof IWorkspaceSourceUnit) {
return createChangeToDelete(elements, ((IWorkspaceSourceUnit) su).getResource());
}
throw new IllegalStateException();
}
protected void addChangesToInsert(final CompositeChange result, final String code,
final RefactoringDestination destination,
final TextChangeManager manager, final SubMonitor m) throws CoreException {
final ISourceElement element= (ISourceElement) destination.getModelElements().get(0);
final ISourceUnit su= LTKUtils.getSourceUnit(element);
m.setWorkRemaining(3);
createChangeToInsert(su, code, element, destination, manager, m);
}
private void createChangeToInsert(final ISourceUnit su,
final String code,
final ISourceElement desElement, final RefactoringDestination destination,
final TextChangeManager manager, final SubMonitor m) throws CoreException {
if (!(su instanceof IWorkspaceSourceUnit)
|| ((IWorkspaceSourceUnit) su).getResource().getType() != IResource.FILE ) {
throw new IllegalArgumentException();
}
su.connect(m.newChild(1));
try {
final AbstractDocument document= su.getDocument(m.newChild(1));
final BasicHeuristicTokenScanner scanner= getScanner(su);
final int offset;
if (destination.getPosition() == RefactoringDestination.Position.AT) {
offset= destination.getOffset();
}
else {
offset= getInsertionOffset(document, desElement, destination.getPosition(), scanner);
}
final MultiTextEdit rootEdit= getRootEdit(manager, su);
final InsertEdit edit= new InsertEdit(offset, code);
rootEdit.addChild(edit);
((SourceUnitChange) manager.get(su)).setInsertPosition(new Position(edit.getOffset()));
m.worked(1);
}
catch (final BadLocationException e) {
throw new CoreException(failCreation(e));
}
catch (final BadPartitioningException e) {
throw new CoreException(failCreation(e));
}
finally {
su.disconnect(m.newChild(1));
}
}
protected int getInsertionOffset(final AbstractDocument document, final ISourceElement element,
final RefactoringDestination.Position pos,
final BasicHeuristicTokenScanner scanner) throws BadLocationException, BadPartitioningException {
final TextRegion range= expandElementRange(element, document, scanner);
if (pos == RefactoringDestination.Position.ABOVE) {
final int offset= range.getStartOffset();
return offset;
}
else {
int offset= range.getEndOffset();
final int line= document.getLineOfOffset(offset);
final IRegion lineInformation= document.getLineInformation(line);
if (offset == lineInformation.getOffset() + lineInformation.getLength()) {
offset+= document.getLineDelimiter(line).length();
}
return offset;
}
}
private MultiTextEdit getRootEdit(final TextChangeManager manager, final ISourceUnit su) {
final TextFileChange textFileChange= manager.get(su);
if (su.getWorkingContext() == LTK.EDITOR_CONTEXT) {
textFileChange.setSaveMode(TextFileChange.LEAVE_DIRTY);
}
if (textFileChange.getEdit() == null) {
textFileChange.setEdit(new MultiTextEdit());
}
return (MultiTextEdit) textFileChange.getEdit();
}
protected IStatus failDocAnalyzation(final Throwable e) {
return new Status(IStatus.ERROR, LTK.BUNDLE_ID, Messages.Common_error_AnalyzingSourceDocument_message, e);
}
protected IStatus failCreation(final Throwable e) {
return new Status(IStatus.ERROR, LTK.BUNDLE_ID, Messages.Common_error_CreatingElementChange_message, e);
}
}