diff --git a/org.eclipse.m2e.bnd.ui/.classpath b/org.eclipse.m2e.bnd.ui/.classpath
new file mode 100644
index 0000000000..375961e4d6
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/org.eclipse.m2e.bnd.ui/.project b/org.eclipse.m2e.bnd.ui/.project
new file mode 100644
index 0000000000..e7bfedc0a1
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/.project
@@ -0,0 +1,33 @@
+
+
+ org.eclipse.m2e.bnd.ui
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.ds.core.builder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.core.resources.prefs b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..99f26c0203
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..23fa13b170
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=21
diff --git a/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.pde.ds.annotations.prefs b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.pde.ds.annotations.prefs
new file mode 100644
index 0000000000..5faf08b7d5
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -0,0 +1,7 @@
+dsVersion=V1_4
+eclipse.preferences.version=1
+enabled=true
+generateBundleActivationPolicyLazy=true
+path=OSGI-INF
+validationErrorLevel=error
+validationErrorLevel.missingImplicitUnbindMethod=error
diff --git a/org.eclipse.m2e.bnd.ui/META-INF/MANIFEST.MF b/org.eclipse.m2e.bnd.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..69c958f9ac
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: bnd-maven-plugin integration
+Bundle-SymbolicName: org.eclipse.m2e.bnd.ui
+Bundle-Version: 1.0.0.qualifier
+Import-Package: aQute.bnd.build;version="[4.5.0,5.0.0)",
+ aQute.bnd.maven.lib.configuration;version="1.2.0",
+ aQute.bnd.maven.lib.resolve;version="[1.3.0,2.0.0)",
+ aQute.bnd.osgi;version="[7.0.0,8.0.0)",
+ aQute.bnd.repository.fileset;version="[1.1.0,2.0.0)"
+Require-Bundle: org.eclipse.equinox.common,
+ org.eclipse.core.resources,
+ org.eclipse.m2e.core,
+ org.eclipse.m2e.maven.runtime
+Service-Component: OSGI-INF/org.eclipse.m2e.bnd.ui.BndPluginAdapter.xml
+Bundle-Vendor: Eclipse
+Automatic-Module-Name: org.eclipse.m2e.bnd.ui
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-21
diff --git a/org.eclipse.m2e.bnd.ui/OSGI-INF/.gitignore b/org.eclipse.m2e.bnd.ui/OSGI-INF/.gitignore
new file mode 100644
index 0000000000..b878e882ac
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/OSGI-INF/.gitignore
@@ -0,0 +1 @@
+/*.xml
diff --git a/org.eclipse.m2e.bnd.ui/build.properties b/org.eclipse.m2e.bnd.ui/build.properties
new file mode 100644
index 0000000000..d5ed28ecef
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/
+jars.extra.classpath = platform:/plugin/org.eclipse.wildwebdeveloper.xml/language-servers/server/org.eclipse.lemminx-uber.jar
diff --git a/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/BndPluginAdapter.java b/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/BndPluginAdapter.java
new file mode 100644
index 0000000000..3dee965432
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/BndPluginAdapter.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.m2e.bnd.ui;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.AdapterTypes;
+import org.eclipse.core.runtime.Adapters;
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata;
+import org.eclipse.m2e.core.project.IMavenProjectFacade;
+import org.eclipse.m2e.core.project.configurator.MojoExecutionKey;
+import org.osgi.service.component.annotations.Component;
+
+import aQute.bnd.build.Project;
+import aQute.bnd.build.Workspace;
+import aQute.bnd.maven.lib.configuration.BeanProperties;
+import aQute.bnd.maven.lib.resolve.ImplicitFileSetRepository;
+import aQute.bnd.osgi.Processor;
+
+/**
+ * Adapts eclipse projects managed by m2e to bnd projects
+ */
+@Component
+@AdapterTypes(adaptableClass = IProject.class, adapterNames = Project.class)
+public class BndPluginAdapter implements IAdapterFactory {
+
+ @Override
+ public T getAdapter(Object adaptableObject, Class adapterType) {
+ try {
+ if (adaptableObject instanceof IProject eclipseProject) {
+ if (adapterType == Project.class) {
+ IMavenProjectFacade mavenProject = Adapters.adapt(eclipseProject, IMavenProjectFacade.class);
+ if (isRelevantProject(mavenProject)) {
+ System.out.println(eclipseProject.getName() + " uses bnd plugin!");
+ BeanProperties beanProperties = new BeanProperties();
+ MavenProject mp = mavenProject.getMavenProject();
+ if (mp != null) {
+ beanProperties.put("project", mp);
+ }
+ // TODO beanProperties.put("settings", settings);
+ Properties processorProperties = new Properties(beanProperties);
+ if (mp != null) {
+ Properties projectProperties = mp.getProperties();
+ for (String key : projectProperties.stringPropertyNames()) {
+ processorProperties.setProperty(key, projectProperties.getProperty(key));
+ }
+ }
+ Processor processor = new Processor(processorProperties, false);
+ Collection list = new ArrayList();
+ if (mp != null) {
+ for (Artifact artifact : mp.getArtifacts()) {
+ list.add(artifact.getFile());
+ }
+ }
+ ImplicitFileSetRepository repository = new ImplicitFileSetRepository("Project Artifacts", list);
+ // TODO propertiesFile = new BndConfiguration(project,
+ // mojoExecution).loadProperties(builder);
+ // TODO
+ // aQute.bnd.maven.lib.resolve.BndrunContainer.getFileSetRepository(MavenProject)
+
+ Workspace standaloneWorkspace = Workspace.createStandaloneWorkspace(processor,
+ mavenProject.getPomFile().getParentFile().toURI());
+ standaloneWorkspace.addBasicPlugin(repository);
+ if (mp == null) {
+ standaloneWorkspace.set("workspaceName", mavenProject.getArtifactKey().toPortableString());
+ } else {
+ // TODO make part of IMavenProjectFacade
+ standaloneWorkspace.set("workspaceName", mp.getName());
+ String description = mp.getDescription();
+ if (description != null) {
+ standaloneWorkspace.set("workspaceDescription", description);
+ }
+ }
+ standaloneWorkspace.refresh();
+ Project bndProject = new Project(standaloneWorkspace, null, null);
+ bndProject.setBase(null);
+ return adapterType.cast(bndProject);
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private boolean isRelevantProject(IMavenProjectFacade mavenProject) {
+ // TODO cache result inside IProject store
+ if (mavenProject != null) {
+ Map> mapping = mavenProject.getMojoExecutionMapping();
+ for (MojoExecutionKey key : mapping.keySet()) {
+ if ("biz.aQute.bnd".equals(key.groupId()) && "bnd-maven-plugin".equals(key.artifactId())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/lemminx/MyExtension.java b/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/lemminx/MyExtension.java
new file mode 100644
index 0000000000..659eaddd9a
--- /dev/null
+++ b/org.eclipse.m2e.bnd.ui/src/org/eclipse/m2e/bnd/ui/lemminx/MyExtension.java
@@ -0,0 +1,21 @@
+package org.eclipse.m2e.bnd.ui.lemminx;
+
+import org.eclipse.lemminx.services.extensions.IXMLExtension;
+import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
+import org.eclipse.lsp4j.InitializeParams;
+
+public class MyExtension implements IXMLExtension {
+
+ @Override
+ public void start(InitializeParams arg0, XMLExtensionsRegistry arg1) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void stop(XMLExtensionsRegistry arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/org.eclipse.m2e.core/.settings/org.eclipse.pde.ds.annotations.prefs b/org.eclipse.m2e.core/.settings/org.eclipse.pde.ds.annotations.prefs
index 73a356b6d0..19be1e2fb6 100644
--- a/org.eclipse.m2e.core/.settings/org.eclipse.pde.ds.annotations.prefs
+++ b/org.eclipse.m2e.core/.settings/org.eclipse.pde.ds.annotations.prefs
@@ -1,5 +1,5 @@
classpath=true
-dsVersion=V1_3
+dsVersion=V1_5
eclipse.preferences.version=1
enabled=true
generateBundleActivationPolicyLazy=true
diff --git a/org.eclipse.m2e.lemminx.bnd/.classpath b/org.eclipse.m2e.lemminx.bnd/.classpath
new file mode 100644
index 0000000000..5d5961d093
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/.classpath
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.m2e.lemminx.bnd/.project b/org.eclipse.m2e.lemminx.bnd/.project
new file mode 100644
index 0000000000..2e7c35044e
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/.project
@@ -0,0 +1,23 @@
+
+
+ org.eclipse.m2e.lemminx.bnd
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..68472858a2
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.m2e.core.prefs b/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000..f897a7f1cb
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/org.eclipse.m2e.lemminx.bnd/pom.xml b/org.eclipse.m2e.lemminx.bnd/pom.xml
new file mode 100644
index 0000000000..837ad3895b
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/pom.xml
@@ -0,0 +1,47 @@
+
+ 4.0.0
+
+
+
+
+
+ org.eclipse.m2e.lemminx.bnd
+ org.eclipse.m2e.lemminx.bnd
+ 2.1.0-SNAPSHOT
+ M2E bnd lemminx extension
+
+
+ 17
+ ${java.version}
+
+
+
+
+
+ org.eclipse.lemminx
+ org.eclipse.lemminx
+ 0.28.0
+ provided
+
+
+ biz.aQute.bnd
+ biz.aQute.bndlib
+ 7.0.0
+
+
+
+
+
+ lemminx-releases
+ https://repo.eclipse.org/content/repositories/lemminx-releases/
+
+ false
+
+
+ true
+
+
+
+
\ No newline at end of file
diff --git a/org.eclipse.m2e.lemminx.bnd/src/main/java/org/eclipse/m2e/lemminx/bnd/BndLemminxPlugin.java b/org.eclipse.m2e.lemminx.bnd/src/main/java/org/eclipse/m2e/lemminx/bnd/BndLemminxPlugin.java
new file mode 100644
index 0000000000..5aa3aa0cb4
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/src/main/java/org/eclipse/m2e/lemminx/bnd/BndLemminxPlugin.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.m2e.lemminx.bnd;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.lemminx.dom.DOMDocument;
+import org.eclipse.lemminx.dom.DOMNode;
+import org.eclipse.lemminx.services.extensions.IXMLExtension;
+import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionParticipant;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse;
+import org.eclipse.lemminx.services.extensions.save.ISaveContext;
+import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.InitializeParams;
+import org.eclipse.lsp4j.jsonrpc.CancelChecker;
+
+import aQute.bnd.help.Syntax;
+
+public class BndLemminxPlugin implements IXMLExtension {
+
+ // TODO LemminxClasspathExtensionProvider that puts our jar on the classpath +
+ // bnd dependencies!
+
+ @Override
+ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
+ Logger logger = Logger.getLogger("bnd");
+ logger.log(Level.INFO, "Hello From BND Extension");
+ registry.registerCompletionParticipant(new ICompletionParticipant() {
+
+ @Override
+ public void onAttributeName(boolean arg0, ICompletionRequest arg1, ICompletionResponse arg2,
+ CancelChecker arg3) throws Exception {
+ logger.log(Level.INFO, "onAttributeName");
+ }
+
+ @Override
+ public void onAttributeValue(String arg0, ICompletionRequest arg1, ICompletionResponse arg2,
+ CancelChecker arg3) throws Exception {
+ logger.log(Level.INFO, "onAttributeValue");
+ }
+
+ @Override
+ public void onDTDSystemId(String arg0, ICompletionRequest arg1, ICompletionResponse arg2,
+ CancelChecker arg3) throws Exception {
+ logger.log(Level.INFO, "onDTDSystemId");
+ }
+
+ @Override
+ public void onTagOpen(ICompletionRequest arg0, ICompletionResponse arg1, CancelChecker arg2)
+ throws Exception {
+ logger.log(Level.INFO, "onTagOpen");
+ }
+
+ @Override
+ public void onXMLContent(ICompletionRequest completionRequest, ICompletionResponse response,
+ CancelChecker checker) throws Exception {
+
+ logger.log(Level.INFO, "onXMLContent");
+ try {
+ // FIXME CDATA do not trigger completion:
+ // https://github.com/eclipse/lemminx/issues/1694
+ DOMDocument xmlDocument = completionRequest.getXMLDocument();
+ DOMNode node = xmlDocument.findNodeBefore(completionRequest.getOffset());
+ if (isBndNode(node)) {
+ // FIXME get the text to give better completion proposals, see:
+ // https://github.com/eclipse/lemminx/issues/1695
+// if (node != null && node.getNodeName().equals("bnd")) {
+// logger.log(Level.INFO, "text content=" + node.getTextContent());
+// String substring = xmlDocument.getText().substring(node.getStart(), node.getEnd());
+// logger.log(Level.INFO, "substring=" + substring);
+// } else {
+// logger.log(Level.INFO,
+// "node=" + node + ", start=" + node.getStart() + ", end=" + node.getEnd()
+// + " --> text content=" + node.getTextContent());
+//
+// // Syntax.HELP.values().stream().map(syntax -> {
+// }
+ Syntax.HELP.values().stream().forEach(syntax -> {
+ CompletionItem item = new CompletionItem();
+ item.setLabel(syntax.getHeader());
+ item.setInsertText(syntax.getHeader() + ": ");
+ response.addCompletionItem(item);
+ });
+ }
+ } catch (Exception e) {
+ logger.log(Level.INFO, "err=" + e);
+ }
+ }
+
+ private boolean isBndNode(DOMNode node) {
+ if (node != null) {
+ if (node.getNodeName().equals("bnd")) {
+ return true;
+ }
+ return isBndNode(node.getParentNode());
+ }
+ return false;
+ }
+
+ });
+ }
+
+ @Override
+ public void stop(XMLExtensionsRegistry registry) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void doSave(ISaveContext context) {
+ // TODO Auto-generated method stub
+ IXMLExtension.super.doSave(context);
+ }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.m2e.lemminx.bnd/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension b/org.eclipse.m2e.lemminx.bnd/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension
new file mode 100644
index 0000000000..08c100d454
--- /dev/null
+++ b/org.eclipse.m2e.lemminx.bnd/src/main/resources/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension
@@ -0,0 +1 @@
+org.eclipse.m2e.lemminx.bnd.BndLemminxPlugin
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index a4930719f5..3549a3b272 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,8 @@
org.eclipse.m2e.apt.core
org.eclipse.m2e.apt.ui
org.eclipse.m2e.mavenarchiver
+ org.eclipse.m2e.bnd.ui
+ org.eclipse.m2e.lemminx.bnd
org.eclipse.m2e.tests.common
diff --git a/target-platform/target-platform.target b/target-platform/target-platform.target
index 96f134caac..b3b0ff35d5 100644
--- a/target-platform/target-platform.target
+++ b/target-platform/target-platform.target
@@ -103,6 +103,12 @@
3.17.0
jar
+
+ biz.aQute.bnd
+ biz.aQute.bnd.maven
+ 7.0.0
+ jar
+