From 9b944cb616c4dea3fd7fb10c5a2f789dc142b86d Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Mon, 14 Mar 2022 14:15:22 +0100 Subject: [PATCH 01/37] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e624941..cd56161 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.4 + 0.0.5-SNAPSHOT Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - v0.0.4 + HEAD From bf4bdd91cd0020e98e6b2c6f13defb66ad8bec57 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 28 Jul 2022 11:32:12 +0200 Subject: [PATCH 02/37] fix: now properly generating hex escape sequences Fixes #11 --- src/main/java/org/codejive/properties/Properties.java | 2 +- .../java/org/codejive/properties/TestProperties.java | 10 ++++++++++ src/test/resources/test-putunicode.properties | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/test-putunicode.properties diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 59c3bb7..1d264bf 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -590,7 +590,7 @@ private String escape(String raw, boolean forKey) { replace( raw, "[^\\x{0000}-\\x{00FF}]", - m -> "\\\\u" + Integer.toString(m.group(0).charAt(0), 16)); + m -> "\\\\u" + String.format("%04x", (int)m.group(0).charAt(0))); return raw; } diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 08a6340..dd84047 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -327,6 +327,16 @@ void testPutNull() throws IOException, URISyntaxException { .isInstanceOf(NullPointerException.class); } + @Test + void testPutUnicode() throws IOException, URISyntaxException { + Properties p = new Properties(); + p.put("test", "الألبانية"); + StringWriter sw = new StringWriter(); + p.store(sw); + assertThat(sw.toString()) + .isEqualTo(readAll(getResource("/test-putunicode.properties"))); + } + @Test void testRemoveFirst() throws IOException, URISyntaxException { Properties p = Properties.loadProperties(getResource("/test.properties")); diff --git a/src/test/resources/test-putunicode.properties b/src/test/resources/test-putunicode.properties new file mode 100644 index 0000000..3978ea1 --- /dev/null +++ b/src/test/resources/test-putunicode.properties @@ -0,0 +1 @@ +test=\u0627\u0644\u0623\u0644\u0628\u0627\u0646\u064a\u0629 \ No newline at end of file From a3c5756382ac9b960e67ef99f40409f813e16b1b Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 28 Jul 2022 19:51:37 +0200 Subject: [PATCH 03/37] fix: we don't escape non-ISO8859-1 chars anymore Fixes #13 --- .../org/codejive/properties/Properties.java | 6 +--- .../codejive/properties/TestProperties.java | 31 ++++++++++--------- src/test/resources/test-comment.properties | 2 +- .../resources/test-getproperty.properties | 2 +- src/test/resources/test-put.properties | 2 +- src/test/resources/test-putnew.properties | 2 +- src/test/resources/test-putraw.properties | 2 +- src/test/resources/test-putunicode.properties | 3 +- .../resources/test-removecomment.properties | 2 +- .../resources/test-removefirst.properties | 2 +- .../resources/test-removemiddle.properties | 2 +- .../resources/test-setproperty.properties | 2 +- .../resources/test-storeheader.properties | 2 +- src/test/resources/test.properties | 2 +- 14 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 1d264bf..c75e6b5 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -579,6 +579,7 @@ private Cursor indexOf(String key) { } private String escape(String raw, boolean forKey) { + raw = raw.replace("\\", "\\\\"); raw = raw.replace("\n", "\\n"); raw = raw.replace("\r", "\\r"); raw = raw.replace("\t", "\\t"); @@ -586,11 +587,6 @@ private String escape(String raw, boolean forKey) { if (forKey) { raw = raw.replace(" ", "\\ "); } - raw = - replace( - raw, - "[^\\x{0000}-\\x{00FF}]", - m -> "\\\\u" + String.format("%04x", (int)m.group(0).charAt(0))); return raw; } diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index dd84047..4987d37 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -30,7 +30,7 @@ void testLoad() throws IOException, URISyntaxException { "everywhere ", "value", "one two three", - "\u1234"); + "\u1234\u1234"); assertThat(p.rawValues()) .containsExactly( "simple", @@ -39,7 +39,7 @@ void testLoad() throws IOException, URISyntaxException { "everywhere ", "value", "one \\\n two \\\n\tthree", - "\\u1234"); + "\\u1234\u1234"); } @Test @@ -69,7 +69,7 @@ void testGet() throws IOException, URISyntaxException { assertThat(p.get(" with spaces")).isEqualTo("everywhere "); assertThat(p.get("altsep")).isEqualTo("value"); assertThat(p.get("multiline")).isEqualTo("one two three"); - assertThat(p.get("key.4")).isEqualTo("\u1234"); + assertThat(p.get("key.4")).isEqualTo("\u1234\u1234"); } @Test @@ -102,7 +102,7 @@ void testGetProperty() throws IOException, URISyntaxException { assertThat(p.getProperty(" with spaces")).isEqualTo("everywhere "); assertThat(p.getProperty("altsep")).isEqualTo(""); assertThat(p.getProperty("multiline")).isEqualTo("one two three"); - assertThat(p.getProperty("key.4")).isEqualTo("\u1234"); + assertThat(p.getProperty("key.4")).isEqualTo("\u1234\u1234"); assertThat(p.getProperty("five")).isEqualTo("5"); assertThat(p.getPropertyComment("five")).containsExactly("# a new comment"); StringWriter sw = new StringWriter(); @@ -119,7 +119,7 @@ void testGetRaw() throws IOException, URISyntaxException { assertThat(p.getRaw(" with spaces")).isEqualTo("everywhere "); assertThat(p.getRaw("altsep")).isEqualTo("value"); assertThat(p.getRaw("multiline")).isEqualTo("one \\\n two \\\n\tthree"); - assertThat(p.getRaw("key.4")).isEqualTo("\\u1234"); + assertThat(p.getRaw("key.4")).isEqualTo("\\u1234\u1234"); } @Test @@ -154,7 +154,7 @@ void testPut() throws IOException, URISyntaxException { p.put(" with spaces", "everywhere "); p.put("altsep", "value"); p.put("multiline", "one two three"); - p.put("key.4", "\u1234"); + p.put("key.4", "\u1234\u1234"); assertThat(p).size().isEqualTo(7); assertThat(p.keySet()) .containsExactly( @@ -170,7 +170,7 @@ void testPut() throws IOException, URISyntaxException { "everywhere ", "value", "one two three", - "\u1234"); + "\u1234\u1234"); assertThat(p.rawValues()) .containsExactly( "simple", @@ -179,7 +179,7 @@ void testPut() throws IOException, URISyntaxException { "everywhere ", "value", "one two three", - "\\u1234"); + "\u1234\u1234"); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-put.properties"))); @@ -195,7 +195,7 @@ void testSetProperty() throws IOException, URISyntaxException { p.setProperty(" with spaces", "everywhere "); p.setProperty("altsep", "value"); p.setProperty("multiline", "one two three"); - p.setProperty("key.4", "\u1234"); + p.setProperty("key.4", "\u1234\u1234"); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-setproperty.properties"))); @@ -210,7 +210,7 @@ void testPutRaw() throws IOException, URISyntaxException { p.putRaw("\\ with\\ spaces", "everywhere "); p.putRaw("altsep", "value"); p.putRaw("multiline", "one \\\n two \\\n\tthree"); - p.putRaw("key.4", "\\u1234"); + p.putRaw("key.4", "\\u1234\u1234"); assertThat(p).size().isEqualTo(7); assertThat(p.keySet()) .containsExactly( @@ -226,7 +226,7 @@ void testPutRaw() throws IOException, URISyntaxException { "everywhere ", "value", "one two three", - "\u1234"); + "\u1234\u1234"); assertThat(p.rawValues()) .containsExactly( "simple", @@ -235,7 +235,7 @@ void testPutRaw() throws IOException, URISyntaxException { "everywhere ", "value", "one \\\n two \\\n\tthree", - "\\u1234"); + "\\u1234\u1234"); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-putraw.properties"))); @@ -330,7 +330,8 @@ void testPutNull() throws IOException, URISyntaxException { @Test void testPutUnicode() throws IOException, URISyntaxException { Properties p = new Properties(); - p.put("test", "الألبانية"); + p.putRaw("encoded", "\\u0627\\u0644\\u0623\\u0644\\u0628\\u0627\\u0646\\u064a\\u0629"); + p.put("text", "\u0627\u0644\u0623\u0644\u0628\u0627\u0646\u064a\u0629"); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()) @@ -431,7 +432,7 @@ public void testInteropLoad() throws IOException, URISyntaxException { "everywhere ", "value", "one two three", - "\u1234"); + "\u1234\u1234"); } @Test @@ -445,7 +446,7 @@ void testInteropStore() throws IOException, URISyntaxException { assertThat(sw.toString()).contains("\\ with\\ spaces=everywhere \n"); assertThat(sw.toString()).contains("altsep=value\n"); assertThat(sw.toString()).contains("multiline=one two three\n"); - assertThat(sw.toString()).contains("key.4=\u1234\n"); + assertThat(sw.toString()).contains("key.4=\u1234\u1234\n"); } @Test diff --git a/src/test/resources/test-comment.properties b/src/test/resources/test-comment.properties index b02026c..0e0d3d1 100644 --- a/src/test/resources/test-comment.properties +++ b/src/test/resources/test-comment.properties @@ -13,5 +13,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment diff --git a/src/test/resources/test-getproperty.properties b/src/test/resources/test-getproperty.properties index fb1e57b..146224a 100644 --- a/src/test/resources/test-getproperty.properties +++ b/src/test/resources/test-getproperty.properties @@ -4,5 +4,5 @@ three=and escapes\n\t\r\f \ with\ spaces=everywhere altsep= multiline=one two three -key.4=\u1234 +key.4=ሴሴ five=5 \ No newline at end of file diff --git a/src/test/resources/test-put.properties b/src/test/resources/test-put.properties index 5c61be4..17e673a 100644 --- a/src/test/resources/test-put.properties +++ b/src/test/resources/test-put.properties @@ -4,4 +4,4 @@ three=and escapes\n\t\r\f \ with\ spaces=everywhere altsep=value multiline=one two three -key.4=\u1234 \ No newline at end of file +key.4=ሴሴ \ No newline at end of file diff --git a/src/test/resources/test-putnew.properties b/src/test/resources/test-putnew.properties index 69ecb32..5e960de 100644 --- a/src/test/resources/test-putnew.properties +++ b/src/test/resources/test-putnew.properties @@ -13,6 +13,6 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ five=5 # final comment diff --git a/src/test/resources/test-putraw.properties b/src/test/resources/test-putraw.properties index ff54e36..61b173f 100644 --- a/src/test/resources/test-putraw.properties +++ b/src/test/resources/test-putraw.properties @@ -6,4 +6,4 @@ altsep=value multiline=one \ two \ three -key.4=\u1234 \ No newline at end of file +key.4=\u1234ሴ \ No newline at end of file diff --git a/src/test/resources/test-putunicode.properties b/src/test/resources/test-putunicode.properties index 3978ea1..29bca09 100644 --- a/src/test/resources/test-putunicode.properties +++ b/src/test/resources/test-putunicode.properties @@ -1 +1,2 @@ -test=\u0627\u0644\u0623\u0644\u0628\u0627\u0646\u064a\u0629 \ No newline at end of file +encoded=\u0627\u0644\u0623\u0644\u0628\u0627\u0646\u064a\u0629 +text=الألبانية \ No newline at end of file diff --git a/src/test/resources/test-removecomment.properties b/src/test/resources/test-removecomment.properties index f891dec..f3fb147 100644 --- a/src/test/resources/test-removecomment.properties +++ b/src/test/resources/test-removecomment.properties @@ -12,5 +12,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment diff --git a/src/test/resources/test-removefirst.properties b/src/test/resources/test-removefirst.properties index 88d9407..369c459 100644 --- a/src/test/resources/test-removefirst.properties +++ b/src/test/resources/test-removefirst.properties @@ -11,5 +11,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment diff --git a/src/test/resources/test-removemiddle.properties b/src/test/resources/test-removemiddle.properties index aaa6461..6d9e10b 100644 --- a/src/test/resources/test-removemiddle.properties +++ b/src/test/resources/test-removemiddle.properties @@ -9,5 +9,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment diff --git a/src/test/resources/test-setproperty.properties b/src/test/resources/test-setproperty.properties index d1e31fc..e4433fa 100644 --- a/src/test/resources/test-setproperty.properties +++ b/src/test/resources/test-setproperty.properties @@ -8,4 +8,4 @@ three=and escapes\n\t\r\f \ with\ spaces=everywhere altsep=value multiline=one two three -key.4=\u1234 \ No newline at end of file +key.4=ሴሴ \ No newline at end of file diff --git a/src/test/resources/test-storeheader.properties b/src/test/resources/test-storeheader.properties index 7ca6eec..3ab1ef7 100644 --- a/src/test/resources/test-storeheader.properties +++ b/src/test/resources/test-storeheader.properties @@ -12,5 +12,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment diff --git a/src/test/resources/test.properties b/src/test/resources/test.properties index ed2a301..f2d73bd 100644 --- a/src/test/resources/test.properties +++ b/src/test/resources/test.properties @@ -13,5 +13,5 @@ altsep:value multiline = one \ two \ three -key.4 = \u1234 +key.4 = \u1234ሴ # final comment From 671cbcbfcb54107d3d1e5d97f8388d3a8409b926 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 28 Jul 2022 20:20:16 +0200 Subject: [PATCH 04/37] feat: Added `escaped()` and `unescaped()` functions Added `escaped()` function that returns a copy of the `Properties` where all non-ISO8859-1 chars in all keys and values are turned into Unicode escape sequences. There's also an `unescaped()` that does the opposite. --- .../org/codejive/properties/Properties.java | 184 ++++++++++++++++-- .../codejive/properties/PropertiesParser.java | 11 +- .../codejive/properties/TestProperties.java | 71 +++++++ src/test/resources/test-escaped.properties | 17 ++ src/test/resources/test-unescaped.properties | 17 ++ 5 files changed, 280 insertions(+), 20 deletions(-) create mode 100644 src/test/resources/test-escaped.properties create mode 100644 src/test/resources/test-unescaped.properties diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index c75e6b5..d6087c2 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -13,6 +13,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * This class is a replacement for java.util.Properties, with the difference that it @@ -36,6 +38,15 @@ public Properties(Properties defaults) { tokens = new ArrayList<>(); } + private Properties(Properties defaults, List tokens) { + this.defaults = defaults; + values = new LinkedHashMap<>(); + this.tokens = tokens; + rawEntrySet().forEach(e -> { + values.put(unescape(e.getKey()), unescape(e.getValue())); + }); + } + /** * Searches for the property with the specified key in this property list. If the key is not * found in this property list, the default property list, and its defaults, recursively, are @@ -186,12 +197,24 @@ public void storeToXML(OutputStream os, String comment, String encoding) throws } /** - * Returns the current properties table with all its defaults as a single flattened properties - * table + * Returns the current properties table with all its defaults as a single + * flattened properties table. NB: Result will have no formatting or comments! * * @return a Properties object + * @deprecated Use flattened() */ + @Deprecated public Properties flatten() { + return flattened(); + } + + /** + * Returns the current properties table with all its defaults as a single + * flattened properties table. NB: Result will have no formatting or comments! + * + * @return a Properties object + */ + public Properties flattened() { Properties result = new Properties(); flatten(result); return result; @@ -261,12 +284,25 @@ public Set rawKeySet() { * @return a collection of raw values. */ public Collection rawValues() { - return IntStream.range(0, tokens.size()) - .filter(idx -> tokens.get(idx).type == PropertiesParser.Type.KEY) - .mapToObj(idx -> tokens.get(idx + 2).getRaw()) + return combined(tokens) + .filter(ts -> ts.get(0).type == PropertiesParser.Type.KEY) + .map(ts -> ts.get(2).getRaw()) .collect(Collectors.toList()); } + /** + * Works like entrySet() but returning the raw values. Meaning that the values have + * not been unescaped before being returned. + * + * @return A set of raw key-value entries + */ + public Set> rawEntrySet() { + return combined(tokens) + .filter(ts -> ts.get(0).type == PropertiesParser.Type.KEY) + .map(ts -> new SimpleEntry<>(ts.get(0).getRaw(), ts.get(2).getRaw())) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + @Override public String get(Object key) { return values.get(key); @@ -296,11 +332,11 @@ public String put(String key, String value) { if (key == null || value == null) { throw new NullPointerException(); } - String rawValue = escape(value, false); + String rawValue = escapeValue(value); if (values.containsKey(key)) { replaceValue(key, rawValue, value); } else { - String rawKey = escape(key, true); + String rawKey = escapeKey(key); addNewKeyValue(rawKey, key, rawValue, value); } return values.put(key, value); @@ -575,19 +611,48 @@ private Cursor indexOf(String key) { return index( tokens.indexOf( new PropertiesParser.Token( - PropertiesParser.Type.KEY, escape(key, true), key))); - } - - private String escape(String raw, boolean forKey) { - raw = raw.replace("\\", "\\\\"); - raw = raw.replace("\n", "\\n"); - raw = raw.replace("\r", "\\r"); - raw = raw.replace("\t", "\\t"); - raw = raw.replace("\f", "\\f"); - if (forKey) { - raw = raw.replace(" ", "\\ "); + PropertiesParser.Type.KEY, escapeKey(key), key))); + } + + private static String escapeValue(String value) { + return value + .replace("\\", "\\\\") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t") + .replace("\f", "\\f"); + } + + private static String escapeKey(String key) { + return escapeValue(key).replace(" ", "\\ "); + } + + private static String escapeUnicode(String text) { + return replace( + text, + "[^\\x{0000}-\\x{00FF}]", + m -> "\\\\u" + String.format("%04x", (int)m.group(0).charAt(0))); + } + + private static String unescapeUnicode(String escape) { + StringBuilder txt = new StringBuilder(); + for (int i = 0; i < escape.length(); i++) { + char ch = escape.charAt(i); + if (ch == '\\') { + ch = escape.charAt(++i); + if (ch == 'u') { + String num = escape.substring(i + 1, i + 5); + txt.append((char) Integer.parseInt(num, 16)); + i += 4; + } else { + txt.append('\\'); + txt.append(ch); + } + } else { + txt.append(ch); + } } - return raw; + return txt.toString(); } private static String replace(String input, String regex, Function callback) { @@ -605,6 +670,87 @@ private static String replace(String input, Pattern regex, Functionstore() to write to an output that does not support UTF8. + * + * @return A Properties with encoded keys and values + */ + public Properties escaped() { + return new Properties(defaults != null ? defaults.escaped() : null, escapeTokens(tokens)); + } + + private static List escapeTokens(List tokens) { + return mapKeyValues(tokens, ts -> Arrays.asList(escapeToken(ts.get(0)), ts.get(1), escapeToken(ts.get(2)))); + } + + private static PropertiesParser.Token escapeToken(PropertiesParser.Token token) { + String raw = escapeUnicode(token.raw); + if (!raw.equals(token.raw)) { + token = new PropertiesParser.Token(token.type, raw, token.text); + } + return token; + } + + /** + * Returns a copy of the object where all Unicode escape sequences, in keys and values, + * have been decoded into their actual Unicode characters. This is useful when using + * store() to write to an output that supports UTF8. + * + * @return A Properties without Unicode escape sequences in its keys and values + */ + public Properties unescaped() { + return new Properties(defaults != null ? defaults.unescaped() : null, unescapeTokens(tokens)); + } + + private static List unescapeTokens(List tokens) { + return mapKeyValues(tokens, ts -> Arrays.asList(unescapeToken(ts.get(0)), ts.get(1), unescapeToken(ts.get(2)))); + } + + private static PropertiesParser.Token unescapeToken(PropertiesParser.Token token) { + String raw = unescapeUnicode(token.raw); + if (!raw.equals(token.raw)) { + token = new PropertiesParser.Token(token.type, raw, token.text); + } + return token; + } + + private static List mapKeyValues( + List tokens, + Function, List> mapper) { + return combined(tokens).map(ts -> { + if (ts.get(0).type == PropertiesParser.Type.KEY) { + return mapper.apply(ts); + } else { + return ts; + } + }).flatMap(Collection::stream).collect(Collectors.toList()); + } + + private static Stream> combined(List tokens) { + Iterator> iter = new Iterator>() { + Iterator i = tokens.iterator(); + + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public List next() { + PropertiesParser.Token t = i.next(); + if (t.type == PropertiesParser.Type.KEY) { + return Arrays.asList(t, i.next(), i.next()); + } else { + return Collections.singletonList(t); + } + } + }; + + return StreamSupport.stream(Spliterators.spliterator(iter, tokens.size(), Spliterator.SORTED), false); + } + /** * Returns a java.util.Properties with the same contents as this object. The * information is a copy, changes to one Properties object will not affect the other. diff --git a/src/main/java/org/codejive/properties/PropertiesParser.java b/src/main/java/org/codejive/properties/PropertiesParser.java index 4f197e7..2388e44 100644 --- a/src/main/java/org/codejive/properties/PropertiesParser.java +++ b/src/main/java/org/codejive/properties/PropertiesParser.java @@ -21,7 +21,9 @@ class PropertiesParser { public enum Type { /** The key part of a key-value pair */ KEY, - /** The separator between a key and a value */ + /** The separator between a key and a value. This will include any whitespace that exists + * before and after the separator! + */ SEPARATOR, /** The value part of a key-value pair */ VALUE, @@ -293,6 +295,13 @@ private String string() { return result; } + /** + * Returns a copy of the given string where all escape sequences + * have been turned into their representative values. + * + * @param escape Input string + * @return Decoded string + */ static String unescape(String escape) { StringBuilder txt = new StringBuilder(); for (int i = 0; i < escape.length(); i++) { diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 4987d37..badd1de 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.AbstractMap; import java.util.Collections; import java.util.Iterator; import org.junit.jupiter.api.Test; @@ -40,6 +41,24 @@ void testLoad() throws IOException, URISyntaxException { "value", "one \\\n two \\\n\tthree", "\\u1234\u1234"); + assertThat(p.entrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\n\t\r\f"), + new AbstractMap.SimpleEntry<>(" with spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one two three"), + new AbstractMap.SimpleEntry<>("key.4", "\u1234\u1234")); + assertThat(p.rawEntrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\\n\\t\\r\\f"), + new AbstractMap.SimpleEntry<>("\\ with\\ spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one \\\n two \\\n\tthree"), + new AbstractMap.SimpleEntry<>("key.4", "\\u1234\u1234")); } @Test @@ -180,6 +199,24 @@ void testPut() throws IOException, URISyntaxException { "value", "one two three", "\u1234\u1234"); + assertThat(p.entrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\n\t\r\f"), + new AbstractMap.SimpleEntry<>(" with spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one two three"), + new AbstractMap.SimpleEntry<>("key.4", "\u1234\u1234")); + assertThat(p.rawEntrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\\n\\t\\r\\f"), + new AbstractMap.SimpleEntry<>("\\ with\\ spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one two three"), + new AbstractMap.SimpleEntry<>("key.4", "\u1234\u1234")); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-put.properties"))); @@ -236,6 +273,24 @@ void testPutRaw() throws IOException, URISyntaxException { "value", "one \\\n two \\\n\tthree", "\\u1234\u1234"); + assertThat(p.entrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\n\t\r\f"), + new AbstractMap.SimpleEntry<>(" with spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one two three"), + new AbstractMap.SimpleEntry<>("key.4", "\u1234\u1234")); + assertThat(p.rawEntrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\\n\\t\\r\\f"), + new AbstractMap.SimpleEntry<>("\\ with\\ spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one \\\n two \\\n\tthree"), + new AbstractMap.SimpleEntry<>("key.4", "\\u1234\u1234")); StringWriter sw = new StringWriter(); p.store(sw); assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-putraw.properties"))); @@ -485,6 +540,22 @@ void testInteropPutLoad() throws IOException, URISyntaxException { "\u1234"); } + @Test + void testEscaped() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + StringWriter sw = new StringWriter(); + p.escaped().store(sw); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-escaped.properties"))); + } + + @Test + void testUnescaped() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + StringWriter sw = new StringWriter(); + p.unescaped().store(sw); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-unescaped.properties"))); + } + private Path getResource(String name) throws URISyntaxException { return Paths.get(getClass().getResource(name).toURI()); } diff --git a/src/test/resources/test-escaped.properties b/src/test/resources/test-escaped.properties new file mode 100644 index 0000000..7c6e49c --- /dev/null +++ b/src/test/resources/test-escaped.properties @@ -0,0 +1,17 @@ +#comment1 +# comment2 + +! comment3 +one=simple +two=value containing spaces +# another comment +! and a comment +! block +three=and escapes\n\t\r\f +\ with\ spaces = everywhere +altsep:value +multiline = one \ + two \ + three +key.4 = \u1234\u1234 +# final comment diff --git a/src/test/resources/test-unescaped.properties b/src/test/resources/test-unescaped.properties new file mode 100644 index 0000000..bac7a7f --- /dev/null +++ b/src/test/resources/test-unescaped.properties @@ -0,0 +1,17 @@ +#comment1 +# comment2 + +! comment3 +one=simple +two=value containing spaces +# another comment +! and a comment +! block +three=and escapes\n\t\r\f +\ with\ spaces = everywhere +altsep:value +multiline = one \ + two \ + three +key.4 = ሴሴ +# final comment From 2e72f7f21fa98b9a92ef3c99a86fefaba9919b07 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sat, 30 Jul 2022 19:38:46 +0200 Subject: [PATCH 05/37] chore: formatting --- .../org/codejive/properties/Properties.java | 105 ++++++++++-------- .../codejive/properties/PropertiesParser.java | 7 +- .../codejive/properties/TestProperties.java | 3 +- 3 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index d6087c2..81e5429 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -12,7 +12,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -42,9 +41,11 @@ private Properties(Properties defaults, List tokens) { this.defaults = defaults; values = new LinkedHashMap<>(); this.tokens = tokens; - rawEntrySet().forEach(e -> { - values.put(unescape(e.getKey()), unescape(e.getValue())); - }); + rawEntrySet() + .forEach( + e -> { + values.put(unescape(e.getKey()), unescape(e.getValue())); + }); } /** @@ -197,8 +198,8 @@ public void storeToXML(OutputStream os, String comment, String encoding) throws } /** - * Returns the current properties table with all its defaults as a single - * flattened properties table. NB: Result will have no formatting or comments! + * Returns the current properties table with all its defaults as a single flattened properties + * table. NB: Result will have no formatting or comments! * * @return a Properties object * @deprecated Use flattened() @@ -209,8 +210,8 @@ public Properties flatten() { } /** - * Returns the current properties table with all its defaults as a single - * flattened properties table. NB: Result will have no formatting or comments! + * Returns the current properties table with all its defaults as a single flattened properties + * table. NB: Result will have no formatting or comments! * * @return a Properties object */ @@ -615,8 +616,7 @@ private Cursor indexOf(String key) { } private static String escapeValue(String value) { - return value - .replace("\\", "\\\\") + return value.replace("\\", "\\\\") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t") @@ -631,7 +631,7 @@ private static String escapeUnicode(String text) { return replace( text, "[^\\x{0000}-\\x{00FF}]", - m -> "\\\\u" + String.format("%04x", (int)m.group(0).charAt(0))); + m -> "\\\\u" + String.format("%04x", (int) m.group(0).charAt(0))); } private static String unescapeUnicode(String escape) { @@ -671,9 +671,9 @@ private static String replace(String input, Pattern regex, Functionstore() to write to an output that does not support UTF8. + * Returns a copy of the object where all characters, in keys and values that are not in the + * Unicode range of 0x0000-0x00FF, have been escaped. This is useful when using store() + * to write to an output that does not support UTF8. * * @return A Properties with encoded keys and values */ @@ -682,7 +682,9 @@ public Properties escaped() { } private static List escapeTokens(List tokens) { - return mapKeyValues(tokens, ts -> Arrays.asList(escapeToken(ts.get(0)), ts.get(1), escapeToken(ts.get(2)))); + return mapKeyValues( + tokens, + ts -> Arrays.asList(escapeToken(ts.get(0)), ts.get(1), escapeToken(ts.get(2)))); } private static PropertiesParser.Token escapeToken(PropertiesParser.Token token) { @@ -694,18 +696,22 @@ private static PropertiesParser.Token escapeToken(PropertiesParser.Token token) } /** - * Returns a copy of the object where all Unicode escape sequences, in keys and values, - * have been decoded into their actual Unicode characters. This is useful when using - * store() to write to an output that supports UTF8. + * Returns a copy of the object where all Unicode escape sequences, in keys and values, have + * been decoded into their actual Unicode characters. This is useful when using store() + * to write to an output that supports UTF8. * * @return A Properties without Unicode escape sequences in its keys and values */ public Properties unescaped() { - return new Properties(defaults != null ? defaults.unescaped() : null, unescapeTokens(tokens)); + return new Properties( + defaults != null ? defaults.unescaped() : null, unescapeTokens(tokens)); } - private static List unescapeTokens(List tokens) { - return mapKeyValues(tokens, ts -> Arrays.asList(unescapeToken(ts.get(0)), ts.get(1), unescapeToken(ts.get(2)))); + private static List unescapeTokens( + List tokens) { + return mapKeyValues( + tokens, + ts -> Arrays.asList(unescapeToken(ts.get(0)), ts.get(1), unescapeToken(ts.get(2)))); } private static PropertiesParser.Token unescapeToken(PropertiesParser.Token token) { @@ -719,36 +725,43 @@ private static PropertiesParser.Token unescapeToken(PropertiesParser.Token token private static List mapKeyValues( List tokens, Function, List> mapper) { - return combined(tokens).map(ts -> { - if (ts.get(0).type == PropertiesParser.Type.KEY) { - return mapper.apply(ts); - } else { - return ts; - } - }).flatMap(Collection::stream).collect(Collectors.toList()); + return combined(tokens) + .map( + ts -> { + if (ts.get(0).type == PropertiesParser.Type.KEY) { + return mapper.apply(ts); + } else { + return ts; + } + }) + .flatMap(Collection::stream) + .collect(Collectors.toList()); } - private static Stream> combined(List tokens) { - Iterator> iter = new Iterator>() { - Iterator i = tokens.iterator(); + private static Stream> combined( + List tokens) { + Iterator> iter = + new Iterator>() { + Iterator i = tokens.iterator(); - @Override - public boolean hasNext() { - return i.hasNext(); - } + @Override + public boolean hasNext() { + return i.hasNext(); + } - @Override - public List next() { - PropertiesParser.Token t = i.next(); - if (t.type == PropertiesParser.Type.KEY) { - return Arrays.asList(t, i.next(), i.next()); - } else { - return Collections.singletonList(t); - } - } - }; + @Override + public List next() { + PropertiesParser.Token t = i.next(); + if (t.type == PropertiesParser.Type.KEY) { + return Arrays.asList(t, i.next(), i.next()); + } else { + return Collections.singletonList(t); + } + } + }; - return StreamSupport.stream(Spliterators.spliterator(iter, tokens.size(), Spliterator.SORTED), false); + return StreamSupport.stream( + Spliterators.spliterator(iter, tokens.size(), Spliterator.SORTED), false); } /** diff --git a/src/main/java/org/codejive/properties/PropertiesParser.java b/src/main/java/org/codejive/properties/PropertiesParser.java index 2388e44..8cd90f3 100644 --- a/src/main/java/org/codejive/properties/PropertiesParser.java +++ b/src/main/java/org/codejive/properties/PropertiesParser.java @@ -21,7 +21,8 @@ class PropertiesParser { public enum Type { /** The key part of a key-value pair */ KEY, - /** The separator between a key and a value. This will include any whitespace that exists + /** + * The separator between a key and a value. This will include any whitespace that exists * before and after the separator! */ SEPARATOR, @@ -296,8 +297,8 @@ private String string() { } /** - * Returns a copy of the given string where all escape sequences - * have been turned into their representative values. + * Returns a copy of the given string where all escape sequences have been turned into their + * representative values. * * @param escape Input string * @return Decoded string diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index badd1de..7716552 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -389,8 +389,7 @@ void testPutUnicode() throws IOException, URISyntaxException { p.put("text", "\u0627\u0644\u0623\u0644\u0628\u0627\u0646\u064a\u0629"); StringWriter sw = new StringWriter(); p.store(sw); - assertThat(sw.toString()) - .isEqualTo(readAll(getResource("/test-putunicode.properties"))); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-putunicode.properties"))); } @Test From 1e4a9c3698553ed8abe871a63553c1a25081cc31 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sat, 30 Jul 2022 19:40:25 +0200 Subject: [PATCH 06/37] [maven-release-plugin] prepare release v0.0.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cd56161..96bb13b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.5-SNAPSHOT + 0.0.5 Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - HEAD + v0.0.5 From b41c1b9a089243f64c7e303590d695fd91f6df0d Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sat, 30 Jul 2022 19:40:28 +0200 Subject: [PATCH 07/37] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 96bb13b..48392de 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.5 + 0.0.6-SNAPSHOT Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - v0.0.5 + HEAD From 2fe492054d3b0ef219de692e42a42cb5beaa9f56 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Mon, 14 Nov 2022 15:35:05 +0100 Subject: [PATCH 08/37] fix: Fixed handling of files containin CRLF lines endings Fixes #15 --- .gitattributes | 1 + .../org/codejive/properties/Properties.java | 34 ++++++++++- .../codejive/properties/PropertiesParser.java | 14 ++--- .../codejive/properties/TestProperties.java | 56 +++++++++++++++++++ .../properties/TestPropertiesParser.java | 12 ++-- .../resources/testcrlf-storeheader.properties | 16 ++++++ src/test/resources/testcrlf.properties | 17 ++++++ 7 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 src/test/resources/testcrlf-storeheader.properties create mode 100644 src/test/resources/testcrlf.properties diff --git a/.gitattributes b/.gitattributes index fcadb2c..bcc0efa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text eol=lf +src/test/resources/testcrlf* text eol=crlf diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 81e5429..510187a 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -903,14 +903,15 @@ public void store(Writer writer, String... comment) throws IOException { Cursor pos = first(); if (comment.length > 0) { pos = skipHeaderCommentLines(); + String nl = determineNewline(); List newcs = normalizeComments(Arrays.asList(comment), "# "); for (String c : newcs) { writer.write(new PropertiesParser.Token(PropertiesParser.Type.COMMENT, c).getRaw()); - writer.write(PropertiesParser.Token.EOL.getRaw()); + writer.write(nl); } // We write an extra empty line so this comment won't be taken as part of the first // property - writer.write(PropertiesParser.Token.EOL.getRaw()); + writer.write(nl); } while (pos.hasToken()) { writer.write(pos.raw()); @@ -918,6 +919,35 @@ public void store(Writer writer, String... comment) throws IOException { } } + /** + * This method determines the newline string to use when generating line terminators. It looks + * at all existing line terminators and will use those for any new lines. In case of ambiguity + * (a file contains both LF and CRLF terminators) it will return the system's default line + * ending. + * + * @return A string containing the line ending to use + */ + private String determineNewline() { + boolean lf = false; + boolean crlf = false; + for (PropertiesParser.Token token : tokens) { + if (token.isWs()) { + if (token.raw.endsWith("/r/n")) { + crlf = true; + } else if (token.raw.endsWith("/n")) { + lf = true; + } + } + } + if (lf && crlf) { + return System.lineSeparator(); + } else if (crlf) { + return "/r/n"; + } else { + return "\n"; + } + } + private Cursor skipHeaderCommentLines() { Cursor pos = first(); // Skip a single following whitespace if it is NOT an EOL token diff --git a/src/main/java/org/codejive/properties/PropertiesParser.java b/src/main/java/org/codejive/properties/PropertiesParser.java index 8cd90f3..c5f73bc 100644 --- a/src/main/java/org/codejive/properties/PropertiesParser.java +++ b/src/main/java/org/codejive/properties/PropertiesParser.java @@ -283,8 +283,8 @@ private void addChar(int ch) throws IOException { } private void readEol(int ch) throws IOException { - if (ch == '\n') { - if (peekChar() == '\r') { + if (ch == '\r') { + if (peekChar() == '\n') { str.append((char) readChar()); } } @@ -327,15 +327,15 @@ static String unescape(String escape) { txt.append((char) Integer.parseInt(num, 16)); i += 4; break; - case '\n': - // Skip the next character if it's a '\r' - if (i < escape.length() && escape.charAt(i + 1) == '\r') { + case '\r': + // Skip the next character if it's a '\n' + if (i < (escape.length() - 1) && escape.charAt(i + 1) == '\n') { i++; } // fall-through! - case '\r': + case '\n': // Skip any leading whitespace - while (i < escape.length() + while (i < (escape.length() - 1) && isWhitespaceChar(ch = escape.charAt(i + 1)) && !isEol(ch)) { i++; diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 7716552..b8d7601 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -61,6 +61,53 @@ void testLoad() throws IOException, URISyntaxException { new AbstractMap.SimpleEntry<>("key.4", "\\u1234\u1234")); } + void testLoadCrLf() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/testcrlf.properties")); + assertThat(p).size().isEqualTo(7); + assertThat(p.keySet()) + .containsExactly( + "one", "two", "three", " with spaces", "altsep", "multiline", "key.4"); + assertThat(p.rawKeySet()) + .containsExactly( + "one", "two", "three", "\\ with\\ spaces", "altsep", "multiline", "key.4"); + assertThat(p.values()) + .containsExactly( + "simple", + "value containing spaces", + "and escapes\n\t\r\f", + "everywhere ", + "value", + "one two three", + "\u1234\u1234"); + assertThat(p.rawValues()) + .containsExactly( + "simple", + "value containing spaces", + "and escapes\\n\\t\\r\\f", + "everywhere ", + "value", + "one \\\n two \\\n\tthree", + "\\u1234\u1234"); + assertThat(p.entrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\n\t\r\f"), + new AbstractMap.SimpleEntry<>(" with spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one two three"), + new AbstractMap.SimpleEntry<>("key.4", "\u1234\u1234")); + assertThat(p.rawEntrySet()) + .containsExactly( + new AbstractMap.SimpleEntry<>("one", "simple"), + new AbstractMap.SimpleEntry<>("two", "value containing spaces"), + new AbstractMap.SimpleEntry<>("three", "and escapes\\n\\t\\r\\f"), + new AbstractMap.SimpleEntry<>("\\ with\\ spaces", "everywhere "), + new AbstractMap.SimpleEntry<>("altsep", "value"), + new AbstractMap.SimpleEntry<>("multiline", "one \\\n two \\\n\tthree"), + new AbstractMap.SimpleEntry<>("key.4", "\\u1234\u1234")); + } + @Test void testStore() throws IOException, URISyntaxException { Path f = getResource("/test.properties"); @@ -70,6 +117,15 @@ void testStore() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(f)); } + @Test + void testStoreCrLf() throws IOException, URISyntaxException { + Path f = getResource("/testcrlf.properties"); + Properties p = Properties.loadProperties(f); + StringWriter sw = new StringWriter(); + p.store(sw); + assertThat(sw.toString()).isEqualTo(readAll(f)); + } + @Test void testStoreHeader() throws IOException, URISyntaxException { Path f = getResource("/test.properties"); diff --git a/src/test/java/org/codejive/properties/TestPropertiesParser.java b/src/test/java/org/codejive/properties/TestPropertiesParser.java index 4ae32f2..cf31786 100644 --- a/src/test/java/org/codejive/properties/TestPropertiesParser.java +++ b/src/test/java/org/codejive/properties/TestPropertiesParser.java @@ -19,7 +19,7 @@ public class TestPropertiesParser { + "\n" + "! comment3\n" + "one=simple\n" - + "two=value containing spaces\n\r" + + "two=value containing spaces\r\n" + "# another comment\n" + "! and a comment\n" + "! block\n" @@ -27,9 +27,9 @@ public class TestPropertiesParser { + " \\ with\\ spaces = everywhere \n" + "altsep:value\n" + "multiline = one \\\n" - + " two \\\n\r" + + " two \\\r\n" + "\tthree\n" - + "key.4 = \\u1234\n\r" + + "key.4 = \\u1234\r\n" + " # final comment"; @Test @@ -53,7 +53,7 @@ void testTokens() throws IOException { new Token(Type.KEY, "two"), new Token(Type.SEPARATOR, "="), new Token(Type.VALUE, "value containing spaces"), - new Token(Type.WHITESPACE, "\n\r"), + new Token(Type.WHITESPACE, "\r\n"), new Token(Type.COMMENT, "# another comment"), new Token(Type.WHITESPACE, "\n"), new Token(Type.COMMENT, "! and a comment"), @@ -75,12 +75,12 @@ void testTokens() throws IOException { new Token(Type.WHITESPACE, "\n"), new Token(Type.KEY, "multiline"), new Token(Type.SEPARATOR, " = "), - new Token(Type.VALUE, "one \\\n two \\\n\r\tthree", "one two three"), + new Token(Type.VALUE, "one \\\n two \\\r\n\tthree", "one two three"), new Token(Type.WHITESPACE, "\n"), new Token(Type.KEY, "key.4"), new Token(Type.SEPARATOR, " = "), new Token(Type.VALUE, "\\u1234", "\u1234"), - new Token(Type.WHITESPACE, "\n\r"), + new Token(Type.WHITESPACE, "\r\n"), new Token(Type.WHITESPACE, " "), new Token(Type.COMMENT, "# final comment")); } diff --git a/src/test/resources/testcrlf-storeheader.properties b/src/test/resources/testcrlf-storeheader.properties new file mode 100644 index 0000000..3ab1ef7 --- /dev/null +++ b/src/test/resources/testcrlf-storeheader.properties @@ -0,0 +1,16 @@ +# A header line + +! comment3 +one=simple +two=value containing spaces +# another comment +! and a comment +! block +three=and escapes\n\t\r\f +\ with\ spaces = everywhere +altsep:value +multiline = one \ + two \ + three +key.4 = \u1234ሴ +# final comment diff --git a/src/test/resources/testcrlf.properties b/src/test/resources/testcrlf.properties new file mode 100644 index 0000000..f2d73bd --- /dev/null +++ b/src/test/resources/testcrlf.properties @@ -0,0 +1,17 @@ +#comment1 +# comment2 + +! comment3 +one=simple +two=value containing spaces +# another comment +! and a comment +! block +three=and escapes\n\t\r\f +\ with\ spaces = everywhere +altsep:value +multiline = one \ + two \ + three +key.4 = \u1234ሴ +# final comment From f502203c340a00a773a86eb3583c20a047601b22 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Mon, 14 Nov 2022 15:55:19 +0100 Subject: [PATCH 09/37] [maven-release-plugin] prepare release v0.0.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48392de..18a0daa 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.6-SNAPSHOT + 0.0.6 Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - HEAD + v0.0.6 From 72591e61ac8afd1029fcc6ead14e33fe12f0556a Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Mon, 14 Nov 2022 15:55:25 +0100 Subject: [PATCH 10/37] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 18a0daa..7630b44 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.6 + 0.0.7-SNAPSHOT Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - v0.0.6 + HEAD From bf37676bc562f4f440dff2863a746560f0cfc3ad Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sun, 19 Mar 2023 23:49:00 +0100 Subject: [PATCH 11/37] fix: Corrected `Cursor.prevIf()` and `.nextIf()` feedback They would not correctly indicate if they were pointing to a valid element or not. This is now fixed. See #17 for more information --- .../java/org/codejive/properties/Cursor.java | 6 ++++-- .../org/codejive/properties/Properties.java | 6 +++--- .../codejive/properties/TestProperties.java | 18 ++++++++++++++++++ .../resources/test-commentfirstline.properties | 3 +++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/test-commentfirstline.properties diff --git a/src/main/java/org/codejive/properties/Cursor.java b/src/main/java/org/codejive/properties/Cursor.java index e416b12..0da8375 100644 --- a/src/main/java/org/codejive/properties/Cursor.java +++ b/src/main/java/org/codejive/properties/Cursor.java @@ -95,7 +95,8 @@ public boolean nextIf(PropertiesParser.Type type) { public boolean nextIf(Predicate accept) { if (hasToken() && accept.test(token())) { - return next().hasToken(); + next(); + return true; } else { return false; } @@ -120,7 +121,8 @@ public boolean prevIf(PropertiesParser.Type type) { public boolean prevIf(Predicate accept) { if (hasToken() && accept.test(token())) { - return prev().hasToken(); + prev(); + return true; } else { return false; } diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 510187a..dd9fdad 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -969,15 +969,15 @@ private Cursor skipHeaderCommentLines() { } } - private Cursor index(int index) { + Cursor index(int index) { return Cursor.index(tokens, index); } - private Cursor first() { + Cursor first() { return Cursor.first(tokens); } - private Cursor last() { + Cursor last() { return Cursor.last(tokens); } diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index b8d7601..082d80e 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -206,6 +206,15 @@ void testGetComment() throws IOException, URISyntaxException { .containsExactly("# another comment", "! and a comment", "! block"); } + @Test + void testCommentFirstLine() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test-commentfirstline.properties")); + assertThat(p.getComment("one")).containsExactly("#comment1", "# comment2"); + StringWriter sw = new StringWriter(); + p.store(sw); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-commentfirstline.properties"))); + } + @Test void testSetComment() throws IOException, URISyntaxException { Properties p = Properties.loadProperties(getResource("/test.properties")); @@ -611,6 +620,15 @@ void testUnescaped() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-unescaped.properties"))); } + @Test + void testCursor() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + Cursor c = p.first(); + assertThat(c.nextCount(t -> true)).isEqualTo(p.last().position() + 1); + c = p.last(); + assertThat(c.prevCount(t -> true)).isEqualTo(p.last().position() + 1); + } + private Path getResource(String name) throws URISyntaxException { return Paths.get(getClass().getResource(name).toURI()); } diff --git a/src/test/resources/test-commentfirstline.properties b/src/test/resources/test-commentfirstline.properties new file mode 100644 index 0000000..22ec5cf --- /dev/null +++ b/src/test/resources/test-commentfirstline.properties @@ -0,0 +1,3 @@ +#comment1 +# comment2 +one=simple From 183ad48af9acc2d481a39b3b28c4444f8c9cb08c Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sun, 19 Mar 2023 23:53:13 +0100 Subject: [PATCH 12/37] fix: some methods were not checking for valid keys This is now fixed and tests are added for those situations. --- .../org/codejive/properties/Properties.java | 15 +++++-- .../codejive/properties/TestProperties.java | 44 ++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index dd9fdad..2544a33 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -426,10 +426,15 @@ private Cursor addNewKeyValue(String rawKey, String key, String rawValue, String @Override public String remove(Object key) { String skey = key.toString(); - removeItem(skey); - return values.remove(skey); + if (containsKey(key)) { + removeItem(skey); + return values.remove(skey); + } else { + return null; + } } + // Calling code MUST make sure skey exists! private void removeItem(String skey) { setComment(skey, Collections.emptyList()); Cursor pos = indexOf(skey); @@ -580,7 +585,11 @@ private String getPrefix(String comment) { private List findPropertyCommentLines(String key) { Cursor pos = indexOf(key); - return findPropertyCommentLines(pos); + if (pos.hasToken()) { + return findPropertyCommentLines(pos); + } else { + return Collections.emptyList(); + } } /** diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 082d80e..c9f608f 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -10,6 +10,7 @@ import java.util.AbstractMap; import java.util.Collections; import java.util.Iterator; +import java.util.NoSuchElementException; import org.junit.jupiter.api.Test; public class TestProperties { @@ -197,6 +198,24 @@ void testGetRaw() throws IOException, URISyntaxException { assertThat(p.getRaw("key.4")).isEqualTo("\\u1234\u1234"); } + @Test + void testGetNonExistent() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThat(p.get("wrong")).isNull(); + } + + @Test + void testGetPropertyNonExistent() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThat(p.getProperty("wrong")).isNull(); + } + + @Test + void testGetPropertyDefault() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThat(p.getProperty("wrong", "right")).isEqualTo("right"); + } + @Test void testGetComment() throws IOException, URISyntaxException { Properties p = Properties.loadProperties(getResource("/test.properties")); @@ -206,13 +225,20 @@ void testGetComment() throws IOException, URISyntaxException { .containsExactly("# another comment", "! and a comment", "! block"); } + @Test + void testGetCommentNonExistent() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThat(p.getComment("wrong")).isEmpty(); + } + @Test void testCommentFirstLine() throws IOException, URISyntaxException { Properties p = Properties.loadProperties(getResource("/test-commentfirstline.properties")); assertThat(p.getComment("one")).containsExactly("#comment1", "# comment2"); StringWriter sw = new StringWriter(); p.store(sw); - assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-commentfirstline.properties"))); + assertThat(sw.toString()) + .isEqualTo(readAll(getResource("/test-commentfirstline.properties"))); } @Test @@ -229,6 +255,16 @@ void testSetComment() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-comment.properties"))); } + @Test + void testSetCommentNonExistent() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThatThrownBy( + () -> { + p.setComment("wrong", "dummy"); + }) + .isInstanceOf(NoSuchElementException.class); + } + @Test void testPut() throws IOException, URISyntaxException { Properties p = new Properties(); @@ -499,6 +535,12 @@ void testRemoveAll() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-removeall.properties"))); } + @Test + void testRemoveNonExistent() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test.properties")); + assertThat(p.remove("wrong")).isNull(); + } + @Test void testRemoveMiddleIterator() throws IOException, URISyntaxException { Properties p = Properties.loadProperties(getResource("/test.properties")); From 30003828afa71bc46e613e0828bb3c420375a326 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sun, 19 Mar 2023 23:55:16 +0100 Subject: [PATCH 13/37] docs: updated README.md with more info about comments Clarified how comments work and mentioned that there are actually two different comment characters. Added that to the example as well. --- README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa93a91..5d55f55 100644 --- a/README.md +++ b/README.md @@ -97,11 +97,27 @@ Retrieving values is simple: ```java p.get("port"); // Returns "8080" p.getProperty("port"); // Also returns "8080" -p.getComment("port") // Returns ["Port number to", "use for the server"] +p.getComment("port") // Returns ["# Port number to", "# use for the server"] ``` ### Comments +Just like with the original `Properties` implementation, lines starting with a +`#` or a `!` are considered comments. Consecutive comments lines that start +with the same comment character and have no other lines in between (not even +empty lines) are considered a single multi-line comment. + +```properties +# A single comment line + +! A multi-line comment +! spanning two lines + +# This is actually a single comment line +! And this is a single comment line too +two=Second value +``` + Comments are considered either "free" or "attached", which you could see as either being just part of the file or attached to a property. For example: @@ -110,7 +126,7 @@ just part of the file or attached to a property. For example: one=First value (that has no comment) -# Another free comment +! Another free comment # An attached comment two=Second value From 86f4a85bebad5532968e3d5b06883d76f68c672c Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Sun, 19 Mar 2023 23:56:28 +0100 Subject: [PATCH 14/37] docs: updated version info to maven and gradle usages --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d55f55..a3a75e8 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,14 @@ Add it to your build file with: org.codejive java-properties - 0.0.2 + 0.0.6 ``` or ```groovy -implementation 'org.codejive:java-properties:0.0.2' +implementation 'org.codejive:java-properties:0.0.6' ``` And add this import to your code: From 5e5162f6722fea237c53d3b0b0b3ba4824778b40 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 21 Feb 2024 11:02:27 +0100 Subject: [PATCH 15/37] test: added tests for delimiter bugs Lines that either have no delimiter or that have multiple delimters are not handled correctly. This commit adds tests for these cases. See #20 --- .../codejive/properties/TestProperties.java | 20 +++++++++++++++++++ .../properties/TestPropertiesParser.java | 10 ++++++++++ .../resources/test-missingdelim.properties | 1 + src/test/resources/test-multidelim.properties | 1 + 4 files changed, 32 insertions(+) create mode 100644 src/test/resources/test-missingdelim.properties create mode 100644 src/test/resources/test-multidelim.properties diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index c9f608f..4c51c72 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -671,6 +671,26 @@ void testCursor() throws IOException, URISyntaxException { assertThat(c.prevCount(t -> true)).isEqualTo(p.last().position() + 1); } + @Test + void testMissingDelim() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test-missingdelim.properties")); + assertThat(p).containsOnlyKeys("A-string-without-delimiter"); + assertThat(p).containsEntry("A-string-without-delimiter", ""); + StringWriter sw = new StringWriter(); + p.store(sw); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-missingdelim.properties"))); + } + + @Test + void testMultiDelim() throws IOException, URISyntaxException { + Properties p = Properties.loadProperties(getResource("/test-multidelim.properties")); + assertThat(p).containsOnlyKeys("key"); + assertThat(p).containsEntry("key", "==value"); + StringWriter sw = new StringWriter(); + p.store(sw); + assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-multidelim.properties"))); + } + private Path getResource(String name) throws URISyntaxException { return Paths.get(getClass().getResource(name).toURI()); } diff --git a/src/test/java/org/codejive/properties/TestPropertiesParser.java b/src/test/java/org/codejive/properties/TestPropertiesParser.java index cf31786..d2f719b 100644 --- a/src/test/java/org/codejive/properties/TestPropertiesParser.java +++ b/src/test/java/org/codejive/properties/TestPropertiesParser.java @@ -30,6 +30,8 @@ public class TestPropertiesParser { + " two \\\r\n" + "\tthree\n" + "key.4 = \\u1234\r\n" + + "line-with-missing-delim \n" + + "multidelim===value\n" + " # final comment"; @Test @@ -81,6 +83,14 @@ void testTokens() throws IOException { new Token(Type.SEPARATOR, " = "), new Token(Type.VALUE, "\\u1234", "\u1234"), new Token(Type.WHITESPACE, "\r\n"), + new Token(Type.KEY, "line-with-missing-delim"), + new Token(Type.SEPARATOR, " "), + new Token(Type.VALUE, ""), + new Token(Type.WHITESPACE, "\n"), + new Token(Type.KEY, "multidelim"), + new Token(Type.SEPARATOR, "="), + new Token(Type.VALUE, "==value"), + new Token(Type.WHITESPACE, "\n"), new Token(Type.WHITESPACE, " "), new Token(Type.COMMENT, "# final comment")); } diff --git a/src/test/resources/test-missingdelim.properties b/src/test/resources/test-missingdelim.properties new file mode 100644 index 0000000..32172df --- /dev/null +++ b/src/test/resources/test-missingdelim.properties @@ -0,0 +1 @@ +A-string-without-delimiter diff --git a/src/test/resources/test-multidelim.properties b/src/test/resources/test-multidelim.properties new file mode 100644 index 0000000..b95aa30 --- /dev/null +++ b/src/test/resources/test-multidelim.properties @@ -0,0 +1 @@ +key===value From 1c63d7297f7a8e9de29c0013d16d3d979e703be1 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 21 Feb 2024 13:49:33 +0100 Subject: [PATCH 16/37] fix: fixes problems with missing or multiple delimiters A missing delimiter would cause the parser to hang in a loop and cause a OOM error, while multiple delimiters would cause the parser to incorrectly parse the line, Fixes #20 --- .../codejive/properties/PropertiesParser.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/codejive/properties/PropertiesParser.java b/src/main/java/org/codejive/properties/PropertiesParser.java index c5f73bc..aada459 100644 --- a/src/main/java/org/codejive/properties/PropertiesParser.java +++ b/src/main/java/org/codejive/properties/PropertiesParser.java @@ -4,8 +4,10 @@ import java.io.Reader; import java.util.Objects; import java.util.Spliterators; -import java.util.function.BiFunction; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -211,31 +213,36 @@ public Token nextToken() throws IOException { if (isEof(ch)) { return null; } - int oldch = -1; - BiFunction isValid = (c, oldc) -> false; + Function isValid = (c) -> false; Type nextState = null; if (state == null) { if (isCommentChar(ch)) { state = Type.COMMENT; - isValid = (c, oldc) -> !isEol(c) && !isEof(c); - } else if (isWhitespaceChar(ch)) { + isValid = (c) -> !isEol(c) && !isEof(c); + } else if (isWhitespaceEolChar(ch)) { state = Type.WHITESPACE; - isValid = (c, oldc) -> isWhitespaceChar(c) && !isEol(oldc); + final AtomicInteger oldc = new AtomicInteger(-1); + isValid = (c) -> isWhitespaceEolChar(c) && !isEol(oldc.getAndSet(c)); } else { state = Type.KEY; - isValid = (c, oldc) -> !isSeparatorChar(c); + isValid = + (c) -> + !isSeparatorChar(c) + && !isWhitespaceChar(c) + && !isEol(c) + && !isEof(c); nextState = Type.SEPARATOR; } } else if (state == Type.SEPARATOR) { - isValid = (c, oldc) -> isSeparatorChar(c); + final AtomicBoolean once = new AtomicBoolean(true); + isValid = (c) -> isWhitespaceChar(c) || (isSeparatorChar(c) && once.getAndSet(false)); nextState = Type.VALUE; } else if (state == Type.VALUE) { - isValid = (c, oldc) -> !isEol(c) && !isEof(c); + isValid = (c) -> !isEol(c) && !isEof(c); } while (true) { - if (isValid.apply(ch, oldch)) { + if (isValid.apply(ch)) { addChar(readChar()); - oldch = ch; ch = peekChar(); } else { String text = string(); @@ -336,8 +343,7 @@ static String unescape(String escape) { case '\n': // Skip any leading whitespace while (i < (escape.length() - 1) - && isWhitespaceChar(ch = escape.charAt(i + 1)) - && !isEol(ch)) { + && isWhitespaceChar(ch = escape.charAt(i + 1))) { i++; } break; @@ -353,11 +359,15 @@ && isWhitespaceChar(ch = escape.charAt(i + 1)) } private static boolean isSeparatorChar(int ch) { - return ch == ' ' || ch == '\t' || ch == '=' || ch == ':'; + return ch == '=' || ch == ':'; } private static boolean isWhitespaceChar(int ch) { - return ch == ' ' || ch == '\t' || ch == '\f' || isEol(ch); + return ch == ' ' || ch == '\t' || ch == '\f'; + } + + private static boolean isWhitespaceEolChar(int ch) { + return isWhitespaceChar(ch) || isEol(ch); } private static boolean isCommentChar(int ch) { From 7aa0ea4102cb9ee96c29293370505d6daccb0acf Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 21 Feb 2024 14:14:04 +0100 Subject: [PATCH 17/37] [maven-release-plugin] prepare release v0.0.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7630b44..a0e86f2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.7-SNAPSHOT + 0.0.7 Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - HEAD + v0.0.7 From b80acac828d648769b65d712a8770927b5a30e4a Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 3 Jul 2024 23:27:40 +0200 Subject: [PATCH 18/37] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a0e86f2..f87cfa6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.codejive java-properties - 0.0.7 + 0.0.8-SNAPSHOT Java Properties parser A simple Java Properties parser that retains the exact format of the input file, including any comments @@ -174,7 +174,7 @@ scm:git:git://github.com/codejive/java-properties.git scm:git:git@github.com:codejive/java-properties.git http://github.com/codejive/java-properties - v0.0.7 + HEAD From c360eba020de62e1e32557e0467c92b8af8cb461 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 21 Feb 2024 15:05:28 +0100 Subject: [PATCH 19/37] docs: fix error in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3a75e8..e992a6b 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ implementation 'org.codejive:java-properties:0.0.6' And add this import to your code: ```java -import org.codejive.Properties; +import org.codejive.properties.Properties; ``` ### Usage From 256287d5c522770ccee41522b3a2b8df2366df05 Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Wed, 21 Feb 2024 15:05:54 +0100 Subject: [PATCH 20/37] docs: updated README to reflect actual implementation The explanation of comment blocks in tre README didn't match the actual implementation, it now does. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e992a6b..53cab6f 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ p.getComment("port") // Returns ["# Port number to", "# use for the server"] ### Comments Just like with the original `Properties` implementation, lines starting with a -`#` or a `!` are considered comments. Consecutive comments lines that start -with the same comment character and have no other lines in between (not even -empty lines) are considered a single multi-line comment. +`#` or a `!` are considered comments. Consecutive comments lines (even the ones +that start with another comment character) and have no other lines in between +(not even empty lines) are considered a single multi-line comment. ```properties # A single comment line @@ -113,8 +113,8 @@ empty lines) are considered a single multi-line comment. ! A multi-line comment ! spanning two lines -# This is actually a single comment line -! And this is a single comment line too +# This is also a multi-line comment +! but using different comment chars two=Second value ``` From 2fd84688a04ff453569e991504ecd0a34fe7841b Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Jul 2024 12:20:03 -0300 Subject: [PATCH 21/37] Introduce putAll(Properties) - This is useful when you need to copy all the values from a `java.util.Properties` to this structure --- .../java/org/codejive/properties/Properties.java | 12 ++++++++++++ .../java/org/codejive/properties/TestProperties.java | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 2544a33..3f28fa9 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -773,6 +773,18 @@ public List next() { Spliterators.spliterator(iter, tokens.size(), Spliterator.SORTED), false); } + /** + * Copies all entries from the java.util.Properties object to this object + * + * @param properties a java.util.Properties object + * @throws NullPointerException if the properties parameter is null + */ + public void putAll(java.util.Properties properties) { + for (Entry entry : properties.entrySet()) { + put(entry.getKey().toString(), entry.getValue().toString()); + } + } + /** * Returns a java.util.Properties with the same contents as this object. The * information is a copy, changes to one Properties object will not affect the other. diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 4c51c72..43304e1 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -691,6 +691,15 @@ void testMultiDelim() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(getResource("/test-multidelim.properties"))); } + @Test + void testPutAll() { + Properties p = new Properties(); + java.util.Properties ju = new java.util.Properties(); + ju.setProperty("foo", "bar"); + p.putAll(ju); + assertThat(p.getProperty("foo")).isEqualTo("bar"); + } + private Path getResource(String name) throws URISyntaxException { return Paths.get(getClass().getResource(name).toURI()); } From f03fe7e308dc169eb09f263486b901ea96800d01 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Jul 2024 22:57:14 -0300 Subject: [PATCH 22/37] Bump actions --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbe9fd8..3b6211d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'adopt' From 2d98a6467741a24ef23da0bc32667fdd97368a8a Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Jul 2024 20:44:29 -0300 Subject: [PATCH 23/37] Fix `Properties.store(OutputStream os, String comment)` This fixes the store method taking an `OutputStream` by flushing and closing the writer created to wrap it. --- pom.xml | 2 +- src/main/java/org/codejive/properties/Properties.java | 5 ++++- .../java/org/codejive/properties/TestProperties.java | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f87cfa6..829a016 100644 --- a/pom.xml +++ b/pom.xml @@ -187,4 +187,4 @@ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - \ No newline at end of file + diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 3f28fa9..5b2e9e5 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -910,7 +910,9 @@ public void store(Path file, String... comment) throws IOException { * @throws IOException Thrown when any IO error occurs during operation */ public void store(OutputStream out, String... comment) throws IOException { - store(new OutputStreamWriter(out, StandardCharsets.ISO_8859_1), comment); + store( + new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.ISO_8859_1)), + comment); } /** @@ -938,6 +940,7 @@ public void store(Writer writer, String... comment) throws IOException { writer.write(pos.raw()); pos.next(); } + writer.flush(); } /** diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 43304e1..c74449b 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -118,6 +118,15 @@ void testStore() throws IOException, URISyntaxException { assertThat(sw.toString()).isEqualTo(readAll(f)); } + @Test + void testStoreOutputStream() throws IOException, URISyntaxException { + Path f = getResource("/test-escaped.properties"); + Properties p = Properties.loadProperties(f); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + p.store(os); + assertThat(os.toString()).isEqualTo(readAll(f)); + } + @Test void testStoreCrLf() throws IOException, URISyntaxException { Path f = getResource("/testcrlf.properties"); From 6d846f134a50cff7c879cb2325ef176685fc2973 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 10:11:24 +0000 Subject: [PATCH 24/37] Add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} From 9ea16f9349279433535dcbdfd4fcf373449e7fd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 10:12:32 +0000 Subject: [PATCH 25/37] chore(deps): update dependency maven to v3.9.9 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 642d572..9b66d8c 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar From 9a8ed9105a7ad4441e0cad4721fd59489e4ead61 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 16:27:35 +0000 Subject: [PATCH 26/37] chore(deps): update dependency org.apache.maven.plugins:maven-gpg-plugin to v1.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 829a016..f96006a 100644 --- a/pom.xml +++ b/pom.xml @@ -154,7 +154,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts From e3e15f9baf378627a0a6422ca357c03f48a369cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 16:27:38 +0000 Subject: [PATCH 27/37] chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v2.10.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f96006a..de7a2f7 100644 --- a/pom.xml +++ b/pom.xml @@ -141,7 +141,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 2.10.4 attach-javadocs From 2afa4884f596c65cf0175dbce322c7658559296b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 17:15:46 +0000 Subject: [PATCH 28/37] chore(deps): update dependency org.apache.maven.plugins:maven-source-plugin to v2.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index de7a2f7..094c3fe 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 2.4 attach-sources From d61edcd76a139a404aab180e1bde6366dd79bf57 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 17:15:50 +0000 Subject: [PATCH 29/37] chore(deps): update dependency org.assertj:assertj-core to v3.27.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 094c3fe..e7dd9d3 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 8 8 5.8.2 - 3.21.0 + 3.27.3 2.12.2 1.7 From 7986c22925f2a0ecc05c2bda4651ad71dde9f094 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 23:37:41 +0000 Subject: [PATCH 30/37] chore(deps): update dependency org.junit.jupiter:junit-jupiter to v5.12.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e7dd9d3..2a90145 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ UTF-8 8 8 - 5.8.2 + 5.12.2 3.27.3 2.12.2 1.7 From d6c3e70929e74ee1c034c944618c9bd0a4eb6112 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 23:37:45 +0000 Subject: [PATCH 31/37] chore(deps): update dependency org.sonatype.plugins:nexus-staging-maven-plugin to v1.7.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a90145..1ea9b80 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.7.0 true ossrh From ac4e3a1dad216bba501d5361ee851d1f8227c93f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 00:20:37 +0000 Subject: [PATCH 32/37] chore(deps): update dependency org.apache.maven.plugins:maven-gpg-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1ea9b80..3446ff2 100644 --- a/pom.xml +++ b/pom.xml @@ -154,7 +154,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.7 sign-artifacts From b22330b57402f4a3376e760a9544d00b625cc146 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 00:20:34 +0000 Subject: [PATCH 33/37] chore(deps): update dependency maven-wrapper to v3 --- .mvn/wrapper/maven-wrapper.properties | 19 +- mvnw | 451 ++++++++++++-------------- mvnw.cmd | 281 +++++++--------- 3 files changed, 342 insertions(+), 409 deletions(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 9b66d8c..d58dfb7 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/mvnw b/mvnw index 41c0f0c..19529dd 100755 --- a/mvnw +++ b/mvnw @@ -19,292 +19,241 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac -fi +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 fi fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" +} - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done + printf %x\\n $h +} - saveddir=`pwd` +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - M2_HOME=`dirname "$PRG"`/.. +die() { + printf %s\\n "$1" >&2 + exit 1 +} - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" fi -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; fi -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 8611571..249bdf3 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,3 +1,4 @@ +<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -18,165 +19,131 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM ---------------------------------------------------------------------------- -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) ) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" From 7a4667a094af4fc11e1c066442305db76495a770 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 05:38:45 +0000 Subject: [PATCH 34/37] chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3446ff2..21f67a1 100644 --- a/pom.xml +++ b/pom.xml @@ -141,7 +141,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.11.2 attach-javadocs From 64acc6e253310c242543ac67d322b8e99c60fad5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 05:38:48 +0000 Subject: [PATCH 35/37] chore(deps): update dependency org.apache.maven.plugins:maven-release-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 21f67a1..8034917 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.1.1 release v@{project.version} From e0b940a50acae3bd25bc5f513ad5ad60fccd40e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:45 +0000 Subject: [PATCH 36/37] chore(deps): update dependency org.apache.maven.plugins:maven-source-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8034917..3dff8ff 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ org.apache.maven.plugins maven-source-plugin - 2.4 + 3.3.1 attach-sources From fc2746cd0cc1b65719e154825c96e19d07b6b624 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:48 +0000 Subject: [PATCH 37/37] chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dff8ff..abd0fdc 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.5.3 org.sonatype.plugins