Skip to content

Commit

Permalink
Add support for PropertyKeys, where these records pair the Processor/key
Browse files Browse the repository at this point in the history
---
 Signed-off-by: Peter Kriens <[email protected]>

Signed-off-by: Peter Kriens <[email protected]>
  • Loading branch information
pkriens committed Apr 4, 2024
1 parent 883914f commit 27487b2
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 37 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);
}
}
}
71 changes: 49 additions & 22 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 @@ -168,7 +170,7 @@ public void close() {
}
}

private final static Map<File, WeakReference<Workspace>> cache = newHashMap();
private final static Map<File, WeakReference<Workspace>> cache = newHashMap();
private static final Memoize<Processor> defaults;
static {
defaults = Memoize.supplier(() -> {
Expand All @@ -185,31 +187,31 @@ public void close() {
return new Processor(props, false);
});
}
final Map<String, Action> commands = newMap();
final Maven maven;
private final AtomicBoolean offline = new AtomicBoolean();
Settings settings = new Settings(
final Map<String, Action> commands = newMap();
final Maven maven;
private final AtomicBoolean offline = new AtomicBoolean();
Settings settings = new Settings(
Home.getUserHomeBnd("settings.json"));
WorkspaceRepository workspaceRepo = new WorkspaceRepository(this);
static String overallDriver = "unset";
static Parameters overallGestalt = new Parameters();
WorkspaceRepository workspaceRepo = new WorkspaceRepository(this);
static String overallDriver = "unset";
static Parameters overallGestalt = new Parameters();
/**
* Signal a BndListener plugin. We ran an infinite bug loop :-(
*/
final ThreadLocal<Reporter> signalBusy = new ThreadLocal<>();
ResourceRepositoryImpl resourceRepositoryImpl;
private String driver;
private final WorkspaceLayout layout;
final Set<Project> trail = Collections
final ThreadLocal<Reporter> signalBusy = new ThreadLocal<>();
ResourceRepositoryImpl resourceRepositoryImpl;
private String driver;
private final WorkspaceLayout layout;
final Set<Project> trail = Collections
.newSetFromMap(new ConcurrentHashMap<Project, Boolean>());
private volatile WorkspaceData data = new WorkspaceData();
private File buildDir;
private final ProjectTracker projects = new ProjectTracker(this);
private volatile WorkspaceData data = new WorkspaceData();
private File buildDir;
private final ProjectTracker projects = new ProjectTracker(this);
private final WorkspaceLock workspaceLock = new WorkspaceLock(true);
private static final long WORKSPACE_LOCK_DEFAULT_TIMEOUTMS = 120_000L;
final WorkspaceNotifier notifier = new WorkspaceNotifier(this);
final WorkspaceNotifier notifier = new WorkspaceNotifier(this);

public static boolean remoteWorkspaces = false;
public static boolean remoteWorkspaces = false;

/**
* This static method finds the workspace and creates a project (or returns
Expand Down Expand Up @@ -1533,6 +1535,7 @@ public <T> void writeLocked(Runnable runnable) throws Exception {
return null;
});
}

public <T> T writeLocked(Callable<T> callable) throws Exception {
return workspaceLock.locked(workspaceLock.writeLock(), WORKSPACE_LOCK_DEFAULT_TIMEOUTMS, callable, () -> false);
}
Expand All @@ -1555,13 +1558,13 @@ public <T> T writeLocked(Callable<T> callable) throws Exception {
* obtain the lock.
* @throws Exception If the callable or function throws an exception.
*/
public <T, U> T writeLocked(Callable<U> underWrite, FunctionWithException<U, T> underRead,
BooleanSupplier canceled, long timeoutInMs) throws Exception {
public <T, U> T writeLocked(Callable<U> underWrite, FunctionWithException<U, T> underRead, BooleanSupplier canceled,
long timeoutInMs) throws Exception {
return workspaceLock.writeReadLocked(timeoutInMs, underWrite, underRead, canceled);
}

public <T, U> T writeLocked(Callable<U> underWrite, FunctionWithException<U, T> underRead,
BooleanSupplier canceled) throws Exception {
public <T, U> T writeLocked(Callable<U> underWrite, FunctionWithException<U, T> underRead, BooleanSupplier canceled)
throws Exception {
return workspaceLock.writeReadLocked(WORKSPACE_LOCK_DEFAULT_TIMEOUTMS, underWrite, underRead, canceled);
}

Expand Down Expand Up @@ -1977,4 +1980,28 @@ public Result<File> getExpandedInCache(String urn, File file) throws IOException
return Result.err("Failed to expand %s into %s: %s", file, cache, e);
}
}

/**
* 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();
}
}
21 changes: 16 additions & 5 deletions biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
@SuppressWarnings("deprecation")
public class BndEditModel {

private static final String BUILDFILE = Workspace.CNFDIR
+ "/" + Workspace.BUILDFILE;
public static final String NEWLINE_LINE_SEPARATOR = "\\n\\\n\t";
public static final String LIST_SEPARATOR = ",\\\n\t";

Expand Down Expand Up @@ -271,6 +273,13 @@ public ImportPattern error(

// for change detection when multiple wizards look at the same model
private long lastChangedAt;
/**
* 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/bndrun files
*/
Processor owner;

// Converter<String, ResolveMode> resolveModeFormatter =
// EnumFormatter.create(ResolveMode.class, ResolveMode.manual);
Expand Down Expand Up @@ -382,10 +391,13 @@ 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.project = bndrun;
File propertiesFile = bndrun.getPropertiesFile();
this.owner = workspace.findProcessor(propertiesFile)
.orElse(bndrun);

if (propertiesFile.isFile())
this.document = new Document(IO.collect(propertiesFile));
else
Expand Down Expand Up @@ -1456,5 +1468,4 @@ public <T extends Collection<Object>> String add(String header, String toAdd) {
public long getLastChangedAt() {
return lastChangedAt;
}

}
97 changes: 87 additions & 10 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,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 @@ -2061,7 +2144,8 @@ public static int getLine(String s, int index) {
* not set, we assume the latest version.
*/

Version upto = null;
Version upto = null;

public boolean since(Version introduced) {
if (upto == null) {
String uptov = getProperty(UPTO);
Expand Down Expand Up @@ -2115,18 +2199,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 27487b2

Please sign in to comment.