From a0697bc3626360fbad9ce271aeb9458b198d26c8 Mon Sep 17 00:00:00 2001 From: Peter Kriens Date: Thu, 4 Apr 2024 15:17:40 +0200 Subject: [PATCH 1/4] Add support for PropertyKeys, where these records pair the Processor/key --- Signed-off-by: Peter Kriens Signed-off-by: Peter Kriens --- .../test/test/ProcessorTest.java | 22 +++++ .../src/aQute/bnd/build/Project.java | 36 +++++++ .../src/aQute/bnd/build/Workspace.java | 24 +++++ .../aQute/bnd/build/model/BndEditModel.java | 21 ++++- .../src/aQute/bnd/osgi/Processor.java | 94 +++++++++++++++++-- 5 files changed, 183 insertions(+), 14 deletions(-) diff --git a/biz.aQute.bndlib.tests/test/test/ProcessorTest.java b/biz.aQute.bndlib.tests/test/test/ProcessorTest.java index c2abbeb457..2d86beb083 100644 --- a/biz.aQute.bndlib.tests/test/test/ProcessorTest.java +++ b/biz.aQute.bndlib.tests/test/test/ProcessorTest.java @@ -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; @@ -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 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"); diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/Project.java b/biz.aQute.bndlib/src/aQute/bnd/build/Project.java index fb147c90e7..39c68a90ae 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/Project.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/Project.java @@ -3656,4 +3656,40 @@ private List 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 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); + } + } } diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java b/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java index c26a138ea9..e98d5d1029 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java @@ -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; @@ -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"; @@ -2009,4 +2011,26 @@ 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 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(); + } } diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java index e1a260c920..9085c3f85d 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java @@ -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"; @@ -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 resolveModeFormatter = // EnumFormatter.create(ResolveMode.class, ResolveMode.manual); @@ -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 @@ -1456,5 +1468,4 @@ public > String add(String header, String toAdd) { public long getLastChangedAt() { return lastChangedAt; } - } diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java index e36ee75b1b..b541c2f9dc 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java @@ -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 { + + /** + * 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 getPropertyKeys(Predicate predicate) { + List 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 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 @@ -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) { From 83e192d07ae6f14d8f483c5a315b10f3ba0851c6 Mon Sep 17 00:00:00 2001 From: Peter Kriens Date: Thu, 4 Apr 2024 16:23:07 +0200 Subject: [PATCH 2/4] Hmm, recursion. bndrun created a bndeditmodel --- Signed-off-by: Peter Kriens Signed-off-by: Peter Kriens --- .../aQute/bnd/build/model/BndEditModel.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java index 9085c3f85d..19cd872717 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java @@ -77,8 +77,6 @@ @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"; @@ -273,13 +271,6 @@ 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 resolveModeFormatter = // EnumFormatter.create(ResolveMode.class, ResolveMode.manual); @@ -395,9 +386,6 @@ 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 @@ -405,6 +393,22 @@ public BndEditModel(Project bndrun) throws IOException { 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 = project.getPropertiesFile(); + if (!propertiesFile.getName() + .endsWith(".bnd")) + return project; + + return workspace.findProcessor(propertiesFile) + .orElse(project); + } + public void loadFrom(IDocument document) throws IOException { try (InputStream in = toEscaped(document.get())) { loadFrom(in); From 1ac2cd781e216e443c33adade9e8640bf9daa0f9 Mon Sep 17 00:00:00 2001 From: Peter Kriens Date: Thu, 4 Apr 2024 17:42:32 +0200 Subject: [PATCH 3/4] Renamed project to bndrun because that is what it is --- Signed-off-by: Peter Kriens Signed-off-by: Peter Kriens --- .../aQute/bnd/build/model/BndEditModel.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java index 19cd872717..8983b56a1d 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/model/BndEditModel.java @@ -112,7 +112,7 @@ public class BndEditModel { private Properties properties = new UTF8Properties(); private final Map objectProperties = new HashMap<>(); private final Map changesToSave = new TreeMap<>(); - private Project project; + private Project bndrun; private volatile boolean dirty; @@ -384,7 +384,7 @@ public BndEditModel(IDocument document) throws IOException { public BndEditModel(Project bndrun) throws IOException { this(bndrun.getWorkspace()); - this.project = bndrun; + this.bndrun = bndrun; File propertiesFile = bndrun.getPropertiesFile(); if (propertiesFile.isFile()) this.document = new Document(IO.collect(propertiesFile)); @@ -400,13 +400,13 @@ public BndEditModel(Project bndrun) throws IOException { * Workspace/Project/sub bnd files files */ Processor getOwner() { - File propertiesFile = project.getPropertiesFile(); + File propertiesFile = bndrun.getPropertiesFile(); if (!propertiesFile.getName() .endsWith(".bnd")) - return project; + return bndrun; return workspace.findProcessor(propertiesFile) - .orElse(project); + .orElse(bndrun); } public void loadFrom(IDocument document) throws IOException { @@ -1253,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() { @@ -1290,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; @@ -1366,7 +1366,7 @@ public Map 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()); @@ -1383,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()); } } @@ -1407,7 +1407,7 @@ public void setDirty(boolean isDirty) { } public void load() throws IOException { - loadFrom(project.getPropertiesFile()); + loadFrom(bndrun.getPropertiesFile()); } /** From 9bb802bf006d40e5b48ea09cea360ad1b435e6f8 Mon Sep 17 00:00:00 2001 From: Peter Kriens Date: Thu, 4 Apr 2024 18:00:16 +0200 Subject: [PATCH 4/4] ouch, conflict editing failed --- Signed-off-by: Peter Kriens Signed-off-by: Peter Kriens --- biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java | 1 + 1 file changed, 1 insertion(+) diff --git a/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java b/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java index e98d5d1029..63820ffe13 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java +++ b/biz.aQute.bndlib/src/aQute/bnd/build/Workspace.java @@ -2011,6 +2011,7 @@ 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