/* * Copyright (c) 2014, 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.ui.internal.refactoring; import com.google.common.collect.Lists; import com.google.dart.tools.core.internal.util.ResourceUtil; import com.google.dart.tools.core.pub.PubCacheManager_NEW; import com.google.dart.tools.core.refactoring.CompilationUnitChange; import com.google.dart.tools.internal.corext.refactoring.base.DartStatusContext_NEW; import com.google.dart.tools.ui.DartPluginImages; import com.google.dart.tools.ui.DartToolsPlugin; import com.google.dart.tools.ui.internal.text.correction.proposals.ChangeCorrectionProposal; import com.google.dart.tools.ui.internal.text.correction.proposals.CreateFileChange; import com.google.dart.tools.ui.internal.text.correction.proposals.LinkedCorrectionProposal_NEW; import com.google.dart.tools.ui.internal.text.correction.proposals.TrackedPositions; import org.apache.commons.lang3.StringUtils; import org.dartlang.analysis.server.protocol.LinkedEditGroup; import org.dartlang.analysis.server.protocol.LinkedEditSuggestion; import org.dartlang.analysis.server.protocol.LinkedEditSuggestionKind; import org.dartlang.analysis.server.protocol.Location; import org.dartlang.analysis.server.protocol.Position; import org.dartlang.analysis.server.protocol.RefactoringProblem; import org.dartlang.analysis.server.protocol.RefactoringProblemSeverity; import org.dartlang.analysis.server.protocol.SourceChange; import org.dartlang.analysis.server.protocol.SourceEdit; import org.dartlang.analysis.server.protocol.SourceFileEdit; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; 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.RefactoringStatusContext; import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.swt.graphics.Image; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import java.io.File; import java.util.List; /** * Utilities to create LTK wrapper around Engine Services objects. * * @coverage dart.editor.ui.refactoring.ui */ public class ServiceUtils_NEW { /** * @return the {@link CoreException} wrapper around given {@link Throwable}. */ public static CoreException createCoreException(Throwable e) { IStatus status = createRuntimeStatus(e); return new CoreException(status); } /** * Simplifies the given {@link Change}. If it is a {@link CompositeChange}, and it is zero * children - returns {@code null}; if it has one child - attempts to simplify it; otherwise * returns the current {@link Change}. */ public static Change expandSingleChildCompositeChanges(Change change) { while (change instanceof CompositeChange) { CompositeChange compositeChange = (CompositeChange) change; Change[] children = compositeChange.getChildren(); if (children.length == 0) { return null; } else if (children.length == 1) { change = children[0]; compositeChange.remove(change); } else { break; } } return change; } public static TextEdit[] toLTK(List<SourceEdit> edits) { List<TextEdit> ltkEdits = Lists.newArrayList(); for (SourceEdit edit : edits) { TextEdit ltkEdit = toLTK(edit); ltkEdits.add(ltkEdit); } return ltkEdits.toArray(new TextEdit[ltkEdits.size()]); } /** * @return the LTK change for the given Services {@link CompositeChange}. */ public static CompositeChange toLTK(SourceChange sourceChange, List<String> externalFiles) { if (sourceChange == null) { return null; } CompositeChange ltkChange = new CompositeChange(sourceChange.getMessage()); for (SourceFileEdit fileEdit : sourceChange.getEdits()) { Change textChange = toLTK(fileEdit); if (textChange != null) { ltkChange.add(textChange); } else { String file = fileEdit.getFile(); externalFiles.add(file); } } return ltkChange; } /** * @return the LTK {@link Change} for the given {@link SourceFileEdit}. */ public static Change toLTK(SourceFileEdit change) { if (change.getFileStamp() == -1) { String filePath = change.getFile(); File fileJava = new File(filePath); return new CreateFileChange(filePath, fileJava, change.getEdits().get(0).getReplacement()); } // prepare IFile IFile file = getFile(change); if (file == null) { return null; } // prepare CompilationUnitChange String name = file.getName(); CompilationUnitChange ltkChange = new CompilationUnitChange(name, file); ltkChange.setEdit(new MultiTextEdit()); // add edits List<SourceEdit> sourceEdits = change.getEdits(); TextEdit ltkEdits[] = toLTK(sourceEdits); try { for (TextEdit ltkEdit : ltkEdits) { ltkChange.addEdit(ltkEdit); } } catch (MalformedTreeException e) { throw new Error(name + " " + StringUtils.join(ltkEdits, " "), e); } // done return ltkChange; } /** * @return the error status for given {@link Throwable}. */ public static RefactoringStatus toLTK(Throwable e) { IStatus status = createRuntimeStatus(e); return RefactoringStatus.create(status); } /** * @return the LTK status for the given {@link RefactoringProblem}s. */ public static RefactoringStatus toRefactoringStatus(List<RefactoringProblem> problems) { RefactoringStatus result = new RefactoringStatus(); for (RefactoringProblem problem : problems) { result.addEntry( toProblemSeverity(problem.getSeverity()), problem.getMessage(), toRefactoringContext(problem.getLocation()), null, RefactoringStatusEntry.NO_CODE); } return result; } /** * @return the {@link LinkedCorrectionProposal_NEW} for the given {@link SourceChange}. */ public static ChangeCorrectionProposal toUI(SourceChange sourceChange, int relevance) { List<SourceFileEdit> fileEdits = sourceChange.getEdits(); if (fileEdits.size() != 1) { return null; } SourceFileEdit fileEdit = fileEdits.get(0); // prepare presentation Image image = DartPluginImages.get(DartPluginImages.IMG_CORRECTION_CHANGE); // prepare TextChange TextChange textChange; Change change = toLTK(fileEdit); if (change instanceof TextChange) { textChange = (TextChange) change; } else if (change != null) { return new ChangeCorrectionProposal(sourceChange.getMessage(), change, relevance, image); } else { return null; } // prepare UI proposal // TODO(scheglov) expose "image" through the server API LinkedCorrectionProposal_NEW uiProposal = new LinkedCorrectionProposal_NEW( sourceChange.getMessage(), fileEdit.getFile(), textChange, relevance, image); // add linked positions List<LinkedEditGroup> linkedGroups = sourceChange.getLinkedEditGroups(); for (int i = 0; i < linkedGroups.size(); i++) { String groupId = "group_" + i; LinkedEditGroup linkedGroup = linkedGroups.get(i); int length = linkedGroup.getLength(); // add positions for (Position position : linkedGroup.getPositions()) { int offset = position.getOffset(); uiProposal.addLinkedPosition( TrackedPositions.forStartLength(offset, length), false, groupId); } // add suggestions for (LinkedEditSuggestion suggestion : linkedGroup.getSuggestions()) { Image icon = getLinkedEditSuggestionIcon(suggestion); uiProposal.addLinkedPositionProposal(groupId, suggestion.getValue(), icon); } } // set end position { Position selection = sourceChange.getSelection(); if (selection != null) { uiProposal.setEndPosition(TrackedPositions.forStartLength(selection.getOffset(), 0)); } } // done return uiProposal; } /** * @return the error {@link IStatus} for the given {@link Throwable}. */ private static IStatus createRuntimeStatus(Throwable e) { return new Status(IStatus.ERROR, DartToolsPlugin.getPluginId(), e.getMessage(), e); } /** * @return the {@link IFile} of the given {@link SourceFileEdit}, may be {@code null} if external. */ private static IFile getFile(SourceFileEdit change) { String filePath = change.getFile(); File fileJava = new File(filePath); IFile file = ResourceUtil.getFile(fileJava); if (PubCacheManager_NEW.isPubCacheResource(file)) { return null; } return file; } private static Image getLinkedEditSuggestionIcon(LinkedEditSuggestion suggestion) { String kind = suggestion.getKind(); ImageDescriptor imageDescriptor = null; if (LinkedEditSuggestionKind.METHOD.equals(kind)) { imageDescriptor = DartPluginImages.DESC_DART_METHOD_PUBLIC; } if (LinkedEditSuggestionKind.PARAMETER.equals(kind)) { imageDescriptor = DartPluginImages.DESC_DART_LOCAL_VARIABLE; } if (LinkedEditSuggestionKind.TYPE.equals(kind)) { imageDescriptor = DartPluginImages.DESC_DART_CLASS_PUBLIC; } if (LinkedEditSuggestionKind.VARIABLE.equals(kind)) { imageDescriptor = DartPluginImages.DESC_DART_LOCAL_VARIABLE; } if (imageDescriptor == null) { return null; } return DartToolsPlugin.getImageDescriptorRegistry().get(imageDescriptor); } private static TextEdit toLTK(SourceEdit edit) { return new ReplaceEdit(edit.getOffset(), edit.getLength(), edit.getReplacement()); } private static int toProblemSeverity(String severity) { if (RefactoringProblemSeverity.FATAL.equals(severity)) { return RefactoringStatus.FATAL; } if (RefactoringProblemSeverity.ERROR.equals(severity)) { return RefactoringStatus.ERROR; } if (RefactoringProblemSeverity.WARNING.equals(severity)) { return RefactoringStatus.WARNING; } return RefactoringStatus.OK; } /** * @return the Dart status context for the given {@link Location}. */ private static RefactoringStatusContext toRefactoringContext(Location location) { if (location == null) { return null; } return new DartStatusContext_NEW(location); } }