Skip to content

Commit

Permalink
Merge pull request #6069 from chrisrueger/plugin-list-should-detect-p…
Browse files Browse the repository at this point in the history
…lugins-by-prefix

BndEditor build.bnd: Plugin list should detect plugins by prefix
  • Loading branch information
pkriens authored Apr 9, 2024
2 parents f1cb90b + 0cafeea commit 5d721aa
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 42 deletions.
36 changes: 36 additions & 0 deletions biz.aQute.bndlib.tests/test/test/ProcessorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

Expand Down Expand Up @@ -645,6 +646,41 @@ public void testPropertyKeys() throws IOException {

}

@Test
public void testPropertyKeysFindVisibles() throws IOException {
try (Processor top = new Processor()) {
top.setProperty("-plugin.a", "a1");
top.setProperty("-plugin.b", "b1");
try (Processor bottom = new Processor(top)) {
bottom.setProperty("-plugin.a", "a,b,c");
List<PropertyKey> keys = bottom.getMergePropertyKeys("-plugin");
assertThat(keys).hasSize(3);

assertThat(keys).containsExactly(//
new PropertyKey(bottom, "-plugin.a", 0), //
new PropertyKey(top, "-plugin.a", 1), //
new PropertyKey(top, "-plugin.b", 1));

Collection<PropertyKey> visibles = PropertyKey.findVisible(keys);
System.out.println(keys);
System.out.println(visibles);

assertThat(visibles).hasSize(2);
assertThat(visibles).containsExactly(//
new PropertyKey(bottom, "-plugin.a", 0), //
new PropertyKey(top, "-plugin.b", 1));

assertThat(List.copyOf(visibles)
.get(0)
.getValue()).isEqualTo("a,b,c");
assertThat(List.copyOf(visibles)
.get(1)
.getValue()).isEqualTo("b1");
}
}

}

@Test
public void testIncludeItself() throws IOException {
File foo = IO.getFile("generated/foo.bnd");
Expand Down
134 changes: 133 additions & 1 deletion biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

Expand Down Expand Up @@ -57,6 +59,7 @@
import aQute.bnd.osgi.BundleId;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Processor.PropertyKey;
import aQute.bnd.properties.Document;
import aQute.bnd.properties.IDocument;
import aQute.bnd.properties.IRegion;
Expand Down Expand Up @@ -986,14 +989,143 @@ public void setTestSuites(List<String> suites) {
}

public List<HeaderClause> getPlugins() {
return doGetObject(Constants.PLUGIN, headerClauseListConverter);
// return all plugins
// we do prefix matching to support merged properties like
// -plugin.1.Test, -plugin.2.Maven etc.
try {
List<PropertyKey> propertyKeys = getOwner().getMergePropertyKeys(Constants.PLUGIN);

List<HeaderClause> headers = PropertyKey.findVisible(propertyKeys)
.stream()
.map(p -> headerClauseListConverter.convert(p.getValue()))
.flatMap(List::stream)
.toList();

return headers;

} catch (Exception e) {
throw Exceptions.duck(e);
}

}

public void setPlugins(List<HeaderClause> plugins) {
List<HeaderClause> old = getPlugins();
doSetObject(Constants.PLUGIN, old, plugins, complexHeaderClauseListFormatter);
}



/**
* Similar to {@link #getPlugins()} but returns a map where the key is the
* property key of the bnd file e.g.
* <code>-plugin.1.Test, -plugin.2.Maven </code> The value is a List of
* plugins, although usually it is just a 1-element list. But it is also
* possible to specify multiple plugins under a single key, thus it is a
* list.
*
* @return a map with a property keys and their plugins.
*/
public Map<String, List<BndEditModelHeaderClause>> getPluginsProperties() {
return getProperties(Constants.PLUGIN);
}

private Map<String, List<BndEditModelHeaderClause>> getProperties(String stem) {
// return all properties
// we do step prefix-matching to support merged properties. e.g.
// stem=-plugin hould return
// -plugin.1.Test, -plugin.2.Maven etc.

try {

Map<String, List<BndEditModelHeaderClause>> map = new LinkedHashMap<>();

PropertyKey.findVisible(getOwner().getMergePropertyKeys(stem))
.stream()
.forEach(pk -> {

boolean isLocal = isLocalPropertyKey(pk.key());

List<BndEditModelHeaderClause> headers = headerClauseListConverter.convert(pk.getValue())
.stream()
.map(h -> new BndEditModelHeaderClause(pk.key(), h, isLocal))
.collect(Collectors.toList());
map.put(pk.key(), headers);

});

return map;

} catch (Exception e) {
throw Exceptions.duck(e);
}

}

/**
* Updates and removes plugins.
*
* @param plugins
* @param pluginPropKeysToRemove the property keys to remove (not modified,
* caller needs to handle cleanup)
*/
public void setPlugins(Map<String, List<BndEditModelHeaderClause>> plugins,
Collection<String> pluginPropKeysToRemove) {
setProperties(Constants.PLUGIN, plugins, pluginPropKeysToRemove);
}

private void setProperties(String stem, Map<String, List<BndEditModelHeaderClause>> map,
Collection<String> propKeysToRemove) {
Map<String, List<BndEditModelHeaderClause>> old = getProperties(stem);

map.entrySet()
.stream()
// safety check: filter out properties not starting with the step
.filter(entry -> entry.getKey()
.startsWith(stem))
.forEach(p -> {

List<BndEditModelHeaderClause> newLocalHeaders = p.getValue()
.stream()
.filter(mh -> mh.isLocal())
.toList();

List<BndEditModelHeaderClause> oldList = old.get(p.getKey());

List<BndEditModelHeaderClause> oldLocalHeaders = oldList == null ? null
: oldList
.stream()
.filter(mh -> mh.isLocal())
.toList();

if (oldList != null && !isLocalPropertyKey(p.getKey())) {
// skip writing
// existing but non-local means an inherited property
// not from this properties file. so do not write it.
return;
}

doSetObject(p.getKey(), oldLocalHeaders, newLocalHeaders,
complexHeaderClauseListFormatter);

});

if (propKeysToRemove != null) {
propKeysToRemove.forEach(key -> removeEntries(key));
}
}



/**
* @param key
* @return <code>true</code> if the given propertyKey is physically in the
* local {@link #properties}
*/
private boolean isLocalPropertyKey(String key) {
return doGetObject(key, headerClauseListConverter) != null;
}

public List<String> getPluginPath() {
return doGetObject(Constants.PLUGINPATH, listConverter);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package aQute.bnd.build.model;

import aQute.bnd.build.model.clauses.HeaderClause;

/**
* This is a special {@link HeaderClause} which knows from which property-key it
* came from. Also it has knowledge about if it <i>isLocal</i>. local means that
* it is in the current file you are looking it (vs. inherited via
* mergedProperties)
*/
public class BndEditModelHeaderClause extends HeaderClause {

private String propertyKey;
private boolean isLocal;

public BndEditModelHeaderClause(String propertyKey, HeaderClause headerClause, boolean isLocal) {
super(headerClause.getName(), headerClause.getAttribs());
this.propertyKey = propertyKey;
this.isLocal = isLocal;
}

public String key() {
return propertyKey;
}

public boolean isLocal() {
return isLocal;
}


public String displayTitle() {
return attribs.get("name", getName());
}
}
17 changes: 17 additions & 0 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -1051,6 +1052,22 @@ public int compareTo(PropertyKey o) {
return n;
return Integer.compare(floor, o.floor);
}

/**
* Find visible property keys. "Visible" in this context means that
* among the {@code PropertyKey} objects with the same key, only the one
* with the lowest floor number is included in the result.
*
* @param keys sorted keys defined by {@link #compareTo(PropertyKey)}
* @return only unique keys which are visible (lowest floor value)
*/
public static Collection<PropertyKey> findVisible(Collection<PropertyKey> keys) {
Map<String, PropertyKey> map = new LinkedHashMap<>();
for (PropertyKey pk : keys) {
map.putIfAbsent(pk.key(), pk);
}
return new LinkedHashSet<>(map.values());
}
}

/**
Expand Down
58 changes: 54 additions & 4 deletions bndtools.core/_plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -834,8 +834,9 @@

<!-- BND PLUGINS -->
<extension point="bndtools.core.bndPlugins">
<plugin class="aQute.lib.deployer.FileRepo" rank="10"
icon="icons/bundlefolder.png" name="File Repository">
<plugin class="aQute.lib.deployer.FileRepo" rank="9"
icon="icons/bundlefolder.png" name="File Repository"
helpUrl="https://bnd.bndtools.org/plugins/filerepo.html">
<property name="name" type="string"
description="Name of the repository" />
<property name="location" type="string"
Expand All @@ -844,7 +845,8 @@
</plugin>

<plugin class="aQute.bnd.repository.osgi.OSGiRepository"
icon="icons/database.png" name="Indexed Repository (read-only)">
icon="icons/database.png" name="Indexed Repository (read-only)"
helpUrl="https://bnd.bndtools.org/plugins/osgirepo.html">
<property name="name" type="string"
description="Name of the repository" />
<property name="locations" type="string"
Expand All @@ -855,7 +857,8 @@

<plugin class="aQute.bnd.deployer.repository.LocalIndexedRepo"
icon="icons/database.png"
name="Locally Indexed Repository (read/write)">
name="Locally Indexed Repository (read/write)"
helpUrl="https://bnd.bndtools.org/plugins/localindexrepo.html">
<property name="name" type="string"
description="Name of the repository" />
<property name="local" type="string"
Expand Down Expand Up @@ -890,6 +893,53 @@
description="Disable verification of server's X509 certificate (insecure, for testing only!)" />
</plugin>

<plugin class="aQute.bnd.repository.maven.provider.MavenBndRepository"
icon="icons/database.png"
name="Maven Bnd Repository"
helpUrl="https://bnd.bndtools.org/plugins/maven.html"
rank="10">
<property name="name" type="string" description="The name of this repository" default="Maven Repository" />
<property name="releaseUrl" type="string" description="The urls to the remote release repository." default="https://repo.maven.apache.org/maven2/" />
<property name="stagingUrl" type="string" description="The url of the staging release repository." />
<property name="snapshotUrl" type="string" description="The urls to the remote snapshot repository." default="https://repository.apache.org/snapshots/" />
<property name="local" type="string" description="The path to the local repository" default="~/.m2/repository" />
<property name="readOnly" type="boolean" description="" default="false" />
<property name="index" type="string" description="The path to the index file" default="${.}/cnf/central.maven" />
<property name="source" type="string" description="Content added to the index file. Content maybe one line without CR/LF as long as there is a comma or whitespace separating the GAVs. Further same format as the index file." />
<property name="noupdateOnRelease" type="boolean" description="Do not update the index when a file is released" />
<property name="poll_time" type="int" description="Sets the time in seconds when to check for changes in the pom-files" default="5" />
<property name="redeploy" type="boolean" description="Allow redeploy" />
<property name="ignore_metainf_maven" type="boolean" description="Ignore maven information in META-INF/maven/...." />
<property name="multi" type="string" description="Extensions for files that contain multiple JARs" />
</plugin>

<plugin class="aQute.bnd.repository.p2.provider.P2Repository"
icon="icons/database.png"
name="P2Repository"
helpUrl="https://bnd.bndtools.org/plugins/p2repo.html">
<property name="name" type="string" description="The name of this repository" default="P2Repository" />
<property name="url" type="string" description="The URL to either the P2 repository (a directory) or an Eclipse target platform. Required." />
<property name="location" type="string" description="The location to store the index file. Default: 'cnf/cache/p2-index-reponame/index.xml.gz'" />
</plugin>

<plugin class="aQute.bnd.repository.maven.pom.provider.BndPomRepository"
icon="icons/database.png"
name="Maven POM Repository"
helpUrl="https://bnd.bndtools.org/plugins/p2repo.html">
<property name="name" type="string" description="The name of the repo. Required." default="Maven POM Repository" />
<property name="releaseUrl" type="string" description="The url to the remote release repository. Can be a comma separated list of urls." default="https://repo.maven.apache.org/maven2/" />
<property name="snapshotUrl" type="string" description="The url to the remote snapshot repository. If this is not specified, it falls back to the release repository or just local if this is also not specified. Can be a comma separated list of urls." default="https://repository.apache.org/snapshots/" />
<property name="local" type="string" description="The path to the local repository" default="~/.m2/repository" />
<property name="revision" type="string" description="Coordinates of a maven revision. I.e. group:artifactid[:c]:version. Can be a comma separated list of GAVs." />
<property name="location" type="string" description="Points to a file that is used as the cache. It will be in OSGi format." />
<property name="pom" type="string" description="Points to a pom.xml file. This is exclusive with revision. Can be a comma separated list of files." default="${.}/cnf/pom.xml" />
<property name="query" type="string" description="The query used to search Maven Central Search." />
<property name="queryUrl" type="string" description="The url of the Maven Central Search." />
<property name="transitive" type="boolean" description="Allow transitive dependencies" />
<property name="poll_time" type="int" description="Sets the time in seconds when to check for changes in the pom-files" default="300" />
<property name="dependencyManagement" type="boolean" description="Also considers the dependency management section of a POM. Default is false." default="false" />
</plugin>

</extension>

<extension point="org.eclipse.ui.intro.configExtension">
Expand Down
2 changes: 2 additions & 0 deletions bndtools.core/src/bndtools/editor/common/HelpButtons.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ public final class HelpButtons {

public static final String HELP_URL_RESOLUTIONRESULTSWIZARDPAGE = "https://bnd.bndtools.org/chapters/250-resolving.html#resolving-1";

public static final String HELP_URL_PLUGINPROPERTIESPAGE = "https://bnd.bndtools.org/chapters/870-plugins.html";

private HelpButtons() {}
}
Loading

0 comments on commit 5d721aa

Please sign in to comment.