From 474f8004de4f28b5bec2071a188bd2ec2408e7ee Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Thu, 1 Oct 2026 20:12:19 +0200 Subject: [PATCH] fix: keep trailing comments separate from appended properties Fixes #90 --- .../org/codejive/properties/Properties.java | 21 +++++++++++++++++++ .../codejive/properties/TestProperties.java | 18 ++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/main/java/org/codejive/properties/Properties.java b/src/main/java/org/codejive/properties/Properties.java index 27ba332..6916b9b 100644 --- a/src/main/java/org/codejive/properties/Properties.java +++ b/src/main/java/org/codejive/properties/Properties.java @@ -513,9 +513,16 @@ private Cursor addNewKeyValue( pos = afterLastProperty(); } + boolean afterFinalComment = isAfterFinalComment(pos); + // Add a newline whitespace token if necessary pos = prepareNewLine(pos); + if (afterFinalComment) { + // Keep a trailing comment free rather than attaching it to the new key. + pos.addEol(eolType); + } + // Add tokens for key, separator and value pos.add(new PropertiesParser.Token(PropertiesParser.Type.KEY, rawKey, key)); pos.add(new PropertiesParser.Token(PropertiesParser.Type.SEPARATOR, "=")); @@ -523,6 +530,20 @@ private Cursor addNewKeyValue( return pos; } + // Returns true only at the end of the token list when the last token is a comment, + // optionally followed by one EOL. An existing blank line after the comment returns false. + // Uses a copy so the insertion cursor is not moved. + private boolean isAfterFinalComment(Cursor pos) { + if (!pos.atEnd()) { + return false; + } + Cursor previous = pos.copy().prev(); + if (previous.isEol()) { + previous.prev(); + } + return previous.isType(PropertiesParser.Type.COMMENT); + } + // Prepare the list for adding a new line at the given position. It will make sure the cursor // is at a valid insertion point and that any required EOLs are added. private Cursor prepareNewLine(Cursor pos) { diff --git a/src/test/java/org/codejive/properties/TestProperties.java b/src/test/java/org/codejive/properties/TestProperties.java index 9ed59e5..989d325 100644 --- a/src/test/java/org/codejive/properties/TestProperties.java +++ b/src/test/java/org/codejive/properties/TestProperties.java @@ -1394,6 +1394,24 @@ void testPutFirstLineHeaderOnlyCrLf() throws IOException { expectStoreText(p, "put=putVal\r\n # header\r\n"); } + @Test + void testPutLastLine() throws IOException { + final String given = "key=val\n" + "# trailer comment"; + final String expected = "key=val\n" + "# trailer comment\n" + "\n" + "put=putVal"; + Properties p = Properties.loadProperties(new StringReader(given)); + p.put(p.last().next(), "put", "putVal"); + expectStoreText(p, expected); + assertThat(p.getComment("put")).isEmpty(); + } + + @Test + void testPutLastLineWithFinalEol() throws IOException { + Properties p = Properties.loadProperties(new StringReader("key=val\r\n # trailer\r\n")); + p.put(p.last().next(), "put", "putVal"); + expectStoreText(p, "key=val\r\n # trailer\r\n\r\nput=putVal\r\n"); + assertThat(p.getComment("put")).isEmpty(); + } + private void expectStoreText(Properties props, String expectedText) throws IOException { StringWriter sw = new StringWriter(); props.store(sw);