diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java
index c7a6a21131..2f8a5b61c3 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/MavenPlugin.java
@@ -13,9 +13,13 @@
package org.eclipse.m2e.core;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+
import org.eclipse.m2e.core.embedder.IMaven;
import org.eclipse.m2e.core.embedder.IMavenConfiguration;
import org.eclipse.m2e.core.embedder.MavenModelManager;
+import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.MavenPluginActivator;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
@@ -71,4 +75,12 @@ public static IWorkspaceClassifierResolverManager getWorkspaceClassifierResolver
return MavenPluginActivator.getDefault().getWorkspaceClassifierResolverManager();
}
+ public static boolean isMavenProject(IProject project) {
+ try {
+ return project != null && project.isAccessible() && project.hasNature(IMavenConstants.NATURE_ID);
+ } catch(CoreException ex) {
+ }
+ return false;
+ }
+
}
diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java
index 49e90a1c6c..6085e0ed9e 100644
--- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java
+++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/project/registry/ProjectRegistryRefreshJob.java
@@ -43,9 +43,9 @@
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
+import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.IMaven;
import org.eclipse.m2e.core.embedder.IMavenConfiguration;
-import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.Messages;
import org.eclipse.m2e.core.internal.embedder.MavenImpl;
import org.eclipse.m2e.core.internal.jobs.IBackgroundProcessingQueue;
@@ -199,7 +199,7 @@ public void resourceChanged(IResourceChangeEvent event) {
if(IResourceChangeEvent.PRE_CLOSE == type || IResourceChangeEvent.PRE_DELETE == type) {
IProject project = (IProject) event.getResource();
- if(isMavenProject(project)) {
+ if(MavenPlugin.isMavenProject(project)) {
queue(new MavenUpdateRequest(project, offline, forceDependencyUpdate));
}
} else {
@@ -211,7 +211,7 @@ public void resourceChanged(IResourceChangeEvent event) {
IResourceDelta[] projectDeltas = delta.getAffectedChildren();
for(IResourceDelta projectDelta : projectDeltas) {
IProject project = (IProject) projectDelta.getResource();
- if(!isMavenProject(project)) {
+ if(!MavenPlugin.isMavenProject(project)) {
continue;
}
//Bug 436679: queue update request only for reopened projects.
@@ -253,12 +253,4 @@ public boolean isEmpty() {
}
}
- private boolean isMavenProject(IProject project) {
- try {
- return project != null && project.isAccessible() && project.hasNature(IMavenConstants.NATURE_ID);
- } catch(CoreException ex) {
- log.error(ex.getMessage(), ex);
- }
- return false;
- }
}
diff --git a/org.eclipse.m2e.jdt.ui/META-INF/MANIFEST.MF b/org.eclipse.m2e.jdt.ui/META-INF/MANIFEST.MF
index 8e2e255d17..d9b89b5077 100644
--- a/org.eclipse.m2e.jdt.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.m2e.jdt.ui/META-INF/MANIFEST.MF
@@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.m2e.jdt;bundle-version="[2.0.0,3.0.0)",
org.eclipse.m2e.maven.runtime;bundle-version="[3.8.6,4.0.0)",
org.eclipse.m2e.core;bundle-version="[2.0.0,3.0.0)",
org.eclipse.m2e.core.ui;bundle-version="[2.0.0,3.0.0)",
- org.eclipse.ui
+ org.eclipse.ui,
+ org.eclipse.jface.text;bundle-version="3.21.0"
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.m2e.jdt.ui.internal.MavenJdtUiPlugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
diff --git a/org.eclipse.m2e.jdt.ui/plugin.xml b/org.eclipse.m2e.jdt.ui/plugin.xml
index c82bdb1f61..194f1df0b7 100644
--- a/org.eclipse.m2e.jdt.ui/plugin.xml
+++ b/org.eclipse.m2e.jdt.ui/plugin.xml
@@ -335,5 +335,13 @@
id="org.eclipse.m2e.jdt.keyword"
label="java">
+
+
+
+
diff --git a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyJavaCompletionProposal.java b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyJavaCompletionProposal.java
new file mode 100644
index 0000000000..0039bb2f7b
--- /dev/null
+++ b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyJavaCompletionProposal.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Christoph Läubrich 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
+ *******************************************************************************/
+
+package org.eclipse.m2e.jdt.ui.internal;
+
+import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCIES;
+import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.getChild;
+import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.performOnDOMDocument;
+
+import java.io.IOException;
+
+import org.w3c.dom.Element;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.m2e.core.embedder.ArtifactKey;
+import org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation;
+import org.eclipse.m2e.core.ui.internal.editing.PomEdits.OperationTuple;
+import org.eclipse.m2e.core.ui.internal.editing.PomHelper;
+
+
+@SuppressWarnings("restriction")
+public class AddDependencyJavaCompletionProposal implements IJavaCompletionProposal {
+
+ private ArtifactKey artifactKey;
+
+ private IFile pomfile;
+
+ public AddDependencyJavaCompletionProposal(ArtifactKey artifactKey, IFile pomfile) {
+ this.artifactKey = artifactKey;
+ this.pomfile = pomfile;
+ }
+
+ public void apply(IDocument javaDocument) {
+ try {
+ performOnDOMDocument(new OperationTuple(pomfile, (Operation) document -> {
+ Element depsEl = getChild(document.getDocumentElement(), DEPENDENCIES);
+ PomHelper.addOrUpdateDependency(depsEl, artifactKey.groupId(), artifactKey.artifactId(), artifactKey.version(),
+ null, "compile", null);
+ }));
+ } catch(IOException ex) {
+ MavenJdtUiPlugin.getDefault().getLog().error("Can't modify file " + pomfile, ex);
+ } catch(CoreException ex) {
+ MavenJdtUiPlugin.getDefault().getLog().log(ex.getStatus());
+ }
+
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public String getDisplayString() {
+ return "Add " + artifactKey.groupId() + ":" + artifactKey.artifactId() + ":" + artifactKey.version()
+ + " as dependency";
+ }
+
+ public Image getImage() {
+ return MavenJdtUiPlugin.getDefault().getImageRegistry().get(MavenJdtUiPlugin.M2E_ICON);
+ }
+
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ public int getRelevance() {
+ return 100;
+ }
+
+}
diff --git a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyQuickFixProcessor.java b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyQuickFixProcessor.java
new file mode 100644
index 0000000000..cb04d27641
--- /dev/null
+++ b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/AddDependencyQuickFixProcessor.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Christoph Läubrich 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
+ *******************************************************************************/
+
+package org.eclipse.m2e.jdt.ui.internal;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Adapters;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SimpleName;
+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.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.SearchPattern;
+import org.eclipse.jdt.core.search.SearchRequestor;
+import org.eclipse.jdt.ui.text.java.IInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;
+
+import org.eclipse.m2e.core.MavenPlugin;
+import org.eclipse.m2e.core.embedder.ArtifactKey;
+import org.eclipse.m2e.core.project.IMavenProjectFacade;
+
+
+public class AddDependencyQuickFixProcessor implements IQuickFixProcessor {
+
+ public boolean hasCorrections(ICompilationUnit unit, int problemId) {
+ switch(problemId) {
+ case IProblem.UndefinedName:
+ case IProblem.ImportNotFound:
+ case IProblem.UndefinedType:
+ case IProblem.UnresolvedVariable:
+ case IProblem.MissingTypeInMethod:
+ case IProblem.MissingTypeInConstructor:
+ IJavaElement parent = unit.getParent();
+ if(parent != null) {
+ IJavaProject project = parent.getJavaProject();
+ if(project != null) {
+ return MavenPlugin.isMavenProject(project.getProject());
+ }
+ }
+ }
+ return false;
+ }
+
+ public IJavaCompletionProposal[] getCorrections(IInvocationContext context, IProblemLocation[] locations)
+ throws CoreException {
+ Map> possibleKeys = new HashMap<>();
+ for(IProblemLocation location : locations) {
+ switch(location.getProblemId()) {
+ case IProblem.ImportNotFound:
+ case IProblem.UndefinedName:
+ case IProblem.UndefinedType:
+ case IProblem.UnresolvedVariable:
+ case IProblem.MissingTypeInMethod:
+ case IProblem.MissingTypeInConstructor:
+ handleImportNotFound(context, location, possibleKeys);
+ }
+ }
+ return possibleKeys.entrySet().stream()
+ .flatMap(entry -> entry.getValue().stream()
+ .map(key -> new AddDependencyJavaCompletionProposal(key, entry.getKey().getPom())))
+ .toArray(IJavaCompletionProposal[]::new);
+ }
+
+ private void handleImportNotFound(IInvocationContext context, IProblemLocation problemLocation,
+ Map> possibleKeys) throws CoreException {
+ CompilationUnit cu = context.getASTRoot();
+ ASTNode selectedNode = problemLocation.getCoveringNode(cu);
+ if(selectedNode != null) {
+ String className = getClassName(selectedNode);
+ if(className != null) {
+ IMavenProjectFacade currentFacade = Adapters.adapt(cu.getJavaElement().getJavaProject(),
+ IMavenProjectFacade.class);
+ if(currentFacade != null) {
+ Set artifacts = findMatchingArtifacts(className, currentFacade);
+ if(!artifacts.isEmpty()) {
+ possibleKeys.computeIfAbsent(currentFacade, x -> new HashSet<>()).addAll(artifacts);
+ }
+ }
+ }
+ }
+ }
+
+ static Set findMatchingArtifacts(String className, IMavenProjectFacade currentFacade)
+ throws CoreException {
+ Set possibleKey = new HashSet();
+ SearchPattern typePattern = SearchPattern.createPattern(className, IJavaSearchConstants.TYPE,
+ IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
+ IJavaSearchScope workspaceScope = SearchEngine.createWorkspaceScope();
+ SearchEngine searchEngine = new SearchEngine();
+ SearchRequestor requestor = new SearchRequestor() {
+
+ @Override
+ public void acceptSearchMatch(SearchMatch aMatch) {
+ Object element = aMatch.getElement();
+ if(element instanceof IType) {
+ IType type = (IType) element;
+ IMavenProjectFacade facade = Adapters.adapt(type.getJavaProject(), IMavenProjectFacade.class);
+ if(facade != null) {
+ ArtifactKey artifactKey = facade.getArtifactKey();
+ if(!artifactKey.equals(currentFacade.getArtifactKey())) {
+ possibleKey.add(artifactKey);
+ }
+ }
+ }
+ }
+ };
+ searchEngine.search(typePattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
+ workspaceScope, requestor, null);
+ return possibleKey;
+ }
+
+ private String getClassName(ASTNode selectedNode) {
+ String className = null;
+ if(selectedNode instanceof Name) {
+ ITypeBinding typeBinding = ((Name) selectedNode).resolveTypeBinding();
+ if(typeBinding != null) {
+ className = typeBinding.getBinaryName();
+ }
+ if(className == null && selectedNode instanceof SimpleName) { // fallback if the type cannot be resolved
+ className = ((SimpleName) selectedNode).getIdentifier();
+ }
+ }
+ return className;
+ }
+
+}
diff --git a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/MavenJdtUiPlugin.java b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/MavenJdtUiPlugin.java
index 194522ac16..6c2f539a8f 100644
--- a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/MavenJdtUiPlugin.java
+++ b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/MavenJdtUiPlugin.java
@@ -9,11 +9,15 @@
package org.eclipse.m2e.jdt.ui.internal;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.ui.plugin.AbstractUIPlugin;
public class MavenJdtUiPlugin extends AbstractUIPlugin {
+ public static final String M2E_ICON = "M2E_ICON";
+
public static final String PLUGIN_ID = "org.eclipse.m2e.jdt.ui"; //$NON-NLS-1$
private static MavenJdtUiPlugin instance;
@@ -25,4 +29,8 @@ public MavenJdtUiPlugin() {
public static MavenJdtUiPlugin getDefault() {
return instance;
}
+
+ protected void initializeImageRegistry(ImageRegistry reg) {
+ reg.put(M2E_ICON, ImageDescriptor.createFromURL(MavenJdtUiPlugin.class.getResource("/icons/m2.gif")));
+ }
}
diff --git a/org.eclipse.m2e.jdt/.project b/org.eclipse.m2e.jdt/.project
index f2555af3fb..868fff8d01 100644
--- a/org.eclipse.m2e.jdt/.project
+++ b/org.eclipse.m2e.jdt/.project
@@ -20,6 +20,11 @@
+
+ org.eclipse.pde.ds.core.builder
+
+
+
org.eclipse.jdt.core.javanature
diff --git a/org.eclipse.m2e.jdt/.settings/org.eclipse.pde.ds.annotations.prefs b/org.eclipse.m2e.jdt/.settings/org.eclipse.pde.ds.annotations.prefs
new file mode 100644
index 0000000000..38f9eecff8
--- /dev/null
+++ b/org.eclipse.m2e.jdt/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -0,0 +1,7 @@
+dsVersion=V1_3
+eclipse.preferences.version=1
+enabled=true
+generateBundleActivationPolicyLazy=true
+path=OSGI-INF
+validationErrorLevel=error
+validationErrorLevel.missingImplicitUnbindMethod=error
diff --git a/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF b/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF
index 3aba42369f..e82890689e 100644
--- a/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF
+++ b/org.eclipse.m2e.jdt/META-INF/MANIFEST.MF
@@ -15,6 +15,7 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.m2e.maven.runtime;bundle-version="[3.8.6,4.0.0)",
org.eclipse.m2e.core;bundle-version="[2.0.0,3.0.0)"
+Service-Component: OSGI-INF/org.eclipse.m2e.jdt.internal.JavaProjectAdapterFactory.xml
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.m2e.jdt.MavenJdtPlugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
diff --git a/org.eclipse.m2e.jdt/OSGI-INF/.gitignore b/org.eclipse.m2e.jdt/OSGI-INF/.gitignore
new file mode 100644
index 0000000000..24a7dd2c26
--- /dev/null
+++ b/org.eclipse.m2e.jdt/OSGI-INF/.gitignore
@@ -0,0 +1 @@
+/org.eclipse.m2e.*.xml
diff --git a/org.eclipse.m2e.jdt/build.properties b/org.eclipse.m2e.jdt/build.properties
index 4022234d61..d85bc4b448 100644
--- a/org.eclipse.m2e.jdt/build.properties
+++ b/org.eclipse.m2e.jdt/build.properties
@@ -16,7 +16,8 @@ bin.includes = META-INF/,\
.,\
about.html,\
lifecycle-mapping-metadata.xml,\
- schema/
+ schema/,\
+ OSGI-INF/org.eclipse.m2e.jdt.internal.JavaProjectAdapterFactory.xml
source.. = src/
output.. = bin/
src.includes = about.html
diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/JavaProjectAdapterFactory.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/JavaProjectAdapterFactory.java
new file mode 100644
index 0000000000..70b578a7ae
--- /dev/null
+++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/JavaProjectAdapterFactory.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Christoph Läubrich 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
+ *******************************************************************************/
+
+package org.eclipse.m2e.jdt.internal;
+
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.jdt.core.IJavaProject;
+
+import org.eclipse.m2e.core.project.IMavenProjectFacade;
+import org.eclipse.m2e.core.project.IMavenProjectRegistry;
+
+
+@Component(service = IAdapterFactory.class, property = {
+ IAdapterFactory.SERVICE_PROPERTY_ADAPTABLE_CLASS + "=org.eclipse.jdt.core.IJavaProject",
+ IAdapterFactory.SERVICE_PROPERTY_ADAPTER_NAMES + "=org.eclipse.m2e.core.project.IMavenProjectFacade"})
+public class JavaProjectAdapterFactory implements IAdapterFactory {
+
+ private static final Class>[] ADAPTER_LIST = {IMavenProjectFacade.class};
+
+ @Reference
+ private IMavenProjectRegistry mavenProjectRegistry;
+
+ @Override
+ public T getAdapter(Object adaptableObject, Class adapterType) {
+ if(adaptableObject instanceof IJavaProject javaProject) {
+ if(adapterType == IMavenProjectFacade.class) {
+ return adapterType.cast(mavenProjectRegistry.getProject(javaProject.getProject()));
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Class>[] getAdapterList() {
+ return ADAPTER_LIST;
+ }
+
+}