CHE-139: add 'Organize Imports' functionality

Signed-off-by: Valeriy Svydenko <vsvydenko@codenvy.com>
6.19.x
Valeriy Svydenko 2016-03-14 14:46:12 +02:00
parent 0ac1fb0078
commit af3bcd3874
25 changed files with 5223 additions and 38 deletions

View File

@ -120,6 +120,10 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>org.eclipse.search</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.equinox</groupId>
<artifactId>preferences</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.search</groupId>
<artifactId>org.eclipse.search</artifactId>

View File

@ -19,6 +19,7 @@ import com.google.inject.Singleton;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.ide.ext.java.shared.dto.Change;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.ext.java.shared.dto.Problem;
import org.eclipse.che.ide.ext.java.shared.dto.ProposalApplyResult;
import org.eclipse.che.ide.ext.java.shared.dto.ProposalPresentation;
@ -47,11 +48,15 @@ import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.DocumentAdapter;
import org.eclipse.jdt.internal.core.JavaModelStatus;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation;
import org.eclipse.jdt.internal.corext.refactoring.changes.MoveCompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.RenameCompilationUnitChange;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.text.correction.AssistContext;
import org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor;
import org.eclipse.jdt.internal.ui.text.java.JavaAllCompletionProposalComputer;
@ -59,12 +64,14 @@ import org.eclipse.jdt.internal.ui.text.java.RelevanceSorter;
import org.eclipse.jdt.internal.ui.text.java.TemplateCompletionProposalComputer;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.eclipse.jdt.ui.text.java.correction.ChangeCorrectionProposal;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.text.edits.TextEdit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -216,6 +223,91 @@ public class CodeAssist {
}
}
/**
* Organizes the imports of a compilation unit.
*
* @param project
* current java project
* @param fqn
* fully qualified name of the java file
* @return list of imports which have conflicts
*/
public List<ConflictImportDTO> organizeImports(IJavaProject project, String fqn) throws CoreException, BadLocationException {
ICompilationUnit compilationUnit = prepareCompilationUnit(project, fqn);
return createOrganizeImportOperation(compilationUnit, null);
}
/**
* Applies chosen imports after resolving conflicts.
*
* @param project
* current java project
* @param fqn
* fully qualified name of the java file
* @param chosen
* list of chosen imports as result of resolving conflicts which needed to add to all imports.
*/
public void applyChosenImports(IJavaProject project, String fqn, List<String> chosen) throws CoreException, BadLocationException {
ICompilationUnit compilationUnit = prepareCompilationUnit(project, fqn);
createOrganizeImportOperation(compilationUnit, chosen);
}
private List<ConflictImportDTO> createOrganizeImportOperation(ICompilationUnit compilationUnit,
List<String> chosen) throws CoreException {
CodeGenerationSettings settings = JavaPreferencesSettings.getCodeGenerationSettings(compilationUnit.getJavaProject());
OrganizeImportsOperation operation = new OrganizeImportsOperation(compilationUnit,
null,
settings.importIgnoreLowercase,
!compilationUnit.isWorkingCopy(),
true,
chosen,
null);
NullProgressMonitor monitor = new NullProgressMonitor();
TextEdit edit = operation.createTextEdit(monitor);
TypeNameMatch[][] choices = operation.getChoices();
//Apply organize import declarations if operation doesn't have conflicts (choices.length == 0)
//or all conflicts were resolved (!chosen.isEmpty())
if ((chosen != null && !chosen.isEmpty()) || choices == null || choices.length == 0) {
operation.applyChanges(edit, monitor);
return Collections.emptyList();
}
return createListOfDTOMatches(choices);
}
private List<ConflictImportDTO> createListOfDTOMatches(TypeNameMatch[][] choices) {
List<ConflictImportDTO> typeMatches = new ArrayList<>();
for (int i = 0; i < choices.length; i++) {
List<String> nameMatches = new ArrayList<>();
TypeNameMatch[] choice = choices[i];
for (int j = 0; j < choice.length; j++) {
nameMatches.add(choice[j].getFullyQualifiedName());
}
typeMatches.add(DtoFactory.newDto(ConflictImportDTO.class).withTypeMatches(nameMatches));
}
return typeMatches;
}
private ICompilationUnit prepareCompilationUnit(IJavaProject project, String fqn) throws JavaModelException {
ICompilationUnit compilationUnit;
IType type = project.findType(fqn);
if (type == null) {
throw new JavaModelException(
new JavaModelStatus(IJavaModelStatusConstants.CORE_EXCEPTION, "Can't find a file: " + fqn));
}
if (type.isBinary()) {
throw new JavaModelException(
new JavaModelStatus(IJavaModelStatusConstants.CORE_EXCEPTION, "Can't organize imports on binary file"));
} else {
compilationUnit = type.getCompilationUnit();
}
return compilationUnit;
}
private class CodeAssistContext {
private TextViewer viewer;
private int offset;

View File

@ -11,15 +11,18 @@
package org.eclipse.che.jdt.rest;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.ide.ext.java.shared.dto.Problem;
import org.eclipse.che.ide.ext.java.shared.dto.ProposalApplyResult;
import org.eclipse.che.ide.ext.java.shared.dto.Proposals;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.jdt.CodeAssist;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModel;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jface.text.BadLocationException;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
@ -30,6 +33,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.List;
@ -69,9 +73,9 @@ public class CodeAssistService {
@Produces("application/json")
@Consumes("application/json")
public Proposals computeAssistProposals(@QueryParam("projectpath") String projectPath,
@QueryParam("fqn") String fqn,
@QueryParam("offset") int offset,
List<Problem> problems) throws CoreException {
@QueryParam("fqn") String fqn,
@QueryParam("offset") int offset,
List<Problem> problems) throws CoreException {
IJavaProject javaProject = model.getJavaProject(projectPath);
return codeAssist.computeAssistProposals(javaProject, fqn, offset, problems);
@ -86,4 +90,46 @@ public class CodeAssistService {
return codeAssist.getJavaDoc(sessionId, index);
}
/**
* Organizes the imports of a compilation unit.
*
* @param projectPath
* path to the project
* @param fqn
* fully qualified name of the java file
* @return list of imports which have conflicts
*/
@POST
@Path("/organize-imports")
@Produces({MediaType.APPLICATION_JSON})
public List<ConflictImportDTO> organizeImports(@QueryParam("projectpath") String projectPath,
@QueryParam("fqn") String fqn) throws NotFoundException,
CoreException,
BadLocationException {
IJavaProject project = model.getJavaProject(projectPath);
return codeAssist.organizeImports(project, fqn);
}
/**
* Applies chosen imports after resolving conflicts.
*
* @param projectPath
* path to the project
* @param fqn
* fully qualified name of the java file
* @param chosen
* list of chosen imports from conflicts which needed to add to all imports.
*/
@POST
@Path("/apply-imports")
@Consumes(MediaType.APPLICATION_JSON)
public void applyChosenImports(@QueryParam("projectpath") String projectPath,
@QueryParam("fqn") String fqn,
ConflictImportDTO chosen) throws NotFoundException,
CoreException,
BadLocationException {
IJavaProject project = model.getJavaProject(projectPath);
codeAssist.applyChosenImports(project, fqn, chosen.getTypeMatches());
}
}

View File

@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.che.jdt.core;
import org.eclipse.che.ide.ext.java.BaseTest;
import org.eclipse.che.jdt.testplugin.*;
import org.eclipse.che.jdt.testplugin.ProjectTestSetup;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
public class CoreTests extends BaseTest {
private org.eclipse.che.jdt.testplugin.ProjectTestSetup setup;
public CoreTests(ProjectTestSetup setup) {
this.setup = setup;
}
@Before
public void setUp() throws Exception {
setup.setUp();
}
@After
public void tearDown() throws Exception {
setup.tearDown();
}
public static void assertEqualString(String actual, String expected) {
StringAsserts.assertEqualString(actual, expected);
}
public static void assertEqualStringIgnoreDelim(String actual, String expected) throws IOException {
StringAsserts.assertEqualStringIgnoreDelim(actual, expected);
}
public static void assertEqualStringsIgnoreOrder(String[] actuals, String[] expecteds) {
StringAsserts.assertEqualStringsIgnoreOrder(actuals, expecteds);
}
public static void assertNumberOf(String name, int is, int expected) {
assertTrue("Wrong number of " + name + ", is: " + is + ", expected: " + expected, is == expected);
}
protected ImportRewrite newImportsRewrite(ICompilationUnit cu, String[] order, int normalThreshold, int staticThreshold, boolean restoreExistingImports) throws CoreException {
ImportRewrite rewrite= StubUtility.createImportRewrite(cu, restoreExistingImports);
rewrite.setImportOrder(order);
rewrite.setOnDemandImportThreshold(normalThreshold);
rewrite.setStaticOnDemandImportThreshold(staticThreshold);
return rewrite;
}
protected ImportRewrite newImportsRewrite(CompilationUnit cu, String[] order, int normalThreshold, int staticThreshold, boolean restoreExistingImports) {
ImportRewrite rewrite= ImportRewrite.create(cu, restoreExistingImports);
rewrite.setImportOrder(order);
rewrite.setOnDemandImportThreshold(normalThreshold);
rewrite.setStaticOnDemandImportThreshold(staticThreshold);
return rewrite;
}
}

View File

@ -22,6 +22,7 @@ import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.fix.FixMessages;
import org.eclipse.jdt.internal.corext.template.java.CodeTemplateContextType;
@ -6376,6 +6377,7 @@ public class AssistQuickFixTest extends QuickFixTest {
@Test
public void testConvertAnonymousToNested2() throws Exception {
JavaModelManager.getIndexManager().indexAll(fJProject1.getProject());
// Preferences corePrefs= JavaPlugin.getJavaCorePluginPreferences();
fJProject1.setOption(JavaCore.CODEASSIST_FIELD_PREFIXES, "f");
fJProject1.setOption(JavaCore.CODEASSIST_LOCAL_PREFIXES, "l");

View File

@ -0,0 +1,595 @@
/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* <p/>
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation;
import org.eclipse.che.jdt.util.JavaModelUtil;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jdt.core.Flags;
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.ISourceRange;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.Strings;
import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.jdt.internal.ui.text.correction.SimilarElementsRequestor;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.text.edits.TextEdit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class OrganizeImportsOperation implements IWorkspaceRunnable {
public static interface IChooseImportQuery {
/**
* Selects imports from a list of choices.
*
* @param openChoices
* From each array, a type reference has to be selected
* @param ranges
* For each choice the range of the corresponding type reference.
* @return Returns <code>null</code> to cancel the operation, or the
* selected imports.
*/
TypeNameMatch[] chooseImports(TypeNameMatch[][] openChoices, ISourceRange[] ranges);
}
private static class TypeReferenceProcessor {
private static class UnresolvedTypeData {
final SimpleName ref;
final int typeKinds;
final List<TypeNameMatch> foundInfos;
public UnresolvedTypeData(SimpleName ref) {
this.ref = ref;
this.typeKinds = ASTResolving.getPossibleTypeKinds(ref, true);
this.foundInfos = new ArrayList<TypeNameMatch>(3);
}
public void addInfo(TypeNameMatch info) {
for (int i = this.foundInfos.size() - 1; i >= 0; i--) {
TypeNameMatch curr = this.foundInfos.get(i);
if (curr.getTypeContainerName().equals(info.getTypeContainerName())) {
return; // not added. already contains type with same name
}
}
foundInfos.add(info);
}
}
private Set<String> fOldSingleImports;
private Set<String> fOldDemandImports;
private Set<String> fImplicitImports;
private ImportRewrite fImpStructure;
private boolean fDoIgnoreLowerCaseNames;
private IPackageFragment fCurrPackage;
private ScopeAnalyzer fAnalyzer;
private boolean fAllowDefaultPackageImports;
private Map<String, UnresolvedTypeData> fUnresolvedTypes;
private Set<String> fImportsAdded;
private TypeNameMatch[][] fOpenChoices;
private SourceRange[] fSourceRanges;
public TypeReferenceProcessor(Set<String> oldSingleImports, Set<String> oldDemandImports, CompilationUnit root,
ImportRewrite impStructure, boolean ignoreLowerCaseNames) {
fOldSingleImports = oldSingleImports;
fOldDemandImports = oldDemandImports;
fImpStructure = impStructure;
fDoIgnoreLowerCaseNames = ignoreLowerCaseNames;
ICompilationUnit cu = impStructure.getCompilationUnit();
fImplicitImports = new HashSet<String>(3);
fImplicitImports.add(""); //$NON-NLS-1$
fImplicitImports.add("java.lang"); //$NON-NLS-1$
fImplicitImports.add(cu.getParent().getElementName());
fAnalyzer = new ScopeAnalyzer(root);
fCurrPackage = (IPackageFragment)cu.getParent();
fAllowDefaultPackageImports = cu.getJavaProject().getOption(JavaCore.COMPILER_SOURCE, true).equals(JavaCore.VERSION_1_3);
fImportsAdded = new HashSet<String>();
fUnresolvedTypes = new HashMap<String, UnresolvedTypeData>();
}
private boolean needsImport(ITypeBinding typeBinding, SimpleName ref) {
if (!typeBinding.isTopLevel() && !typeBinding.isMember() || typeBinding.isRecovered()) {
return false; // no imports for anonymous, local, primitive types or parameters types
}
int modifiers = typeBinding.getModifiers();
if (Modifier.isPrivate(modifiers)) {
return false; // imports for privates are not required
}
ITypeBinding currTypeBinding = Bindings.getBindingOfParentType(ref);
if (currTypeBinding == null) {
if (ASTNodes.getParent(ref, ASTNode.PACKAGE_DECLARATION) != null) {
return true; // reference in package-info.java
}
return false; // not in a type
}
if (!Modifier.isPublic(modifiers)) {
if (!currTypeBinding.getPackage().getName().equals(typeBinding.getPackage().getName())) {
return false; // not visible
}
}
ASTNode parent = ref.getParent();
while (parent instanceof Type) {
parent = parent.getParent();
}
if (parent instanceof AbstractTypeDeclaration && parent.getParent() instanceof CompilationUnit) {
return true;
}
if (typeBinding.isMember()) {
if (fAnalyzer.isDeclaredInScope(typeBinding, ref, ScopeAnalyzer.TYPES | ScopeAnalyzer.CHECK_VISIBILITY))
return false;
}
return true;
}
/**
* Tries to find the given type name and add it to the import structure.
*
* @param ref
* the name node
*/
public void add(SimpleName ref) {
String typeName = ref.getIdentifier();
if (fImportsAdded.contains(typeName)) {
return;
}
IBinding binding = ref.resolveBinding();
if (binding != null) {
if (binding.getKind() != IBinding.TYPE) {
return;
}
ITypeBinding typeBinding = (ITypeBinding)binding;
if (typeBinding.isArray()) {
typeBinding = typeBinding.getElementType();
}
typeBinding = typeBinding.getTypeDeclaration();
if (!typeBinding.isRecovered()) {
if (needsImport(typeBinding, ref)) {
fImpStructure.addImport(typeBinding);
fImportsAdded.add(typeName);
}
return;
}
} else {
if (fDoIgnoreLowerCaseNames && typeName.length() > 0) {
char ch = typeName.charAt(0);
if (Strings.isLowerCase(ch) && Character.isLetter(ch)) {
return;
}
}
}
fImportsAdded.add(typeName);
fUnresolvedTypes.put(typeName, new UnresolvedTypeData(ref));
}
public boolean process(IProgressMonitor monitor) throws JavaModelException {
try {
int nUnresolved = fUnresolvedTypes.size();
if (nUnresolved == 0) {
return false;
}
char[][] allTypes = new char[nUnresolved][];
int i = 0;
for (Iterator<String> iter = fUnresolvedTypes.keySet().iterator(); iter.hasNext(); ) {
allTypes[i++] = iter.next().toCharArray();
}
final ArrayList<TypeNameMatch> typesFound = new ArrayList<TypeNameMatch>();
final IJavaProject project = fCurrPackage.getJavaProject();
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[]{project});
TypeNameMatchCollector collector = new TypeNameMatchCollector(typesFound);
new SearchEngine()
.searchAllTypeNames(null, allTypes, scope, collector, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, monitor);
boolean is50OrHigher = JavaModelUtil.is50OrHigher(project);
for (i = 0; i < typesFound.size(); i++) {
TypeNameMatch curr = typesFound.get(i);
UnresolvedTypeData data = fUnresolvedTypes.get(curr.getSimpleTypeName());
if (data != null && isVisible(curr) && isOfKind(curr, data.typeKinds, is50OrHigher)) {
if (fAllowDefaultPackageImports || curr.getPackageName().length() > 0) {
data.addInfo(curr);
}
}
}
ArrayList<TypeNameMatch[]> openChoices = new ArrayList<TypeNameMatch[]>(nUnresolved);
ArrayList<SourceRange> sourceRanges = new ArrayList<SourceRange>(nUnresolved);
for (Iterator<UnresolvedTypeData> iter = fUnresolvedTypes.values().iterator(); iter.hasNext(); ) {
UnresolvedTypeData data = iter.next();
TypeNameMatch[] openChoice = processTypeInfo(data.foundInfos);
if (openChoice != null) {
openChoices.add(openChoice);
sourceRanges.add(new SourceRange(data.ref.getStartPosition(), data.ref.getLength()));
}
}
if (openChoices.isEmpty()) {
return false;
}
fOpenChoices = openChoices.toArray(new TypeNameMatch[openChoices.size()][]);
fSourceRanges = sourceRanges.toArray(new SourceRange[sourceRanges.size()]);
return true;
} finally {
monitor.done();
}
}
private TypeNameMatch[] processTypeInfo(List<TypeNameMatch> typeRefsFound) {
int nFound = typeRefsFound.size();
if (nFound == 0) {
// nothing found
return null;
} else if (nFound == 1) {
TypeNameMatch typeRef = typeRefsFound.get(0);
fImpStructure.addImport(typeRef.getFullyQualifiedName());
return null;
} else {
String typeToImport = null;
boolean ambiguousImports = false;
// multiple found, use old imports to find an entry
for (int i = 0; i < nFound; i++) {
TypeNameMatch typeRef = typeRefsFound.get(i);
String fullName = typeRef.getFullyQualifiedName();
String containerName = typeRef.getTypeContainerName();
if (fOldSingleImports.contains(fullName)) {
// was single-imported
fImpStructure.addImport(fullName);
return null;
} else if (fOldDemandImports.contains(containerName) || fImplicitImports.contains(containerName)) {
if (typeToImport == null) {
typeToImport = fullName;
} else { // more than one import-on-demand
ambiguousImports = true;
}
}
}
if (typeToImport != null && !ambiguousImports) {
fImpStructure.addImport(typeToImport);
return null;
}
// return the open choices
return typeRefsFound.toArray(new TypeNameMatch[nFound]);
}
}
private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) {
int flags = curr.getModifiers();
if (Flags.isAnnotation(flags)) {
return is50OrHigher && (typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0;
}
if (Flags.isEnum(flags)) {
return is50OrHigher && (typeKinds & SimilarElementsRequestor.ENUMS) != 0;
}
if (Flags.isInterface(flags)) {
return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0;
}
return (typeKinds & SimilarElementsRequestor.CLASSES) != 0;
}
private boolean isVisible(TypeNameMatch curr) {
int flags = curr.getModifiers();
if (Flags.isPrivate(flags)) {
return false;
}
if (Flags.isPublic(flags) || Flags.isProtected(flags)) {
return true;
}
return curr.getPackageName().equals(fCurrPackage.getElementName());
}
public TypeNameMatch[][] getChoices() {
return fOpenChoices;
}
public ISourceRange[] getChoicesSourceRanges() {
return fSourceRanges;
}
}
private boolean fDoSave;
private boolean fIgnoreLowerCaseNames;
private IChooseImportQuery fChooseImportQuery;
private int fNumberOfImportsAdded;
private int fNumberOfImportsRemoved;
private IProblem fParsingError;
private ICompilationUnit fCompilationUnit;
private CompilationUnit fASTRoot;
private TextEdit edit;
private TypeReferenceProcessor processor;
private boolean hasOpenChoices;
private TypeNameMatch[][] choices;
private List<String> chosenFQN;
private final boolean fAllowSyntaxErrors;
public OrganizeImportsOperation(ICompilationUnit cu,
CompilationUnit astRoot,
boolean ignoreLowerCaseNames,
boolean save,
boolean allowSyntaxErrors,
List<String> chosen,
IChooseImportQuery chooseImportQuery) {
fCompilationUnit = cu;
fASTRoot = astRoot;
fDoSave = save;
fIgnoreLowerCaseNames = ignoreLowerCaseNames;
fAllowSyntaxErrors = allowSyntaxErrors;
fChooseImportQuery = chooseImportQuery;
fNumberOfImportsAdded = 0;
fNumberOfImportsRemoved = 0;
fParsingError = null;
chosenFQN = chosen;
}
/**
* Runs the operation.
*
* @param monitor
* the progress monitor
* @throws CoreException
* thrown when the operation failed
* @throws OperationCanceledException
* Runtime error thrown when operation is canceled.
*/
public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
try {
monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description,
BasicElementLabels.getFileName(fCompilationUnit)), 10);
edit = createTextEdit(new SubProgressMonitor(monitor, 9));
if (edit == null)
return;
JavaModelUtil.applyEdit(fCompilationUnit, edit, fDoSave, new SubProgressMonitor(monitor, 1));
} finally {
monitor.done();
}
}
public void applyChanges(TextEdit edit, IProgressMonitor monitor) throws CoreException {
if (edit == null) {
return;
}
JavaModelUtil.applyEdit(fCompilationUnit, edit, fDoSave, new SubProgressMonitor(monitor, 1));
}
public TextEdit createTextEdit(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
try {
fNumberOfImportsAdded = 0;
fNumberOfImportsRemoved = 0;
monitor.beginTask(Messages.format(CodeGenerationMessages.OrganizeImportsOperation_description,
BasicElementLabels.getFileName(fCompilationUnit)), 9);
CompilationUnit astRoot = fASTRoot;
if (astRoot == null) {
astRoot = SharedASTProvider.getAST(fCompilationUnit, SharedASTProvider.WAIT_YES, new SubProgressMonitor(monitor, 2));
if (monitor.isCanceled())
throw new OperationCanceledException();
} else {
monitor.worked(2);
}
ImportRewrite importsRewrite = StubUtility.createImportRewrite(astRoot, false);
Set<String> oldSingleImports = new HashSet<>();
Set<String> oldDemandImports = new HashSet<>();
List<SimpleName> typeReferences = new ArrayList<>();
List<SimpleName> staticReferences = new ArrayList<>();
if (!collectReferences(astRoot, typeReferences, staticReferences, oldSingleImports, oldDemandImports))
return null;
monitor.worked(1);
processor = new TypeReferenceProcessor(oldSingleImports, oldDemandImports, astRoot, importsRewrite, fIgnoreLowerCaseNames);
Iterator<SimpleName> refIterator = typeReferences.iterator();
while (refIterator.hasNext()) {
SimpleName typeRef = refIterator.next();
processor.add(typeRef);
}
hasOpenChoices = processor.process(new SubProgressMonitor(monitor, 3));
addStaticImports(staticReferences, importsRewrite);
if (hasOpenChoices) {
choices = processor.getChoices();
ISourceRange[] ranges = processor.getChoicesSourceRanges();
if (fChooseImportQuery != null) {
TypeNameMatch[] chosen = fChooseImportQuery.chooseImports(choices, ranges);
for (int i = 0; i < chosen.length; i++) {
TypeNameMatch typeInfo = chosen[i];
importsRewrite.addImport(typeInfo.getFullyQualifiedName());
}
} else if (chosenFQN != null) {
chosenFQN.forEach(importsRewrite::addImport);
}
}
TextEdit result = importsRewrite.rewriteImports(new SubProgressMonitor(monitor, 3));
determineImportDifferences(importsRewrite, oldSingleImports, oldDemandImports);
return result;
} finally {
monitor.done();
}
}
public TypeNameMatch[][] getChoices() {
return choices;
}
private void determineImportDifferences(ImportRewrite importsStructure, Set<String> oldSingleImports, Set<String> oldDemandImports) {
ArrayList<String> importsAdded = new ArrayList<String>();
importsAdded.addAll(Arrays.asList(importsStructure.getCreatedImports()));
importsAdded.addAll(Arrays.asList(importsStructure.getCreatedStaticImports()));
Object[] content = oldSingleImports.toArray();
for (int i = 0; i < content.length; i++) {
String importName = (String)content[i];
if (importsAdded.remove(importName))
oldSingleImports.remove(importName);
}
content = oldDemandImports.toArray();
for (int i = 0; i < content.length; i++) {
String importName = (String)content[i];
if (importsAdded.remove(importName + ".*")) //$NON-NLS-1$
oldDemandImports.remove(importName);
}
fNumberOfImportsAdded = importsAdded.size();
fNumberOfImportsRemoved = oldSingleImports.size() + oldDemandImports.size();
}
private void addStaticImports(List<SimpleName> staticReferences, ImportRewrite importsStructure) {
for (int i = 0; i < staticReferences.size(); i++) {
Name name = staticReferences.get(i);
IBinding binding = name.resolveBinding();
if (binding != null) { // paranoia check
importsStructure.addStaticImport(binding);
}
}
}
// find type references in a compilation unit
private boolean collectReferences(CompilationUnit astRoot, List<SimpleName> typeReferences, List<SimpleName> staticReferences,
Set<String> oldSingleImports, Set<String> oldDemandImports) {
if (!fAllowSyntaxErrors) {
IProblem[] problems = astRoot.getProblems();
for (int i = 0; i < problems.length; i++) {
IProblem curr = problems[i];
if (curr.isError() && (curr.getID() & IProblem.Syntax) != 0) {
fParsingError = problems[i];
return false;
}
}
}
List<ImportDeclaration> imports = astRoot.imports();
for (int i = 0; i < imports.size(); i++) {
ImportDeclaration curr = imports.get(i);
String id = ASTResolving.getFullName(curr.getName());
if (curr.isOnDemand()) {
oldDemandImports.add(id);
} else {
oldSingleImports.add(id);
}
}
IJavaProject project = fCompilationUnit.getJavaProject();
ImportReferencesCollector.collect(astRoot, project, null, typeReferences, staticReferences);
return true;
}
/**
* After executing the operation, returns <code>null</code> if the operation has been executed successfully or
* the range where parsing failed.
*
* @return returns the parse error
*/
public IProblem getParseError() {
return fParsingError;
}
public int getNumberOfImportsAdded() {
return fNumberOfImportsAdded;
}
public int getNumberOfImportsRemoved() {
return fNumberOfImportsRemoved;
}
/**
* @return Returns the scheduling rule for this operation
*/
public ISchedulingRule getScheduleRule() {
return fCompilationUnit.getResource();
}
}

View File

@ -11,6 +11,7 @@
*******************************************************************************/
package org.eclipse.jdt.ui;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@ -37,6 +38,8 @@ import java.util.StringTokenizer;
*/
public class PreferenceConstants {
public static final String ID_PLUGIN = "org.eclipse.jdt.ui";
private PreferenceConstants() {
}
@ -4083,18 +4086,17 @@ public class PreferenceConstants {
* @since 3.1
*/
public static String getPreference(String key, IJavaProject project) {
// String val;
// if (project != null) {
// val = new ProjectScope(project.getProject()).getNode(JavaUI.ID_PLUGIN).get(key, null);
// if (val != null) {
// return val;
// }
// }
// val = InstanceScope.INSTANCE.getNode(JavaUI.ID_PLUGIN).get(key, null);
// if (val != null) {
// return val;
// }
// return DefaultScope.INSTANCE.getNode(JavaUI.ID_PLUGIN).get(key, null);
String val;
if (project != null) {
val = new ProjectScope(project.getProject()).getNode(ID_PLUGIN).get(key, null);
if (val != null && !val.isEmpty()) {
return val;
}
}
val = InstanceScope.INSTANCE.getNode(ID_PLUGIN).get(key, null);
if (val != null) {
return val;
}
return getPreferenceStore().getString(key);
}

View File

@ -24,13 +24,14 @@ import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.api.icon.IconRegistry;
import org.eclipse.che.ide.api.keybinding.KeyBindingAgent;
import org.eclipse.che.ide.api.keybinding.KeyBuilder;
import org.eclipse.che.ide.ext.java.client.action.FileStructureAction;
import org.eclipse.che.ide.ext.java.client.action.FindUsagesAction;
import org.eclipse.che.ide.ext.java.client.action.NewJavaSourceFileAction;
import org.eclipse.che.ide.ext.java.client.action.NewPackageAction;
import org.eclipse.che.ide.ext.java.client.action.OpenDeclarationAction;
import org.eclipse.che.ide.ext.java.client.action.OpenImplementationAction;
import org.eclipse.che.ide.ext.java.client.action.OrganizeImportsAction;
import org.eclipse.che.ide.ext.java.client.action.QuickDocumentationAction;
import org.eclipse.che.ide.ext.java.client.action.FileStructureAction;
import org.eclipse.che.ide.ext.java.client.refactoring.move.CutJavaSourceAction;
import org.eclipse.che.ide.ext.java.client.refactoring.move.MoveAction;
import org.eclipse.che.ide.ext.java.client.refactoring.rename.RenameRefactoringAction;
@ -72,6 +73,7 @@ public class JavaExtension {
MoveAction moveAction,
CutJavaSourceAction cutAction,
FileStructureAction fileStructureAction,
OrganizeImportsAction organizeImportsAction,
RenameRefactoringAction renameRefactoringAction,
QuickDocumentationAction quickDocumentationAction,
OpenDeclarationAction openDeclarationAction,
@ -106,9 +108,11 @@ public class JavaExtension {
actionManager.registerAction("javaCutRefactoring", cutAction);
actionManager.registerAction("javaFindUsages", findUsagesAction);
actionManager.registerAction("javaClassStructure", fileStructureAction);
actionManager.registerAction("organizeImports", organizeImportsAction);
assistantGroup.add(quickDocumentationAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
assistantGroup.add(openDeclarationAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
assistantGroup.add(organizeImportsAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
assistantGroup.add(openImplementationAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
assistantGroup.add(fileStructureAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
assistantGroup.add(findUsagesAction, new Constraints(Anchor.BEFORE, GROUP_ASSISTANT_REFACTORING));
@ -117,10 +121,12 @@ public class JavaExtension {
keyBinding.getGlobal().addKey(new KeyBuilder().alt().control().charCode('b').build(), "openImplementation");
keyBinding.getGlobal().addKey(new KeyBuilder().control().charCode('j').build(), "showQuickDoc");
keyBinding.getGlobal().addKey(new KeyBuilder().control().charCode(KeyCodeMap.F12).build(), "javaClassStructure");
keyBinding.getGlobal().addKey(new KeyBuilder().alt().control().charCode('o').build(), "organizeImports");
} else {
keyBinding.getGlobal().addKey(new KeyBuilder().alt().action().charCode('b').build(), "openImplementation");
keyBinding.getGlobal().addKey(new KeyBuilder().action().charCode('q').build(), "showQuickDoc");
keyBinding.getGlobal().addKey(new KeyBuilder().action().charCode(KeyCodeMap.F12).build(), "javaClassStructure");
keyBinding.getGlobal().addKey(new KeyBuilder().alt().action().charCode('o').build(), "organizeImports");
}
keyBinding.getGlobal().addKey(new KeyBuilder().none().charCode(KeyCodeMap.F4).build(), "openJavaDeclaration");
keyBinding.getGlobal().addKey(new KeyBuilder().shift().charCode(KeyCodeMap.F6).build(), "javaRenameRefactoring");

View File

@ -129,6 +129,27 @@ public interface JavaLocalizationConstant extends Messages {
@Key("file.structure.action.description")
String fileStructureActionDescription();
@Key("organize.imports.name")
String organizeImportsName();
@Key("organize.imports.description")
String organizeImportsDescription();
@Key("organize.imports.failed.title")
String failedToProcessOrganizeImports();
@Key("organize.imports.button.finish")
String organizeImportsButtonFinish();
@Key("organize.imports.button.cancel")
String organizeImportsButtonCancel();
@Key("organize.imports.button.next")
String organizeImportsButtonNext();
@Key("organize.imports.button.back")
String organizeImportsButtonBack();
@Key("show.inherited.members.label")
String showInheritedMembersLabel();

View File

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.action;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.client.organizeimports.OrganizeImportsPresenter;
/**
* Organizes the imports of a compilation unit.
*
* @author Valeriy Svydenko
*/
@Singleton
public class OrganizeImportsAction extends JavaEditorAction implements ProposalAction {
public final static String JAVA_ORGANIZE_IMPORT_ID = "javaOrganizeImports";
private final EditorAgent editorAgent;
private final OrganizeImportsPresenter organizeImportsPresenter;
@Inject
public OrganizeImportsAction(JavaLocalizationConstant locale,
EditorAgent editorAgent,
OrganizeImportsPresenter organizeImportsPresenter) {
super(locale.organizeImportsName(), locale.organizeImportsDescription(), null, editorAgent);
this.editorAgent = editorAgent;
this.organizeImportsPresenter = organizeImportsPresenter;
}
@Override
public void performAsProposal() {
actionPerformed(null);
}
@Override
public String getId() {
return JAVA_ORGANIZE_IMPORT_ID;
}
@Override
public void actionPerformed(ActionEvent e) {
final EditorPartPresenter editor = editorAgent.getActiveEditor();
organizeImportsPresenter.organizeImports(editor);
}
}

View File

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.action;
/**
* This interface is a general type for all completion proposal actions.
* You must implement the interface when you add new completion action.
*
* @author Valeriy Svydenko
*/
public interface ProposalAction {
/** Implement this method to provide proposal action handler */
void performAsProposal();
/** Returns action id */
String getId();
}

View File

@ -13,6 +13,7 @@ package org.eclipse.che.ide.ext.java.client.editor;
import com.google.gwt.user.client.ui.Widget;
import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.ext.java.client.action.ProposalAction;
import org.eclipse.che.ide.jseditor.client.codeassist.CompletionProposal;
import org.eclipse.che.ide.util.loging.Log;
@ -21,13 +22,15 @@ import org.eclipse.che.ide.util.loging.Log;
*/
public class ActionCompletionProposal implements CompletionProposal {
private final String display;
private final String actionId;
private final Icon icon;
private final String display;
private final String actionId;
private final ProposalAction action;
private final Icon icon;
public ActionCompletionProposal(String display, String actionId, Icon icon) {
public ActionCompletionProposal(String display, String actionId, ProposalAction action, Icon icon) {
this.display = display;
this.actionId = actionId;
this.action = action;
this.icon = icon;
}
@ -48,6 +51,10 @@ public class ActionCompletionProposal implements CompletionProposal {
@Override
public void getCompletion(CompletionCallback callback) {
Log.error(getClass(), "Can't run Action " + actionId);
if (action == null) {
Log.error(getClass(), "Can't run Action " + actionId);
} else {
action.performAsProposal();
}
}
}

View File

@ -22,6 +22,7 @@ import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper;
import org.eclipse.che.ide.MimeType;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.ext.java.shared.dto.Change;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.ext.java.shared.dto.Problem;
import org.eclipse.che.ide.ext.java.shared.dto.ProposalApplyResult;
import org.eclipse.che.ide.ext.java.shared.dto.Proposals;
@ -29,6 +30,8 @@ import org.eclipse.che.ide.rest.AsyncRequestCallback;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.rest.Unmarshallable;
import org.eclipse.che.ide.ui.loaders.request.LoaderFactory;
import org.eclipse.che.ide.ui.loaders.request.MessageLoader;
import java.util.ArrayList;
import java.util.List;
@ -47,15 +50,18 @@ public class JavaCodeAssistClient {
private final DtoUnmarshallerFactory unmarshallerFactory;
private final AsyncRequestFactory asyncRequestFactory;
private final String workspaceId;
private final MessageLoader loader;
@Inject
public JavaCodeAssistClient(@Named("cheExtensionPath") String machineExtPath,
DtoUnmarshallerFactory unmarshallerFactory,
AppContext appContext,
LoaderFactory loaderFactory,
AsyncRequestFactory asyncRequestFactory) {
this.machineExtPath = machineExtPath;
this.workspaceId = appContext.getWorkspace().getId();
this.unmarshallerFactory = unmarshallerFactory;
this.loader = loaderFactory.newLoader();
this.asyncRequestFactory = asyncRequestFactory;
}
@ -133,4 +139,46 @@ public class JavaCodeAssistClient {
}
});
}
/**
* Organizes the imports of a compilation unit.
*
* @param projectPath
* path to the project
* @param fqn
* fully qualified name of the java file
* @return list of imports which have conflicts
*/
public Promise<List<ConflictImportDTO>> organizeImports(String projectPath, String fqn) {
String url = machineExtPath +
"/jdt/" +
workspaceId +
"/code-assist/organize-imports?projectpath=" + projectPath +
"&fqn=" + fqn;
return asyncRequestFactory.createPostRequest(url, null)
.loader(loader)
.send(unmarshallerFactory.newListUnmarshaller(ConflictImportDTO.class));
}
/**
* Organizes the imports of a compilation unit.
*
* @param projectPath
* path to the project
* @param fqn
* fully qualified name of the java file
*/
public Promise<Void> applyChosenImports(String projectPath, String fqn, ConflictImportDTO chosen) {
String url = machineExtPath +
"/jdt/" +
workspaceId +
"/code-assist/apply-imports?projectpath=" + projectPath +
"&fqn=" + fqn;
return asyncRequestFactory.createPostRequest(url, chosen)
.loader(loader)
.header(CONTENT_TYPE, MimeType.APPLICATION_JSON)
.send();
}
}

View File

@ -16,6 +16,7 @@ import org.eclipse.che.ide.api.text.Position;
import org.eclipse.che.ide.api.text.annotation.Annotation;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.ext.java.client.JavaResources;
import org.eclipse.che.ide.ext.java.client.action.ProposalAction;
import org.eclipse.che.ide.ext.java.client.projecttree.JavaSourceFolderUtil;
import org.eclipse.che.ide.ext.java.client.refactoring.RefactoringUpdater;
import org.eclipse.che.ide.ext.java.shared.dto.Problem;
@ -42,28 +43,33 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.ide.ext.java.client.editor.JavaCodeAssistProcessor.insertStyle;
/**
* {@link QuickAssistProcessor} for java files.
*/
public class JavaQuickAssistProcessor implements QuickAssistProcessor {
private final JavaCodeAssistClient client;
private final JavaCodeAssistClient client;
/** The resources used for java assistants. */
private final JavaResources javaResources;
private final DtoUnmarshallerFactory unmarshallerFactory;
private final DtoFactory dtoFactory;
private final RefactoringUpdater refactoringUpdater;
private final EditorAgent editorAgent;
private final JavaResources javaResources;
private final Map<String, ProposalAction> proposalActions;
private final DtoUnmarshallerFactory unmarshallerFactory;
private final DtoFactory dtoFactory;
private final RefactoringUpdater refactoringUpdater;
private final EditorAgent editorAgent;
@Inject
public JavaQuickAssistProcessor(final JavaCodeAssistClient client,
final JavaResources javaResources,
Map<String, ProposalAction> proposalActions,
DtoUnmarshallerFactory unmarshallerFactory,
DtoFactory dtoFactory,
RefactoringUpdater refactoringUpdater,
EditorAgent editorAgent) {
this.client = client;
this.javaResources = javaResources;
this.proposalActions = proposalActions;
this.unmarshallerFactory = unmarshallerFactory;
this.dtoFactory = dtoFactory;
this.refactoringUpdater = refactoringUpdater;
@ -119,17 +125,23 @@ public class JavaQuickAssistProcessor implements QuickAssistProcessor {
HasLinkedMode linkedEditor = editor instanceof HasLinkedMode ? (HasLinkedMode)editor : null;
for (ProposalPresentation proposal : presentations) {
CompletionProposal completionProposal;
if (proposal.getActionId() != null) {
completionProposal =
new ActionCompletionProposal(JavaCodeAssistProcessor.insertStyle(javaResources, proposal.getDisplayString()),
proposal.getActionId(), JavaCodeAssistProcessor.getIcon(proposal.getImage())
);
String actionId = proposal.getActionId();
if (actionId != null) {
ProposalAction action = proposalActions.get(actionId);
completionProposal = new ActionCompletionProposal(insertStyle(javaResources, proposal.getDisplayString()),
actionId,
action,
JavaCodeAssistProcessor.getIcon(proposal.getImage())
);
} else {
completionProposal = new JavaCompletionProposal(
proposal.getIndex(),
JavaCodeAssistProcessor.insertStyle(javaResources, proposal.getDisplayString()),
JavaCodeAssistProcessor.getIcon(proposal.getImage()),
client, responds.getSessionId(), linkedEditor, refactoringUpdater, editorAgent);
completionProposal = new JavaCompletionProposal(proposal.getIndex(),
insertStyle(javaResources, proposal.getDisplayString()),
JavaCodeAssistProcessor.getIcon(proposal.getImage()),
client,
responds.getSessionId(),
linkedEditor,
refactoringUpdater,
editorAgent);
}
proposals.add(completionProposal);
}

View File

@ -26,6 +26,8 @@ import org.eclipse.che.ide.api.project.node.settings.SettingsProvider;
import org.eclipse.che.ide.api.reference.FqnProvider;
import org.eclipse.che.ide.ext.java.client.CurrentClassFQNProvider;
import org.eclipse.che.ide.ext.java.client.JavaResources;
import org.eclipse.che.ide.ext.java.client.action.OrganizeImportsAction;
import org.eclipse.che.ide.ext.java.client.action.ProposalAction;
import org.eclipse.che.ide.ext.java.client.dependenciesupdater.JavaClasspathServiceClient;
import org.eclipse.che.ide.ext.java.client.dependenciesupdater.JavaClasspathServiceClientImpl;
import org.eclipse.che.ide.ext.java.client.documentation.QuickDocPresenter;
@ -50,6 +52,8 @@ import org.eclipse.che.ide.ext.java.client.settings.property.PropertyWidgetImpl;
import org.eclipse.che.ide.extension.machine.client.command.valueproviders.CommandPropertyValueProvider;
import org.eclipse.che.ide.settings.common.SettingsPagePresenter;
import static org.eclipse.che.ide.ext.java.client.action.OrganizeImportsAction.JAVA_ORGANIZE_IMPORT_ID;
/**
* @author Evgen Vidolob
* @author Artem Zatsarynnyi
@ -60,6 +64,9 @@ public class JavaGinModule extends AbstractGinModule {
/** {@inheritDoc} */
@Override
protected void configure() {
GinMapBinder<String, ProposalAction> proposalActionMapBinder = GinMapBinder.newMapBinder(binder(), String.class, ProposalAction.class);
proposalActionMapBinder.addBinding(JAVA_ORGANIZE_IMPORT_ID).to(OrganizeImportsAction.class);
bind(NewJavaSourceFileView.class).to(NewJavaSourceFileViewImpl.class).in(Singleton.class);
bind(QuickDocumentation.class).to(QuickDocPresenter.class).in(Singleton.class);
bind(JavaNavigationService.class).to(JavaNavigationServiceImpl.class);

View File

@ -0,0 +1,250 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.organizeimports;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.project.gwt.client.ProjectServiceClient;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.project.tree.VirtualFile;
import org.eclipse.che.ide.api.texteditor.HandlesUndoRedo;
import org.eclipse.che.ide.api.texteditor.UndoableEditor;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.client.editor.JavaCodeAssistClient;
import org.eclipse.che.ide.ext.java.client.projecttree.JavaSourceFolderUtil;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.jseditor.client.document.Document;
import org.eclipse.che.ide.jseditor.client.texteditor.TextEditor;
import org.eclipse.che.ide.rest.AsyncRequestCallback;
import org.eclipse.che.ide.rest.StringUnmarshaller;
import org.eclipse.che.ide.util.loging.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
/**
* The class that manages conflicts with organize imports if if they occur.
*
* @author Valeriy Svydenko
*/
@Singleton
public class OrganizeImportsPresenter implements OrganizeImportsView.ActionDelegate {
private final OrganizeImportsView view;
private final JavaCodeAssistClient javaCodeAssistClient;
private final AppContext appContext;
private final DtoFactory dtoFactory;
private final JavaLocalizationConstant locale;
private final NotificationManager notificationManager;
private int page;
private List<ConflictImportDTO> choices;
private Map<Integer, String> selected;
private VirtualFile file;
private ProjectServiceClient projectService;
private Document document;
private EditorPartPresenter editor;
@Inject
public OrganizeImportsPresenter(OrganizeImportsView view,
AppContext appContext,
ProjectServiceClient projectService,
JavaCodeAssistClient javaCodeAssistClient,
DtoFactory dtoFactory,
JavaLocalizationConstant locale,
NotificationManager notificationManager) {
this.view = view;
this.projectService = projectService;
this.javaCodeAssistClient = javaCodeAssistClient;
this.view.setDelegate(this);
this.appContext = appContext;
this.dtoFactory = dtoFactory;
this.locale = locale;
this.notificationManager = notificationManager;
}
/**
* Make Organize imports operation. If the operation doesn't have conflicts all imports will be applied
* otherwise a special window will be showed for resolving conflicts.
*
* @param editor
* current active editor
*/
public void organizeImports(EditorPartPresenter editor) {
this.editor = editor;
this.document = ((TextEditor)editor).getDocument();
this.file = editor.getEditorInput().getFile();
final String projectPath = file.getProject().getProjectConfig().getPath();
final String fqn = JavaSourceFolderUtil.getFQNForFile(file);
javaCodeAssistClient.organizeImports(projectPath, fqn)
.then(new Operation<List<ConflictImportDTO>>() {
@Override
public void apply(List<ConflictImportDTO> choices) throws OperationException {
if (!choices.isEmpty()) {
show(choices);
} else {
applyChanges(file);
}
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
notificationManager.notify(locale.failedToProcessOrganizeImports(), arg.getMessage(), FAIL, true);
}
});
}
/** {@inheritDoc} */
@Override
public void onNextButtonClicked() {
selected.put(page++, view.getSelectedImport());
if (!selected.containsKey(page)) {
String newSelection = choices.get(page).getTypeMatches().get(0);
selected.put(page, newSelection);
}
view.setSelectedImport(selected.get(page));
view.changePage(choices.get(page));
updateButtonsState();
}
/** {@inheritDoc} */
@Override
public void onBackButtonClicked() {
selected.put(page--, view.getSelectedImport());
view.setSelectedImport(selected.get(page));
view.changePage(choices.get(page));
updateButtonsState();
}
/** {@inheritDoc} */
@Override
public void onFinishButtonClicked() {
selected.put(page, view.getSelectedImport());
ConflictImportDTO result = dtoFactory.createDto(ConflictImportDTO.class).withTypeMatches(new ArrayList<>(selected.values()));
String projectPath = file.getProject().getProjectConfig().getPath();
String fqn = JavaSourceFolderUtil.getFQNForFile(file);
javaCodeAssistClient.applyChosenImports(projectPath, fqn, result)
.then(new Operation<Void>() {
@Override
public void apply(Void arg) throws OperationException {
applyChanges(file);
view.hide();
((TextEditor)editor).setFocus();
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
notificationManager.notify(locale.failedToProcessOrganizeImports(), arg.getMessage(), FAIL, true);
}
});
}
/** {@inheritDoc} */
@Override
public void onCancelButtonClicked() {
((TextEditor)editor).setFocus();
}
/** Show Organize Imports panel with the special information. */
private void show(List<ConflictImportDTO> choices) {
if (choices == null || choices.isEmpty()) {
return;
}
this.choices = choices;
page = 0;
selected = new HashMap<>(choices.size());
String selection = choices.get(0).getTypeMatches().get(0);
selected.put(page, selection);
view.setSelectedImport(selection);
updateButtonsState();
view.show(choices.get(page));
}
/**
* Update content of the file.
*
* @param file
* current file
*/
private void applyChanges(VirtualFile file) {
HandlesUndoRedo undoRedo = null;
if (editor instanceof UndoableEditor) {
undoRedo = ((UndoableEditor)editor).getUndoRedo();
}
try {
if (undoRedo != null) {
undoRedo.beginCompoundChange();
}
replaceContent(file, document);
} catch (final Exception e) {
Log.error(getClass(), e);
} finally {
if (undoRedo != null) {
undoRedo.endCompoundChange();
}
}
}
private void replaceContent(VirtualFile file, final Document document) {
projectService.getFileContent(appContext.getWorkspaceId(),
file.getPath(),
new AsyncRequestCallback<String>(new StringUnmarshaller()) {
@Override
protected void onSuccess(String result) {
document.replace(0, document.getContents().length(), result);
}
@Override
protected void onFailure(Throwable exception) {
Log.error(getClass(), exception);
}
});
}
private void updateButtonsState() {
view.setEnableBackButton(!isFirstPage());
view.setEnableNextButton(!isLastPage());
view.setEnableFinishButton(selected.size() == choices.size());
}
private boolean isFirstPage() {
return page == 0;
}
private boolean isLastPage() {
return (choices.size() - 1) == page;
}
}

View File

@ -0,0 +1,88 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.organizeimports;
import com.google.inject.ImplementedBy;
import org.eclipse.che.ide.api.mvp.View;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
/**
* The visual part of Organize Imports wizard that has an ability to resolve import conflicts.
*
* @author Valeriy Svydenko
*/
@ImplementedBy(OrganizeImportsViewImpl.class)
interface OrganizeImportsView extends View<OrganizeImportsView.ActionDelegate> {
/** Show Organize Imports panel with the list of conflict imports. */
void show(ConflictImportDTO match);
/** Hide Organize Imports panel. */
void hide();
/** @return selected import for current view page. */
String getSelectedImport();
/**
* Selects an import into current view page.
*
* @param fqn
* import which need to select
*/
void setSelectedImport(String fqn);
/**
* Show new view page for choosing necessary import.
*
* @param match
* conflict object with possible options to choose
*/
void changePage(ConflictImportDTO match);
/**
* Setts enable scope of the Finish button.
*
* @param isEnable
* enable state of scope property
*/
void setEnableFinishButton(boolean isEnable);
/**
* Setts enable scope of the Next button.
*
* @param isEnable
* enable state of scope property
*/
void setEnableNextButton(boolean isEnable);
/**
* Setts enable scope of the Back button.
*
* @param isEnable
* enable state of scope property
*/
void setEnableBackButton(boolean isEnable);
interface ActionDelegate {
/** Performs some actions in response to user's clicking on the 'Next' button. */
void onNextButtonClicked();
/** Performs some actions in response to user's clicking on the 'Back' button. */
void onBackButtonClicked();
/** Performs some actions in response to user's clicking on the 'Finish' button. */
void onFinishButtonClicked();
/** Performs some actions in response to user's clicking on the 'Cancel' button. */
void onCancelButtonClicked();
}
}

View File

@ -0,0 +1,175 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.organizeimports;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.ui.window.Window;
import java.util.List;
import static com.google.gwt.dom.client.Style.Cursor.POINTER;
import static org.eclipse.che.ide.api.theme.Style.getEditorSelectionColor;
import static org.eclipse.che.ide.api.theme.Style.getMainFontColor;
/**
* Implements of {@link OrganizeImportsView}.
*
* @author Valeriy Svydenko
*/
@Singleton
final class OrganizeImportsViewImpl extends Window implements OrganizeImportsView {
interface OrganizeImportsViewImplUiBinder extends UiBinder<Widget, OrganizeImportsViewImpl> {
}
private static OrganizeImportsViewImplUiBinder UI_BINDER = GWT.create(OrganizeImportsViewImplUiBinder.class);
@UiField
VerticalPanel container;
@UiField(provided = true)
final JavaLocalizationConstant locale;
private ActionDelegate delegate;
private Button next;
private Button back;
private Button finish;
private String selectedImport;
private Label selectedLabel;
@Inject
public OrganizeImportsViewImpl(JavaLocalizationConstant locale) {
this.locale = locale;
setTitle(locale.organizeImportsName());
setWidget(UI_BINDER.createAndBindUi(this));
createButtons(locale);
}
/** {@inheritDoc} */
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
/** {@inheritDoc} */
@Override
public void show(ConflictImportDTO match) {
container.clear();
List<String> matches = match.getTypeMatches();
for (String fqn : matches) {
final Label label = new Label(fqn);
if (fqn.equals(selectedImport)) {
selectedLabel = label;
selectedLabel.getElement().getStyle().setBackgroundColor(getEditorSelectionColor());
}
label.getElement().getStyle().setColor(getMainFontColor());
label.getElement().getStyle().setCursor(POINTER);
label.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent clickEvent) {
selectedLabel.getElement().getStyle().setBackgroundColor("initial");
selectedLabel = label;
label.getElement().getStyle().setBackgroundColor(getEditorSelectionColor());
}
});
container.add(label);
}
super.show();
}
/** {@inheritDoc} */
public String getSelectedImport() {
return selectedLabel.getText();
}
/** {@inheritDoc} */
@Override
public void setSelectedImport(String fqn) {
this.selectedImport = fqn;
}
/** {@inheritDoc} */
@Override
public void changePage(ConflictImportDTO match) {
show(match);
}
/** {@inheritDoc} */
@Override
public void setEnableFinishButton(boolean isEnable) {
finish.setEnabled(isEnable);
}
/** {@inheritDoc} */
@Override
public void setEnableNextButton(boolean isEnable) {
next.setEnabled(isEnable);
}
/** {@inheritDoc} */
@Override
public void setEnableBackButton(boolean isEnable) {
back.setEnabled(isEnable);
}
private void createButtons(JavaLocalizationConstant locale) {
back = createButton(locale.organizeImportsButtonBack(), "imports-back-button", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
delegate.onBackButtonClicked();
}
});
next = createButton(locale.organizeImportsButtonNext(), "imports-next-button", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
delegate.onNextButtonClicked();
}
});
Button cancel = createButton(locale.organizeImportsButtonCancel(), "imports-cancel-button", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
hide();
delegate.onCancelButtonClicked();
}
});
finish = createPrimaryButton(locale.organizeImportsButtonFinish(), "imports-finish-button", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
delegate.onFinishButtonClicked();
}
});
addButtonToFooter(finish);
addButtonToFooter(cancel);
addButtonToFooter(next);
addButtonToFooter(back);
}
}

View File

@ -0,0 +1,33 @@
<!--
Copyright (c) 2012-2016 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:with field="locale" type="org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant"/>
<ui:style>
.main {
width: 515px;
float: left;
height: 200px;
}
</ui:style>
<g:SimplePanel addStyleNames="{style.main}">
<g:ScrollPanel width="100%" height="100%">
<g:VerticalPanel ui:field="container" width="100%" height="100%"/>
</g:ScrollPanel>
</g:SimplePanel>
</ui:UiBinder>

View File

@ -21,7 +21,7 @@
.label {
float: left;
margin-left: 6px;
margin-left: 3px;
margin-top: 6px;
}

View File

@ -125,6 +125,15 @@ file.structure.action.description = Open File Structure Window
show.inherited.members.label = Press 'Ctrl+F12' to show inherited members
hide.inherited.members.label = Press 'Ctrl+F12' to hide inherited members
##### Organize Imports #####
organize.imports.name = Organize Imports
organize.imports.description = Organize Imports action generates the import statements in a compilation unit
organize.imports.failed.title = Failed to process organize imports
organize.imports.button.finish=Finish
organize.imports.button.cancel=Cancel
organize.imports.button.back=< Back
organize.imports.button.next=Next >
##### Open Implementation #####
open.implementation.action.name = Implementation(s)
open.implementation.action.description = Open Implementation(s)

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.action;
import com.google.gwtmockito.GwtMockitoTestRunner;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.client.organizeimports.OrganizeImportsPresenter;
import org.eclipse.che.ide.jseditor.client.texteditor.EmbeddedTextEditorPresenter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link OrganizeImportsAction}.
*
* @author Valeriy Svydenko
*/
@RunWith(GwtMockitoTestRunner.class)
public class OrganizeImportsActionTest {
@Mock
private OrganizeImportsPresenter organizeImportsPresenter;
@Mock
private EditorAgent editorAgent;
@Mock
private ActionEvent actionEvent;
@Mock
private EmbeddedTextEditorPresenter editor;
@Mock(answer = Answers.RETURNS_MOCKS)
private JavaLocalizationConstant locale;
@InjectMocks
OrganizeImportsAction action;
@Before
public void setUp() throws Exception {
when(editorAgent.getActiveEditor()).thenReturn(editor);
}
public void prepareAction() throws Exception {
verify(locale).organizeImportsName();
verify(locale).organizeImportsDescription();
}
@Test
public void actionShouldBePerformed() {
action.actionPerformed(actionEvent);
verify(organizeImportsPresenter).organizeImports(editor);
}
@Test
public void actionShouldBePerformedAsProposal() {
action.performAsProposal();
verify(organizeImportsPresenter).organizeImports(editor);
}
}

View File

@ -0,0 +1,232 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.client.organizeimports;
import com.google.gwtmockito.GwtMockitoTestRunner;
import org.eclipse.che.api.project.gwt.client.ProjectServiceClient;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorInput;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.project.node.HasProjectConfig;
import org.eclipse.che.ide.api.project.tree.VirtualFile;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.ext.java.client.JavaLocalizationConstant;
import org.eclipse.che.ide.ext.java.client.editor.JavaCodeAssistClient;
import org.eclipse.che.ide.ext.java.shared.dto.ConflictImportDTO;
import org.eclipse.che.ide.jseditor.client.document.Document;
import org.eclipse.che.ide.jseditor.client.texteditor.TextEditor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Valeriy Svydenko
*/
@RunWith(GwtMockitoTestRunner.class)
public class OrganizeImportsPresenterTest {
private final static String PATH = "/project/a/b/A.java";
private final static String WS_ID = "wsId";
@Mock
private OrganizeImportsView view;
@Mock
private AppContext appContext;
@Mock
private ProjectServiceClient projectService;
@Mock
private JavaCodeAssistClient javaCodeAssistClient;
@Mock
private DtoFactory dtoFactory;
@Mock
private JavaLocalizationConstant locale;
@Mock
private NotificationManager notificationManager;
private OrganizeImportsPresenter presenter;
@Mock
private VirtualFile file;
@Mock
private EditorInput editorInput;
@Mock
private HasProjectConfig hasProjectConfig;
@Mock
private ProjectConfigDto projectConfigDto;
@Mock
private TextEditor editor;
@Mock
private Document document;
@Mock
private Promise<List<ConflictImportDTO>> importsPromise;
@Mock
private Promise<Void> resolveConflictsPromise;
@Captor
private ArgumentCaptor<Operation<List<ConflictImportDTO>>> importsOperation;
@Captor
private ArgumentCaptor<Operation<Void>> resolveConflictsOperation;
private ConflictImportDTO conflict1;
private ConflictImportDTO conflict2;
@Before
public void setUp() throws Exception {
when(editor.getEditorInput()).thenReturn(editorInput);
when(editorInput.getFile()).thenReturn(file);
when(file.getProject()).thenReturn(hasProjectConfig);
when(hasProjectConfig.getProjectConfig()).thenReturn(projectConfigDto);
when(projectConfigDto.getPath()).thenReturn(PATH);
when(file.getName()).thenReturn("A.java");
when(file.getPath()).thenReturn(PATH);
when(appContext.getWorkspaceId()).thenReturn(WS_ID);
when(javaCodeAssistClient.organizeImports(anyString(), anyString())).thenReturn(importsPromise);
when(importsPromise.then(Matchers.<Operation<List<ConflictImportDTO>>>anyObject())).thenReturn(importsPromise);
presenter = new OrganizeImportsPresenter(view,
appContext,
projectService,
javaCodeAssistClient,
dtoFactory,
locale,
notificationManager);
prepareConflicts();
}
@Test
public void organizeImportsShouldBeDoneWithoutConflicts() throws Exception {
presenter.organizeImports(editor);
verify(javaCodeAssistClient).organizeImports(PATH, "A");
verify(importsPromise).then(importsOperation.capture());
importsOperation.getValue().apply(Collections.emptyList());
}
private void prepareConflicts() {
conflict1 = Mockito.mock(ConflictImportDTO.class);
conflict2 = Mockito.mock(ConflictImportDTO.class);
List<String> imports1 = Arrays.asList("import1", "import2");
List<String> imports2 = Arrays.asList("import3", "import4");
when(conflict1.getTypeMatches()).thenReturn(imports1);
when(conflict2.getTypeMatches()).thenReturn(imports2);
}
@Test
public void openWindowForResolvingConflicts() throws Exception {
showOrganizeImportsWindow();
verify(view).setSelectedImport("import1");
verify(view).setEnableBackButton(false);
verify(view).setEnableFinishButton(false);
verify(view).setEnableNextButton(true);
verify(view).show(conflict1);
}
private void showOrganizeImportsWindow() throws Exception {
presenter.organizeImports(editor);
List<ConflictImportDTO> result = Arrays.asList(conflict1, conflict2);
verify(javaCodeAssistClient).organizeImports(PATH, "A");
verify(importsPromise).then(importsOperation.capture());
importsOperation.getValue().apply(result);
}
@Test
public void showNextConflictPage() throws Exception {
when(view.getSelectedImport()).thenReturn("import1");
showOrganizeImportsWindow();
presenter.onNextButtonClicked();
verify(view).getSelectedImport();
verify(view).setSelectedImport("import3");
verify(view).changePage(conflict2);
verify(view).setEnableBackButton(true);
verify(view).setEnableFinishButton(true);
verify(view).setEnableNextButton(false);
}
@Test
public void showPreviousConflictPage() throws Exception {
when(view.getSelectedImport()).thenReturn("import4");
showOrganizeImportsWindow();
presenter.onNextButtonClicked();
reset(view);
presenter.onBackButtonClicked();
verify(view).getSelectedImport();
verify(view).setSelectedImport("import4");
verify(view).changePage(conflict1);
verify(view).setEnableBackButton(false);
verify(view).setEnableFinishButton(true);
verify(view).setEnableNextButton(true);
}
@Test
public void focusShouldBeSetAfterClosingWindow() throws Exception {
showOrganizeImportsWindow();
presenter.onCancelButtonClicked();
verify(editor).setFocus();
}
@Test
public void focusShouldBeSetAfterApplyingConflicts() throws Exception {
when(view.getSelectedImport()).thenReturn("import1");
when(dtoFactory.createDto(ConflictImportDTO.class)).thenReturn(conflict1);
when(conflict1.withTypeMatches(Matchers.<List<String>>anyObject())).thenReturn(conflict1);
when(javaCodeAssistClient.applyChosenImports(anyString(), anyString(), any())).thenReturn(resolveConflictsPromise);
when(resolveConflictsPromise.then(Matchers.<Operation<Void>>anyObject())).thenReturn(resolveConflictsPromise);
showOrganizeImportsWindow();
presenter.onFinishButtonClicked();
verify(resolveConflictsPromise).then(resolveConflictsOperation.capture());
resolveConflictsOperation.getValue().apply(null);
verify(editor).setFocus();
verify(view).hide();
}
}

View File

@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.shared.dto;
import org.eclipse.che.dto.shared.DTO;
import java.util.List;
/**
* DTO represents the information about the imports for the organize imports process.
*
* @author Valeriy Svydenko
*/
@DTO
public interface ConflictImportDTO {
/** Returns list of the possible imports for the current conflict. */
List<String> getTypeMatches();
void setTypeMatches(List<String> typeMatches);
ConflictImportDTO withTypeMatches(List<String> typeMatches);
}