Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PropertyKeys, where these records pair the Processor/key #6073

Merged
merged 4 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions biz.aQute.bndlib.tests/test/test/ProcessorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.OSInformation;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Processor.PropertyKey;
import aQute.bnd.osgi.resource.RequirementBuilder;
import aQute.bnd.osgi.resource.ResourceBuilder;
import aQute.bnd.osgi.resource.ResourceUtils;
Expand Down Expand Up @@ -623,6 +624,27 @@ public void testMergAndSuffixes() throws IOException {

}

@Test
public void testPropertyKeys() throws IOException {
try (Processor top = new Processor()) {
top.setProperty("foo+.1", "x,y,z");
top.setProperty("foo+.2", "x,y,z");
top.setProperty("foo++", "d,e,f");
try (Processor bottom = new Processor(top)) {
bottom.setProperty("foo+", "a,b,c");
bottom.setProperty("foo+.2", "x,y,z");
List<PropertyKey> keys = bottom.getMergePropertyKeys("foo+");
assertThat(keys).hasSize(4);
assertThat(keys).containsExactly(//
new PropertyKey(bottom, "foo+", 0), //
new PropertyKey(top, "foo+.1", 1), //
new PropertyKey(bottom, "foo+.2", 0), //
new PropertyKey(top, "foo+.2", 1));
}
}

}

@Test
public void testIncludeItself() throws IOException {
File foo = IO.getFile("generated/foo.bnd");
Expand Down
36 changes: 36 additions & 0 deletions biz.aQute.bndlib/src/aQute/bnd/build/Project.java
Original file line number Diff line number Diff line change
Expand Up @@ -3656,4 +3656,40 @@ private List<org.osgi.resource.Resource> parseBuildResources() {
}
return result;
}

/**
* Find a processor that is inheriting from a project. This is either the
* bnd.bnd file or a sub bnd.
*
* @param file the file that contains the properties
* @return a processor properly setup for the Workspace inheritance or empty
*/
public Optional<Processor> findProcessor(File file) {

if (file.equals(getPropertiesFile()))
return Optional.of(this);

File projectDir = file.getParentFile();
if (!projectDir.equals(getBase()))
return Optional.empty();

try {
if (file.getName()
.endsWith(".bndrun"))
return Optional.of(Run.createRun(getWorkspace(), file));

try (ProjectBuilder builder = getBuilder(null)) {
for (Builder b : builder.getSubBuilders()) {
if (file.equals(b.getPropertiesFile())) {
Processor sub = new Processor(this);
sub.setProperties(file);
return Optional.of(sub);
}
}
}
return Optional.empty();
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
}
25 changes: 25 additions & 0 deletions biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
Expand Down Expand Up @@ -123,6 +124,7 @@ public class Workspace extends Processor {
public static final String EXT = "ext";
public static final String BUILDFILE = "build.bnd";
public static final String CNFDIR = "cnf";
public static final String CNF_BUILD_BND = CNFDIR + "/" + BUILDFILE;
public static final String CACHEDIR = "cache/" + About.CURRENT;
public static final String STANDALONE_REPO_CLASS = "aQute.bnd.repository.osgi.OSGiRepository";

Expand Down Expand Up @@ -2009,4 +2011,27 @@ protected Properties magicBnd(File file) throws IOException {
}
}

/**
* Find the Processor that has the give file as properties.
*
* @param file the file that should match the Project or Workspace
* @return an optional Processor
*/
public Optional<Processor> findProcessor(File file) {
File cnf = getFile(CNF_BUILD_BND);
if (cnf.equals(file))
return Optional.of(this);

File projectDir = file.getParentFile();
if (projectDir.isDirectory()) {
File wsDir = projectDir.getParentFile();
if (wsDir.equals(getBase())) {
Project project = getProject(projectDir.getName());
if (project != null) {
return project.findProcessor(file);
}
}
}
return Optional.empty();
}
}
43 changes: 29 additions & 14 deletions biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public class BndEditModel {
private Properties properties = new UTF8Properties();
private final Map<String, Object> objectProperties = new HashMap<>();
private final Map<String, String> changesToSave = new TreeMap<>();
private Project project;
private Project bndrun;

private volatile boolean dirty;

Expand Down Expand Up @@ -382,17 +382,33 @@ public BndEditModel(IDocument document) throws IOException {
loadFrom(document);
}

public BndEditModel(Project project) throws IOException {
this(project.getWorkspace());
this.project = project;
File propertiesFile = project.getPropertiesFile();
public BndEditModel(Project bndrun) throws IOException {
this(bndrun.getWorkspace());
this.bndrun = bndrun;
File propertiesFile = bndrun.getPropertiesFile();
if (propertiesFile.isFile())
this.document = new Document(IO.collect(propertiesFile));
else
this.document = new Document("");
loadFrom(this.document);
}

/**
* Is either the workspace (when cnf/build.bnd) or a project (when its
* bnd.bnd) or a random bndrun linked to workspace (event if it isn't a
* bndrun). Primary purpose is to walk the inheritance chain implied in the
* Workspace/Project/sub bnd files files
*/
Processor getOwner() {
File propertiesFile = bndrun.getPropertiesFile();
if (!propertiesFile.getName()
.endsWith(".bnd"))
return bndrun;

return workspace.findProcessor(propertiesFile)
.orElse(bndrun);
}

public void loadFrom(IDocument document) throws IOException {
try (InputStream in = toEscaped(document.get())) {
loadFrom(in);
Expand Down Expand Up @@ -1237,11 +1253,11 @@ private boolean hasIncludeResourceHeaderLikeInstruction() {
}

public void setProject(Project project) {
this.project = project;
this.bndrun = project;
}

public Project getProject() {
return project;
return bndrun;
}

public Workspace getWorkspace() {
Expand Down Expand Up @@ -1274,10 +1290,10 @@ public Processor getProperties() throws Exception {
File source = getBndResource();
Processor parent;

if (project != null) {
parent = project;
if (bndrun != null) {
parent = bndrun;
if (source == null) {
source = project.getPropertiesFile();
source = bndrun.getPropertiesFile();
}
} else if (workspace != null && isCnf()) {
parent = workspace;
Expand Down Expand Up @@ -1350,7 +1366,7 @@ public Map<String, String> getDocumentChanges() {
*/
public void saveChanges() throws IOException {
assert document != null
&& project != null : "you can only call saveChanges when you created this edit model with a project";
&& bndrun != null : "you can only call saveChanges when you created this edit model with a project";

saveChangesTo(document);
store(document, getProject().getPropertiesFile());
Expand All @@ -1367,7 +1383,7 @@ public ResolutionInstructions.ResolveMode getResolveMode() {
try {
return aQute.lib.converter.Converter.cnv(ResolutionInstructions.ResolveMode.class, resolve);
} catch (Exception e) {
project.error("Invalid value for %s: %s. Allowed values are %s", Constants.RESOLVE, resolve,
bndrun.error("Invalid value for %s: %s. Allowed values are %s", Constants.RESOLVE, resolve,
ResolutionInstructions.ResolveMode.class.getEnumConstants());
}
}
Expand All @@ -1391,7 +1407,7 @@ public void setDirty(boolean isDirty) {
}

public void load() throws IOException {
loadFrom(project.getPropertiesFile());
loadFrom(bndrun.getPropertiesFile());
}

/**
Expand Down Expand Up @@ -1456,5 +1472,4 @@ public <T extends Collection<Object>> String add(String header, String toAdd) {
public long getLastChangedAt() {
return lastChangedAt;
}

}
94 changes: 85 additions & 9 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,89 @@ private String getProperty(String key, String deflt, String separator, boolean i
return getWildcardProperty(deflt, separator, inherit, ins);
}

/**
* A Property Key is the pair of a Processor and a key it defines. It also
* defines if this is the firsts definition viewed from this Processor. The
* floor indicates where the property is defined relative to its parents.
* Zero is in the current processor, 1, is its parents, and so on.
*/
public record PropertyKey(Processor processor, String key, int floor)
implements Comparable<PropertyKey> {

/**
* Check if this PropertyKey belongs to the given processor
*
* @param p the processor to check
* @return true if our processor is the same as p
*/
public boolean isLocalTo(Processor p) {
return processor == p;
}

/**
* Get the value of the property key
*
* @return a processed value
*/
public String getValue() {
return processor.getProperty(key);
}

/**
* Get the raw value of the property key
*
* @return a raw value
*/
public String getRawValue() {
return processor.getProperties()
.getProperty(key);
}

@Override
public int compareTo(PropertyKey o) {
int n = key.compareTo(o.key);
if ( n != 0)
return n;
return Integer.compare(floor, o.floor);
}
}

/**
* Return a list of sorted PropertyKey that match the predicate and includes
* the inheritance chain. The intention is to capture the processor that
* defines a key.
*
* @param predicate the predicate to filter the key
* @return new modifiable sorted list of PropertyKey
*/
@SuppressWarnings("resource")
public List<PropertyKey> getPropertyKeys(Predicate<String> predicate) {
List<PropertyKey> keys = new ArrayList<>();
Processor rover = this;
int level = 0;
while( rover != null) {
Processor localRover = rover;
int localLevel = level;
rover.stream(false) // local only
.filter(predicate)
.map(k -> new PropertyKey(localRover, k, localLevel))
.forEach(keys::add);
rover = rover.getParent();
level++;
}
Collections.sort(keys);
return keys;

}

/**
* Return the merge property keys
*/
public List<PropertyKey> getMergePropertyKeys(String stem) {
String prefix = stem + ".";
return getPropertyKeys(k -> k.equals(stem) || k.startsWith(prefix));
}

private String getWildcardProperty(String deflt, String separator, boolean inherit, Instruction ins) {
// Handle a wildcard key, make sure they're sorted
// for consistency
Expand Down Expand Up @@ -2144,18 +2227,11 @@ public String mergeProperties(String key) {
}

public String mergeLocalProperties(String key) {
if (since(About._3_3)) {
return getProperty(makeWildcard(key), null, ",", false);
} else
return mergeProperties(key);
return getProperty(makeWildcard(key), null, ",", false);
}

public String mergeProperties(String key, String separator) {
if (since(About._2_4))
return getProperty(makeWildcard(key), null, separator, true);
else
return getProperty(key);

return getProperty(makeWildcard(key), null, separator, true);
}

private String makeWildcard(String key) {
Expand Down
Loading