Skip to content

Commit

Permalink
Merge pull request #6073 from pkriens/feature/propertykey
Browse files Browse the repository at this point in the history
Add support for PropertyKeys, where these records pair the Processor/key
  • Loading branch information
chrisrueger committed Apr 4, 2024
2 parents c9cb862 + 9bb802b commit 6bf3753
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 23 deletions.
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

0 comments on commit 6bf3753

Please sign in to comment.