/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.internal.corext.refactoring.rename; import com.google.common.base.Objects; import com.google.dart.engine.context.AnalysisContext; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.ExportElement; import com.google.dart.engine.element.ImportElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.element.UriReferencedElement; import com.google.dart.engine.search.SearchEngine; import com.google.dart.engine.search.SearchMatch; import com.google.dart.engine.source.FileBasedSource; import com.google.dart.engine.source.Source; import com.google.dart.engine.source.SourceFactory; import com.google.dart.engine.utilities.source.SourceRange; import com.google.dart.engine.utilities.source.SourceRangeFactory; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.internal.util.SourceRangeUtils; import com.google.dart.tools.core.utilities.io.FilenameUtils; import com.google.dart.tools.core.utilities.net.URIUtilities; import com.google.dart.tools.internal.corext.refactoring.RefactoringCoreMessages; import com.google.dart.tools.internal.corext.refactoring.changes.TextChangeCompatibility; import com.google.dart.tools.internal.corext.refactoring.util.DartElementUtil; import com.google.dart.tools.internal.corext.refactoring.util.ExecutionUtils; import com.google.dart.tools.internal.corext.refactoring.util.RunnableObjectEx; import com.google.dart.tools.internal.corext.refactoring.util.TextChangeManager; import com.google.dart.tools.ui.internal.refactoring.RefactoringMessages; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; 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.TextChange; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.MoveArguments; import org.eclipse.ltk.core.refactoring.participants.MoveParticipant; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.List; /** * {@link MoveParticipant} for updating resource references in Dart libraries. * * @coverage dart.editor.ui.refactoring.core */ public class MoveResourceParticipant_OLD extends MoveParticipant { /** * @return the Java {@link File} which corresponds to the given {@link Source}, may be * {@code null} if cannot be determined. */ private static File getSourceFile(Source source) { if (source instanceof FileBasedSource) { FileBasedSource fileBasedSource = (FileBasedSource) source; return new File(fileBasedSource.getFullName()).getAbsoluteFile(); } return null; } private static boolean isPackageReference(SearchMatch match) throws Exception { Element element = match.getElement(); AnalysisContext context = element.getContext(); Source source = element.getSource(); int offset = match.getSourceRange().getOffset() + "'".length(); String content = context.getContents(source).getData().toString(); return content.startsWith("package:", offset); } /** * @return {@code true} if we can prove that the given {@link UriReferencedElement} has a relative * URI, so should be updated. */ private static boolean isRelativeUri(UriReferencedElement uriElement) { if (uriElement == null) { return false; } String uriString = uriElement.getUri(); if (uriString == null) { return false; } try { URI uri = new URI(uriString); return !uri.isAbsolute(); } catch (URISyntaxException e) { return false; } } private final TextChangeManager changeManager = new TextChangeManager(); private IFile file; @Override public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException { return new RefactoringStatus(); } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { return null; } @Override public Change createPreChange(final IProgressMonitor pm) throws CoreException, OperationCanceledException { return ExecutionUtils.runObjectLog(new RunnableObjectEx<Change>() { @Override public Change runObject() throws Exception { return createChangeEx(pm); } }, null); } @Override public String getName() { return RefactoringMessages.MoveResourceParticipant_name; } @Override protected boolean initialize(Object element) { if (element instanceof IFile) { file = (IFile) element; return true; } return false; } private void addReferenceUpdate(SearchMatch match, IContainer destContainer) throws Exception { Source source = match.getElement().getSource(); String newUri = null; // try to keep package: URI if (isPackageReference(match)) { File destDir = destContainer.getLocation().toFile(); File destFile = new File(destDir, file.getName()); SourceFactory sourceFactory = match.getElement().getContext().getSourceFactory(); FileBasedSource destSource = new FileBasedSource(destFile); URI destUri = sourceFactory.restoreUri(destSource); if (destUri != null) { newUri = destUri.toString(); } } // if no package: URI, prepare relative if (newUri == null) { // prepare source File File sourceFile = getSourceFile(source); if (sourceFile == null) { return; } // prepare relative directory URI sourceUri = sourceFile.getParentFile().toURI(); URI destUri = destContainer.getLocationURI(); URI relative = URIUtilities.relativize(sourceUri, destUri); newUri = relative.getPath(); if (newUri.length() != 0 && !newUri.endsWith("/")) { newUri += "/"; } // append file name newUri += file.getName(); } // prepare "old name" range SourceRange matchRange = match.getSourceRange(); int begin = matchRange.getOffset() + "'".length(); int end = SourceRangeUtils.getEnd(matchRange) - "'".length(); // add TextEdit to replace "old URI" with "new URI" newUri = FilenameUtils.separatorsToUnix(newUri); TextEdit edit = new ReplaceEdit(begin, end - begin, newUri); addTextEdit(source, RefactoringCoreMessages.RenameProcessor_update_reference, edit); } private void addTextEdit(Source source, String groupName, TextEdit textEdit) { IResource resource = DartCore.getProjectManager().getResource(source); TextChange change = getTextChange(resource); if (change == null) { change = changeManager.get(source); } TextChangeCompatibility.addTextEdit(change, groupName, textEdit); } private void addUnitUriTextEdit(CompilationUnitElement fileElement, URI newUnitUri, Element element, SourceRange uriRange) { if (element != null) { Source partUnit = element.getSource(); addUnitUriTextEdit(fileElement.getSource(), newUnitUri, uriRange, partUnit); } } /** * Updates URI of "target" referenced from "source". */ private void addUnitUriTextEdit(Source source, URI sourceUri, SourceRange uriRange, Source target) { File sourceFile = getSourceFile(source); File targetFile = getSourceFile(target); if (sourceFile == null || targetFile == null) { return; } URI targetUri = targetFile.toURI(); URI relative = URIUtilities.relativize(sourceUri, targetUri); String relativeStr = FilenameUtils.separatorsToUnix(relative.toString()); String relativeSource = "'" + relativeStr + "'"; ReplaceEdit textEdit = new ReplaceEdit( uriRange.getOffset(), uriRange.getLength(), relativeSource); String msg = RefactoringCoreMessages.RenameProcessor_update_reference; addTextEdit(source, msg, textEdit); } /** * Implementation of {@link #createChange(IProgressMonitor)} which can throw any exception. */ private Change createChangeEx(IProgressMonitor pm) throws Exception { MoveArguments arguments = getArguments(); // prepare unit CompilationUnitElement fileElement = DartElementUtil.getCompilationUnitElement(file); if (fileElement == null) { return null; } // update references Object destination = arguments.getDestination(); if (arguments.getUpdateReferences() && destination instanceof IContainer) { IContainer destContainer = (IContainer) destination; // prepare references SearchEngine searchEngine = DartCore.getProjectManager().newSearchEngine(); List<SearchMatch> references = searchEngine.searchReferences(fileElement, null, null); // update references for (SearchMatch match : references) { if (!isFileReference(match)) { continue; } addReferenceUpdate(match, destContainer); } // if moved Unit is defining library, updates references from it to its components { LibraryElement library = fileElement.getLibrary(); if (library != null && Objects.equal(library.getDefiningCompilationUnit(), fileElement)) { // update directives URI newUnitUri = destContainer.getLocationURI(); for (CompilationUnitElement unitElement : library.getParts()) { updateUriElement(fileElement, unitElement, newUnitUri); } for (ImportElement importElement : library.getImports()) { updateUriElement(fileElement, importElement, newUnitUri); } for (ExportElement exportElement : library.getExports()) { updateUriElement(fileElement, exportElement, newUnitUri); } } } } // return as single CompositeChange TextChange[] changes = changeManager.getAllChanges(); if (changes.length != 0) { return new CompositeChange(getName(), changes); } else { return null; } } /** * Check if the the given {@link SearchMatch} is a reference to {@link #file}. */ private boolean isFileReference(SearchMatch match) throws Exception { Source source = match.getElement().getSource(); SourceRange range = match.getSourceRange(); CharSequence sourceContent = source.getContents().getData(); String uri = sourceContent.subSequence(range.getOffset(), range.getEnd()).toString(); String fileName = file.getName(); if (uri.startsWith("\"") && uri.endsWith(fileName + "\"")) { return true; } if (uri.startsWith("'") && uri.endsWith(fileName + "'")) { return true; } return false; } private void updateUriElement(CompilationUnitElement fileElement, UriReferencedElement uriElement, URI newUnitUri) { // may be not relative if (!isRelativeUri(uriElement)) { return; } // prepare target Element Element targetElement = uriElement; if (targetElement instanceof ImportElement) { targetElement = ((ImportElement) targetElement).getImportedLibrary(); } if (targetElement instanceof ExportElement) { targetElement = ((ExportElement) targetElement).getExportedLibrary(); } // do update SourceRange uriRange = SourceRangeFactory.rangeStartEnd( uriElement.getUriOffset(), uriElement.getUriEnd()); addUnitUriTextEdit(fileElement, newUnitUri, targetElement, uriRange); } }