/******************************************************************************* * Copyright (c) 2017,2018 Microsoft Corporation and others. * All rights reserved. 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: * Microsoft Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.commands; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation; import org.eclipse.jdt.ls.core.internal.ChangeUtil; import org.eclipse.jdt.ls.core.internal.IConstants; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.TextEditConverter; import org.eclipse.jdt.ls.core.internal.corrections.InnovationContext; import org.eclipse.jdt.ls.core.internal.corrections.proposals.CUCorrectionProposal; import org.eclipse.jdt.ls.core.internal.corrections.proposals.IProposalRelevance; import org.eclipse.jdt.ls.core.internal.handlers.OrganizeImportsHandler; import org.eclipse.jface.text.IDocument; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.text.edits.TextEdit; public class OrganizeImportsCommand { public Object organizeImports(List<Object> arguments) throws CoreException { WorkspaceEdit edit = new WorkspaceEdit(); if (arguments != null && !arguments.isEmpty() && arguments.get(0) instanceof String) { final String fileUri = (String) arguments.get(0); final IPath rootPath = ResourceUtils.filePathFromURI(fileUri); if (rootPath == null) { throw new CoreException(new Status(IStatus.ERROR, IConstants.PLUGIN_ID, "URI is not found")); } final IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = wsroot.getFileForLocation(rootPath); if (resource == null) { resource = wsroot.getContainerForLocation(rootPath); } if (resource != null) { final OrganizeImportsCommand command = new OrganizeImportsCommand(); int type = resource.getType(); switch (type) { case IResource.PROJECT: edit = command.organizeImportsInProject(resource.getAdapter(IProject.class)); break; case IResource.FOLDER: edit = command.organizeImportsInDirectory(fileUri, resource.getProject()); break; case IResource.FILE: edit = command.organizeImportsInFile(fileUri); break; default://This can only be IResource.ROOT. Which is not relevant to jdt.ls // do nothing allow to return the empty WorkspaceEdit. break; } } } return edit; } /** * Organize imports when select a project. * * @param proj * the target project * @return */ public WorkspaceEdit organizeImportsInProject(IProject proj) { WorkspaceEdit rootEdit = new WorkspaceEdit(); HashSet<IJavaElement> result = new HashSet<>(); collectCompilationUnits(JavaCore.create(proj), result, null); for (IJavaElement elem : result) { if (elem.getElementType() == IJavaElement.COMPILATION_UNIT) { organizeImportsInCompilationUnit((ICompilationUnit) elem, rootEdit); } } return rootEdit; } /** * Organize imports underlying a directory * * @param folderUri * Selected folder URI * @param proj * the folder associated project * @return * @throws CoreException */ public WorkspaceEdit organizeImportsInDirectory(String folderUri, IProject proj) throws CoreException { WorkspaceEdit rootEdit = new WorkspaceEdit(); IPackageFragment fragment = null; if (JDTUtils.toURI(folderUri) != null) { fragment = JDTUtils.resolvePackage(folderUri); } // Select an individual package if (fragment != null) { organizeImportsInPackageFragment(fragment, rootEdit); } else if (proj != null) { // Search the packages under the selected folder: IJavaProject javaProject = JavaCore.create(proj); IPath rootPath = ResourceUtils.filePathFromURI(folderUri); IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); HashSet<IJavaElement> result = new HashSet<>(); for (IPackageFragmentRoot root : roots) { if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { String packageRoot = root.getResource().getLocation().toString(); if (packageRoot.toLowerCase().indexOf(rootPath.toString().toLowerCase()) >= 0) { collectCompilationUnits(javaProject, result, null); } } } for (IJavaElement elem : result) { if (elem.getElementType() == IJavaElement.COMPILATION_UNIT) { organizeImportsInCompilationUnit((ICompilationUnit) elem, rootEdit); } } } return rootEdit; } public WorkspaceEdit organizeImportsInFile(String fileUri) { WorkspaceEdit rootEdit = new WorkspaceEdit(); ICompilationUnit unit = null; if (JDTUtils.toURI(fileUri) != null) { unit = JDTUtils.resolveCompilationUnit(fileUri); } if (unit == null) { return rootEdit; } organizeImportsInCompilationUnit(unit, rootEdit); return rootEdit; } public void organizeImportsInPackageFragment(IPackageFragment fragment, WorkspaceEdit rootEdit) throws CoreException { HashSet<IJavaElement> result = new HashSet<>(); collectCompilationUnits(fragment.getParent(), result, fragment.getElementName()); for (IJavaElement elem : result) { if (elem.getElementType() == IJavaElement.COMPILATION_UNIT) { organizeImportsInCompilationUnit((ICompilationUnit) elem, rootEdit); } } } public void organizeImportsInCompilationUnit(ICompilationUnit unit, WorkspaceEdit rootEdit) { try { InnovationContext context = new InnovationContext(unit, 0, unit.getBuffer().getLength() - 1); CUCorrectionProposal proposal = new CUCorrectionProposal("OrganizeImports", CodeActionKind.SourceOrganizeImports, unit, null, IProposalRelevance.ORGANIZE_IMPORTS) { @Override protected void addEdits(IDocument document, TextEdit editRoot) throws CoreException { CompilationUnit astRoot = context.getASTRoot(); OrganizeImportsOperation op = new OrganizeImportsOperation(unit, astRoot, true, false, true, null); TextEdit edit = op.createTextEdit(null); TextEdit staticEdit = OrganizeImportsHandler.wrapStaticImports(edit, astRoot, unit); if (staticEdit.getChildrenSize() > 0) { editRoot.addChild(staticEdit); } } }; addWorkspaceEdit(unit, proposal, rootEdit); } catch (CoreException e) { JavaLanguageServerPlugin.logException("Problem organize imports ", e); } } private void collectCompilationUnits(Object element, Collection<IJavaElement> result, String packagePrefix) { try { if (element instanceof IJavaElement) { IJavaElement elem = (IJavaElement) element; if (elem.exists()) { switch (elem.getElementType()) { case IJavaElement.TYPE: if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) { result.add(elem.getParent()); } break; case IJavaElement.COMPILATION_UNIT: result.add(elem); break; case IJavaElement.IMPORT_CONTAINER: result.add(elem.getParent()); break; case IJavaElement.PACKAGE_FRAGMENT: collectCompilationUnits((IPackageFragment) elem, result); break; case IJavaElement.PACKAGE_FRAGMENT_ROOT: collectCompilationUnits((IPackageFragmentRoot) elem, result, packagePrefix); break; case IJavaElement.JAVA_PROJECT: IPackageFragmentRoot[] roots = ((IJavaProject) elem).getPackageFragmentRoots(); for (int k = 0; k < roots.length; k++) { collectCompilationUnits(roots[k], result, null); } break; } } } } catch (CoreException e) { JavaLanguageServerPlugin.logException("Problem collection compilation unit ", e); } } private void collectCompilationUnits(IPackageFragment pack, Collection<IJavaElement> result) throws JavaModelException { result.addAll(Arrays.asList(pack.getCompilationUnits())); } private void collectCompilationUnits(IPackageFragmentRoot root, Collection<IJavaElement> result, String prefix) throws JavaModelException { if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { IJavaElement[] children = root.getChildren(); for (int i = 0; i < children.length; i++) { IPackageFragment pack = (IPackageFragment) children[i]; if (StringUtils.isBlank(prefix) || pack.getElementName().indexOf(prefix) >= 0) { collectCompilationUnits(pack, result); } } } } private void addWorkspaceEdit(ICompilationUnit cu, CUCorrectionProposal proposal, WorkspaceEdit rootEdit) throws CoreException { TextChange textChange = proposal.getTextChange(); TextEdit edit = textChange.getEdit(); TextEditConverter converter = new TextEditConverter(cu, edit); List<org.eclipse.lsp4j.TextEdit> edits = converter.convert(); if (ChangeUtil.hasChanges(edits)) { rootEdit.getChanges().put(JDTUtils.toURI(cu), edits); } } }