| /*=============================================================================# |
| # Copyright (c) 2008, 2018 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.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().getOffset()+last.getSourceRange().getLength(); |
| final List<ISourceStructElement> checked= new ArrayList<>(elements.length); |
| for (final ISourceStructElement element : elements) { |
| final ISourceUnit unit= element.getSourceUnit(); |
| final int end= last.getSourceRange().getOffset()+last.getSourceRange().getLength(); |
| 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().getOffset(); |
| int end= elements[0].getSourceRange().getOffset() + elements[0].getSourceRange().getLength(); |
| |
| for (int i= 1; i < elements.length; i++) { |
| if (elements[i].getSourceUnit() != su) { |
| return null; |
| } |
| final int elementStart= elements[i].getSourceRange().getOffset(); |
| 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 IRegion range= expandElementRange(element, doc, scanner); |
| if (range != null && range.getLength() > 0) { |
| codeFragments.add(doc.get(range.getOffset(), range.getLength())); |
| } |
| } |
| |
| public IRegion expandElementRange(final ISourceElement element, final AbstractDocument document, |
| final BasicHeuristicTokenScanner scanner) |
| throws BadLocationException, BadPartitioningException { |
| final IRegion sourceRange= element.getSourceRange(); |
| int start= sourceRange.getOffset(); |
| int end= start + sourceRange.getLength(); |
| |
| final IRegion docRange= element.getDocumentationRange(); |
| if (docRange != null) { |
| if (docRange.getOffset() < start) { |
| start= docRange.getOffset(); |
| } |
| if (docRange.getOffset()+docRange.getLength() > end) { |
| end= docRange.getOffset()+docRange.getLength(); |
| } |
| } |
| |
| return expandSourceRange(start, end, document, scanner); |
| } |
| |
| protected IRegion 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 Region(start, end-start); |
| } |
| |
| public IRegion expandWhitespaceBlock(final AbstractDocument document, final IRegion region, |
| final BasicHeuristicTokenScanner scanner) throws BadLocationException { |
| scanner.configure(document); |
| final int firstLine= document.getLineOfOffset(region.getOffset()); |
| int lastLine= document.getLineOfOffset(region.getOffset()+region.getLength()); |
| if (lastLine > firstLine && document.getLineOffset(lastLine) == region.getOffset()+region.getLength()) { |
| lastLine--; |
| } |
| int result; |
| final int min= document.getLineOffset(firstLine); |
| final int max= document.getLineOffset(lastLine)+document.getLineLength(lastLine); |
| result= scanner.findAnyNonBlankForward(region.getOffset()+region.getLength(), max, true); |
| final int end= (result >= 0) ? result : max; |
| result= scanner.findAnyNonBlankBackward(region.getOffset(), min, true); |
| if (result >= 0) { |
| return new Region(result+1, end-(result+1)); |
| } |
| else { |
| return new Region(min, end-min); |
| } |
| } |
| |
| |
| 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 IRegion sourceRange= expandElementRange(member, document, scanner); |
| final DeleteEdit edit= new DeleteEdit(sourceRange.getOffset(), 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 IRegion range= expandElementRange(element, document, scanner); |
| if (pos == RefactoringDestination.Position.ABOVE) { |
| final int offset= range.getOffset(); |
| |
| return offset; |
| } |
| else { |
| int offset= range.getOffset()+range.getLength(); |
| |
| 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); |
| } |
| |
| } |