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

first draft of Effective tab for BndEditor #6406

Merged
merged 14 commits into from
Jan 14, 2025
30 changes: 15 additions & 15 deletions aQute.libg/src/aQute/lib/utf8properties/PropertiesParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.Collection;
import java.util.Collections;
import java.util.Properties;

import aQute.lib.hex.Hex;
import aQute.lib.strings.Strings;
Expand Down Expand Up @@ -40,18 +39,20 @@ final class PropertiesParser {
INFO['\\'] = NOKEY;
}

private int n = 0;
private int line = 0;
private int pos = -1;
private int marker = 0;
private char current;
private Properties properties;
private boolean validKey;
private boolean continuation = true;
private int n = 0;
private int line = 0;
private int pos = -1;
private int marker = 0;
private char current;
private UTF8Properties properties;
private boolean validKey;
private boolean continuation = true;
private final Collection<String> syntaxHeaders;
private final String provenance;

PropertiesParser(String source, String file, Reporter reporter, Properties properties,
Collection<String> syntaxHeaders) {
PropertiesParser(String source, String file, Reporter reporter, UTF8Properties properties,
Collection<String> syntaxHeaders, String provenance) {
this.provenance = provenance;
this.source = source.toCharArray();
this.file = file;
this.reporter = reporter;
Expand Down Expand Up @@ -154,19 +155,18 @@ void parse() {
next();
skipWhitespace();
if (current == '\n') {
properties.put(key, "");
properties.setProperty(key, "", provenance);
continue;
}
}

if (current != '\n') {

String value = token(LINE, isSyntaxHeader(key));
properties.put(key, value);

properties.setProperty(key, value, provenance);
} else {
error("No value specified for key: %s. An empty value should be specified as '%<s:' or '%<s='", key);
properties.put(key, "");
properties.setProperty(key, "", provenance);
continue;
}
assert current == '\n';
Expand Down
100 changes: 97 additions & 3 deletions aQute.libg/src/aQute/lib/utf8properties/UTF8Properties.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -46,6 +49,10 @@ public class UTF8Properties extends Properties {
private static final List<ThreadLocal<CharsetDecoder>> decoders = Collections.unmodifiableList(
Arrays.asList(ThreadLocal.withInitial(UTF_8::newDecoder), ThreadLocal.withInitial(ISO_8859_1::newDecoder)));

record Provenance(String source) {}

private final Map<String, Provenance> provenance = new HashMap<>();

public UTF8Properties(Properties p) {
super(p);
}
Expand All @@ -66,7 +73,8 @@ public UTF8Properties(File file, Reporter reporter) throws Exception {
load(file, reporter, (Collection<String>) null);
}

public UTF8Properties() {}
public UTF8Properties() {
}

private static Collection<String> fromArray(String[] array) {
return (array != null) ? Arrays.asList(array) : null;
Expand Down Expand Up @@ -95,8 +103,13 @@ public void load(String source, File file, Reporter reporter, String[] syntaxHea
}

public void load(String source, File file, Reporter reporter, Collection<String> syntaxHeaders) throws IOException {
load(source, file, reporter, syntaxHeaders, file == null ? "" : file.getAbsolutePath());
}

public void load(String source, File file, Reporter reporter, Collection<String> syntaxHeaders, String provenance)
throws IOException {
PropertiesParser parser = new PropertiesParser(source, file == null ? null : file.getAbsolutePath(), reporter,
this, syntaxHeaders);
this, syntaxHeaders, provenance);
parser.parse();
}

Expand Down Expand Up @@ -201,7 +214,7 @@ private UTF8Properties replaceAll(Pattern regex, String replacement) {
String value = (String) entry.getValue();
value = regex.matcher(value)
.replaceAll(replacement);
result.put(key, value);
result.setProperty(key, value, getProvenance(key).orElse(null));
}
return result;
}
Expand All @@ -221,4 +234,85 @@ public UTF8Properties replaceHere(File file) {
}
return replaceAll(HERE_PATTERN, Matcher.quoteReplacement(here));
}

public synchronized Object setProperty(String key, String value, String provenance) {
if (provenance != null)
getProvenance().put(key, new Provenance(provenance));
return super.setProperty(key, value);
}

@Override
public synchronized Object remove(Object key) {
getProvenance().remove(key);
return super.remove(key);
}

/**
* Get the provenance of the given key if set
*
* @param key the key
*/

public Optional<String> getProvenance(String key) {
Provenance provenance = getProvenance().get(key);
return Optional.ofNullable(provenance)
.map(Provenance::source);
}

/**
* Set the provenance of the given key
*
* @param key the key
* @param provenance the provenance, maybe null to remove
*/

public UTF8Properties setProvenance(String key, String provenance) {
if (provenance == null)
getProvenance().remove(key);
else
getProvenance().put(key, new Provenance(provenance));
return this;
}

/**
* Load the properties from a properties. If the properties is a
* UTF8Properties, we also copy the provenance.
*
* @param properties the properties
* @param overwriteIfPresent overwrite an exissting value in this properties
*/
public void load(Properties properties, boolean overwriteIfPresent) {
BiConsumer<String, String> set = properties instanceof UTF8Properties p
? (k, v) -> setProperty(k, v, p.getProvenance(k)
.orElse(null))
: (k, v) -> setProperty(k, v);

properties.forEach((k, v) -> {
String key = (String) k;
String value = (String) v;
if (overwriteIfPresent || !contains(key))
set.accept(key, value);
});
}

Map<String, Provenance> getProvenance() {
return provenance;
}

@Override
public synchronized void putAll(Map<?, ?> t) {
if (t instanceof Properties p) {
load(p, true);
} else
super.putAll(t);
}

/**
* Set the provenance on all current keys
*
* @param provenance
*/
public void setProvenance(String provenance) {
keySet().forEach(k -> setProvenance((String) k, provenance));
}
}
2 changes: 1 addition & 1 deletion aQute.libg/src/aQute/lib/utf8properties/package-info.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@Version("4.1.0")
@Version("4.2.0")
package aQute.lib.utf8properties;

import org.osgi.annotation.versioning.Version;
35 changes: 35 additions & 0 deletions aQute.libg/test/aQute/lib/utf8properties/UTF8PropertiesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,41 @@ public void testWriteFile(@InjectTemporaryDirectory
assertThat(p1).containsExactlyInAnyOrderEntriesOf(p);
}

@Test
public void testProvenance() throws IOException {
UTF8Properties a = new UTF8Properties();
a.load("""
a.a = 1
a.b = 2
x = 0
""", null, null, null, "from_a");
UTF8Properties b = new UTF8Properties();
b.load("""
b.a = 1
b.c = 3
x = 0
""", null, null, null, "from_b");

assertThat(a.getProvenance("x")).isPresent()
.get()
.isEqualTo("from_a");
assertThat(b.getProvenance("x")).isPresent()
.get()
.isEqualTo("from_b");
assertThat(a.getProvenance("y")).isNotPresent();

a.load(b, true);
assertThat(a.getProvenance("x")).isPresent()
.get()
.isEqualTo("from_b");
assertThat(a.getProvenance("a.a")).isPresent()
.get()
.isEqualTo("from_a");
assertThat(a.getProvenance("b.a")).isPresent()
.get()
.isEqualTo("from_b");
}

private void testProperty(String content, String key, String value) throws IOException {
testProperty(content, key, value, null);
}
Expand Down
76 changes: 76 additions & 0 deletions biz.aQute.bndlib.tests/test/test/ProcessorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import aQute.lib.collections.ExtList;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.lib.utf8properties.UTF8Properties;
import aQute.libg.reporter.ReporterAdapter;
import aQute.service.reporter.Reporter;
import aQute.service.reporter.Reporter.SetLocation;
Expand Down Expand Up @@ -774,4 +775,79 @@ public void testIncludeItself() throws IOException {
}
}

@Test
public void testProvenance() throws IOException {
File base = IO.getFile("generated/provenance");
try {
base.mkdirs();
File bnd = new File(base, "bnd.bnd");
IO.store("""
-include sup.bnd, ~inf.bnd
in_top = top
in_sup = top
#in_inf = top
top = true
""", bnd);
File sup = new File(base, "sup.bnd");
IO.store("""
#in_top = sup
in_sup = sup
#in_inf = sup
sup = true
""", sup);
File inf = new File(base, "inf.bnd");
IO.store("""
-include sup.bnd, ~inf.bnd
in_top = inf
in_sup = inf
in_inf = inf
inf = true
sup = false
top = false
""", inf);

try (Processor a = new Processor(); Processor b = new Processor(a)) {
a.setProperty("a", "true");
b.setProperties(bnd);
UTF8Properties bp = (UTF8Properties) b.getProperties();
assertThat(b.getProperty("a")).isEqualTo("true");
assertThat(b.getProperty("in_top")).isEqualTo("top");
assertThat(b.getProperty("in_sup")).isEqualTo("sup");
assertThat(b.getProperty("in_inf")).isEqualTo("inf");
assertThat(b.getProperty("top")).isEqualTo("true");
assertThat(b.getProperty("sup")).isEqualTo("true");
assertThat(b.getProperty("inf")).isEqualTo("true");

assertThat(bp.getProvenance("in_top")).isPresent()
.get()
.isEqualTo(bnd.getAbsolutePath());
assertThat(bp.getProvenance("in_sup")).isPresent()
.get()
.isEqualTo(sup.getAbsolutePath());
assertThat(bp.getProvenance("in_inf")).isPresent()
.get()
.isEqualTo(inf.getAbsolutePath());
assertThat(bp.getProvenance("top")).isPresent()
.get()
.isEqualTo(bnd.getAbsolutePath());
assertThat(bp.getProvenance("sup")).isPresent()
.get()
.isEqualTo(sup.getAbsolutePath());

bp.remove("in_top");
assertThat(bp.getProvenance("in_top")).isNotPresent();

b.setProperty("in_top", "foo");
assertThat(bp.getProvenance("in_top")).isNotPresent();
b.setProperty("in_top", "bar", "xxx");
assertThat(bp.getProvenance("in_top")).isPresent()
.get()
.isEqualTo("xxx");
}
} finally {
IO.delete(base);
}

}

}
2 changes: 2 additions & 0 deletions biz.aQute.bndlib.tests/testresources/provenance/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-include a/sup.bnd,~a/inf.bnd

9 changes: 5 additions & 4 deletions biz.aQute.bndlib/src/aQute/bnd/build/MagicBnd.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import aQute.bnd.result.Result;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.lib.utf8properties.UTF8Properties;
import aQute.libg.re.Catalog;
import aQute.libg.re.RE;
import aQute.libg.re.RE.Match;
Expand Down Expand Up @@ -77,8 +78,8 @@ private static Result<Properties> convertOBR(Workspace workspace, File file) {
.append("'");
}

Properties p = new Properties();
p.put("-plugin.ext." + file.getName(), sb.toString());
UTF8Properties p = new UTF8Properties();
p.setProperty("-plugin.ext." + file.getName(), sb.toString(), file.getAbsolutePath());
return Result.ok(p);
}

Expand Down Expand Up @@ -163,8 +164,8 @@ private static Result<Properties> convertMaven(Workspace ws, File file) {
.collect(Collectors.joining()))
.append("'");

Properties p = new Properties();
p.put("-plugin.ext." + file.getName(), sb.toString());
UTF8Properties p = new UTF8Properties();
p.setProperty("-plugin.ext." + file.getName(), sb.toString(), file.getAbsolutePath());
return Result.ok(p);
}
}
2 changes: 1 addition & 1 deletion biz.aQute.bndlib/src/aQute/bnd/build/Project.java
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ public void prepare() throws Exception {

// We use a builder to construct all the properties for
// use.
setProperty("basedir", basePath);
setProperty("basedir", basePath, "[project]");

// If a bnd.bnd file exists, we read it.
// Otherwise, we just do the build properties.
Expand Down
Loading
Loading