From 81bc67c5a7b94f82a7c781ea40f9ec82af8a3e67 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 5 Jun 2025 13:15:11 +0200 Subject: [PATCH 1/2] Refactor WrapperProperties template to remove caching and simplify implementation This commit refactors the WrapperProperties template used for Maven model properties: TEMPLATE CHANGES (src/mdo/java/WrapperProperties.java): - Removed caching mechanism (orderedProps field and ensureInitialized() method) - Simplified read operations to directly delegate to getter.get() - Introduced writeOperation() pattern for all write operations that: * Creates a fresh OrderedProperties from current state * Performs the operation on the copy * Only calls setter if changes were made - All write operations now use synchronized writeOperation() wrapper - Maintains insertion order through OrderedProperties inner class - Supports multiple wrapper instances sharing the same backend storage The new approach eliminates cache invalidation complexity while ensuring: - Thread safety through synchronized write operations - Insertion order preservation - Immediate visibility of changes across wrapper instances - Proper delegation to underlying storage SIDE EFFECTS: - Consolidated redundant test classes into single PropertiesTest - Removed PropertiesOrderTest and WrapperPropertiesOrderTest (functionality covered by PropertiesTest) - Cleaned up duplicate test methods in PropertiesTest This refactoring simplifies the WrapperProperties implementation while maintaining all required functionality for Maven model property management. --- .../maven/model/PropertiesOrderTest.java | 97 ------- .../apache/maven/model/PropertiesTest.java | 260 ++++++++++++++++++ .../model/WrapperPropertiesOrderTest.java | 63 ----- src/mdo/java/WrapperProperties.java | 117 ++++---- 4 files changed, 308 insertions(+), 229 deletions(-) delete mode 100644 impl/maven-core/src/test/java/org/apache/maven/model/PropertiesOrderTest.java create mode 100644 impl/maven-core/src/test/java/org/apache/maven/model/PropertiesTest.java delete mode 100644 impl/maven-core/src/test/java/org/apache/maven/model/WrapperPropertiesOrderTest.java diff --git a/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesOrderTest.java b/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesOrderTest.java deleted file mode 100644 index 2a0bd9e3babb..000000000000 --- a/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesOrderTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - */ -package org.apache.maven.model; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * Test for MNG-8746: Properties order is preserved in model - */ -public class PropertiesOrderTest { - - @Test - public void testPropertiesOrderPreservedInImmutableModel() { - // Create properties with specific insertion order using LinkedHashMap - Map orderedMap = new LinkedHashMap<>(); - orderedMap.put("third", "3"); - orderedMap.put("first", "1"); - orderedMap.put("second", "2"); - - // Create model and set properties - Model model = new Model(); - Properties props = model.getProperties(); - - // Create properties and populate from map to maintain order - orderedMap.forEach(props::setProperty); - - // Get the immutable delegate (v4 API model is already immutable) - org.apache.maven.api.model.Model immutable = model.getDelegate(); - - // Verify order is preserved - Map resultProps = immutable.getProperties(); - assertNotNull(resultProps); - - // Check order by collecting keys in iteration order - List keys = new ArrayList<>(resultProps.keySet()); - - // Verify the original insertion order is maintained - assertEquals(3, keys.size()); - assertEquals("third", keys.get(0)); - assertEquals("first", keys.get(1)); - assertEquals("second", keys.get(2)); - } - - @Test - public void testPropertiesOrderPreservedInMutableModel() { - // Create ordered map to simulate properties with specific order - Map orderedMap = new LinkedHashMap<>(); - orderedMap.put("z-property", "z"); - orderedMap.put("a-property", "a"); - orderedMap.put("m-property", "m"); - - // Create and populate model - Model model = new Model(); - Properties props = model.getProperties(); - - // Create properties and populate from map to maintain order - orderedMap.forEach(props::setProperty); - - // Get properties back and verify order - Properties resultProps = model.getProperties(); - - // Check order by collecting keys in iteration order - List keys = new ArrayList<>(); - resultProps.keySet().forEach(k -> keys.add(k.toString())); - - // Verify the original insertion order is maintained - assertEquals(3, keys.size()); - assertEquals("z-property", keys.get(0)); - assertEquals("a-property", keys.get(1)); - assertEquals("m-property", keys.get(2)); - } -} diff --git a/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesTest.java b/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesTest.java new file mode 100644 index 000000000000..978823d059cd --- /dev/null +++ b/impl/maven-core/src/test/java/org/apache/maven/model/PropertiesTest.java @@ -0,0 +1,260 @@ +/* + * 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. + */ +package org.apache.maven.model; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Comprehensive test suite for Properties behavior in Maven models. + * Tests order preservation, caching behavior, and WrapperProperties functionality. + */ +class PropertiesTest { + + @Nested + class OrderPreservationTests { + + @Test + void testPropertiesOrderPreservedInImmutableModel() { + // Create properties with specific insertion order using LinkedHashMap + Map orderedMap = new LinkedHashMap<>(); + orderedMap.put("third", "3"); + orderedMap.put("first", "1"); + orderedMap.put("second", "2"); + + // Create model and set properties + Model model = new Model(); + Properties props = model.getProperties(); + + // Create properties and populate from map to maintain order + orderedMap.forEach(props::setProperty); + + // Get the immutable delegate (v4 API model is already immutable) + org.apache.maven.api.model.Model immutable = model.getDelegate(); + + // Verify order is preserved + Map resultProps = immutable.getProperties(); + assertNotNull(resultProps); + + // Check order by collecting keys in iteration order + List keys = new ArrayList<>(resultProps.keySet()); + + // Verify the original insertion order is maintained + assertEquals(3, keys.size()); + assertEquals("third", keys.get(0)); + assertEquals("first", keys.get(1)); + assertEquals("second", keys.get(2)); + } + + @Test + void testPropertiesOrderPreservedInMutableModel() { + // Create ordered map to simulate properties with specific order + Map orderedMap = new LinkedHashMap<>(); + orderedMap.put("z-property", "z"); + orderedMap.put("a-property", "a"); + orderedMap.put("m-property", "m"); + + // Create and populate model + Model model = new Model(); + Properties props = model.getProperties(); + + // Create properties and populate from map to maintain order + orderedMap.forEach(props::setProperty); + + // Get properties back and verify order + Properties resultProps = model.getProperties(); + + // Check order by collecting keys in iteration order + List keys = new ArrayList<>(); + resultProps.keySet().forEach(k -> keys.add(k.toString())); + + // Verify the original insertion order is maintained + assertEquals(3, keys.size()); + assertEquals("z-property", keys.get(0)); + assertEquals("a-property", keys.get(1)); + assertEquals("m-property", keys.get(2)); + } + + @Test + void testOrderPreservationAfterModification() { + // Create a model with properties + Model model = new Model(); + Properties modelProps = model.getProperties(); + + // Add properties in specific order + modelProps.setProperty("first", "1"); + modelProps.setProperty("second", "2"); + + // Modify existing property + modelProps.setProperty("first", "modified"); + + // Add new property + modelProps.setProperty("third", "3"); + + // Collect keys in iteration order + List keys = new ArrayList<>(); + modelProps.keySet().forEach(k -> keys.add(k.toString())); + + // Verify order is preserved (first should still be first since it was modified, not re-added) + assertEquals(3, keys.size()); + assertEquals("first", keys.get(0)); + assertEquals("second", keys.get(1)); + assertEquals("third", keys.get(2)); + + // Verify value was updated + assertEquals("modified", modelProps.getProperty("first")); + } + } + + @Nested + class WrapperPropertiesBehaviorTests { + + @Test + void testWriteOperationBehavior() { + // Create a Model with initial properties + Model model = new Model(); + + // Set initial properties using setProperties to establish the backend + Properties initialProps = new Properties(); + initialProps.setProperty("initial.key", "initial.value"); + model.setProperties(initialProps); + + // Get the WrapperProperties instance + Properties wrapperProps = model.getProperties(); + + // First read - should initialize cache + assertEquals("initial.value", wrapperProps.getProperty("initial.key")); + + // Simulate external change by directly calling setProperties (another WrapperProperties instance) + Properties externalProps = new Properties(); + externalProps.setProperty("initial.key", "externally.modified"); + externalProps.setProperty("external.key", "external.value"); + model.setProperties(externalProps); + + // Read again - should return fresh value (no caching in current implementation) + assertEquals("externally.modified", wrapperProps.getProperty("initial.key")); + + // Now perform a write operation + wrapperProps.setProperty("new.key", "new.value"); + + // Read the initial key again - should return the current value + assertEquals("externally.modified", wrapperProps.getProperty("initial.key")); + + // Read the external key that was set before the write operation + assertEquals("external.value", wrapperProps.getProperty("external.key")); + + // Read the new key that was just set + assertEquals("new.value", wrapperProps.getProperty("new.key")); + } + + @Test + void testMultipleWrapperPropertiesShareSameBackend() { + // Create a Model with initial properties + Model model = new Model(); + + Properties initialProps = new Properties(); + initialProps.setProperty("shared.key", "initial.value"); + model.setProperties(initialProps); + + // Get two WrapperProperties instances from the same Model + Properties wrapper1 = model.getProperties(); + Properties wrapper2 = model.getProperties(); + + // Both wrappers should read the same initial value + assertEquals("initial.value", wrapper1.getProperty("shared.key")); + assertEquals("initial.value", wrapper2.getProperty("shared.key")); + + // Write through wrapper1 + wrapper1.setProperty("from.wrapper1", "value1"); + + // wrapper2 should see the changes immediately (no caching) + assertEquals("value1", wrapper2.getProperty("from.wrapper1")); + assertEquals("initial.value", wrapper2.getProperty("shared.key")); + + // Now wrapper2 performs a write operation + wrapper2.setProperty("from.wrapper2", "value2"); + + // Both wrappers should see all changes immediately + assertEquals("value1", wrapper1.getProperty("from.wrapper1")); + assertEquals("value2", wrapper1.getProperty("from.wrapper2")); + assertEquals("value1", wrapper2.getProperty("from.wrapper1")); + assertEquals("value2", wrapper2.getProperty("from.wrapper2")); + + // Add another property through wrapper1 + wrapper1.setProperty("another.key", "another.value"); + assertEquals("another.value", wrapper1.getProperty("another.key")); + assertEquals("another.value", wrapper2.getProperty("another.key")); + } + + @Test + void testVariousWriteOperations() { + // Create a Model with initial properties + Model model = new Model(); + + Properties initialProps = new Properties(); + initialProps.setProperty("key1", "value1"); + model.setProperties(initialProps); + + Properties wrapper = model.getProperties(); + + // Initial read + assertEquals("value1", wrapper.getProperty("key1")); + + // Test put() method + wrapper.put("key2", "value2"); + assertEquals("value2", wrapper.getProperty("key2")); + + // Simulate external change + Properties externalProps1 = new Properties(); + externalProps1.setProperty("key1", "modified_after_put"); + externalProps1.setProperty("key2", "value2"); + externalProps1.setProperty("external.key", "external.value"); + model.setProperties(externalProps1); + assertEquals("modified_after_put", wrapper.getProperty("key1")); + + // Test remove() method + wrapper.remove("key2"); + assertEquals(null, wrapper.getProperty("key2")); + + // Simulate external change + Properties externalProps2 = new Properties(); + externalProps2.setProperty("key1", "modified_after_remove"); + externalProps2.setProperty("external.key", "external.value"); + model.setProperties(externalProps2); + assertEquals("modified_after_remove", wrapper.getProperty("key1")); + + // Test putAll() method + Properties newProps = new Properties(); + newProps.setProperty("putall.key1", "putall.value1"); + newProps.setProperty("putall.key2", "putall.value2"); + wrapper.putAll(newProps); + assertEquals("putall.value1", wrapper.getProperty("putall.key1")); + assertEquals("putall.value2", wrapper.getProperty("putall.key2")); + } + } +} diff --git a/impl/maven-core/src/test/java/org/apache/maven/model/WrapperPropertiesOrderTest.java b/impl/maven-core/src/test/java/org/apache/maven/model/WrapperPropertiesOrderTest.java deleted file mode 100644 index 34c87bedfa10..000000000000 --- a/impl/maven-core/src/test/java/org/apache/maven/model/WrapperPropertiesOrderTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ -package org.apache.maven.model; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Test for order preservation in WrapperProperties used by Model - */ -public class WrapperPropertiesOrderTest { - - @Test - public void testOrderPreservationAfterModification() { - // Create a model with properties - Model model = new Model(); - Properties modelProps = model.getProperties(); - - // Add properties in specific order - modelProps.setProperty("first", "1"); - modelProps.setProperty("second", "2"); - - // Modify existing property - modelProps.setProperty("first", "modified"); - - // Add new property - modelProps.setProperty("third", "3"); - - // Collect keys in iteration order - List keys = new ArrayList<>(); - modelProps.keySet().forEach(k -> keys.add(k.toString())); - - // Verify order is preserved (first should still be first since it was modified, not re-added) - assertEquals(3, keys.size()); - assertEquals("first", keys.get(0)); - assertEquals("second", keys.get(1)); - assertEquals("third", keys.get(2)); - - // Verify value was updated - assertEquals("modified", modelProps.getProperty("first")); - } -} diff --git a/src/mdo/java/WrapperProperties.java b/src/mdo/java/WrapperProperties.java index 8e1af9a71678..84907cfc9467 100644 --- a/src/mdo/java/WrapperProperties.java +++ b/src/mdo/java/WrapperProperties.java @@ -50,43 +50,30 @@ class WrapperProperties extends Properties { final Supplier> getter; final Consumer setter; - private final OrderedProperties orderedProps = new OrderedProperties(); - private boolean initialized; WrapperProperties(Supplier> getter, Consumer setter) { this.getter = getter; this.setter = setter; } - private synchronized void ensureInitialized() { - if (!initialized) { - orderedProps.putAll(getter.get()); - initialized = true; - } - } - @Override public String getProperty(String key) { - ensureInitialized(); - return orderedProps.getProperty(key); + return getter.get().get(key); } @Override public String getProperty(String key, String defaultValue) { - ensureInitialized(); - return orderedProps.getProperty(key, defaultValue); + return getter.get().getOrDefault(key, defaultValue); } @Override public Enumeration propertyNames() { - ensureInitialized(); - return orderedProps.propertyNames(); + return Collections.enumeration(getter.get().keySet()); } @Override public Set stringPropertyNames() { - ensureInitialized(); - return orderedProps.stringPropertyNames(); + return getter.get().keySet(); } @Override @@ -101,102 +88,89 @@ public void list(PrintWriter out) { @Override public int size() { - ensureInitialized(); - return orderedProps.size(); + return getter.get().size(); } @Override public boolean isEmpty() { - ensureInitialized(); - return orderedProps.isEmpty(); + return getter.get().isEmpty(); } @Override public Enumeration keys() { - ensureInitialized(); - return orderedProps.keys(); + return Collections.enumeration((Set) getter.get().keySet()); } @Override public Enumeration elements() { - ensureInitialized(); - return orderedProps.elements(); + return Collections.enumeration((Collection) getter.get().values()); } @Override public boolean contains(Object value) { - ensureInitialized(); - return orderedProps.contains(value); + return getter.get().containsKey(value != null ? value.toString() : null); } @Override public boolean containsValue(Object value) { - ensureInitialized(); - return orderedProps.containsValue(value); + return getter.get().containsValue(value); } @Override public boolean containsKey(Object key) { - ensureInitialized(); - return orderedProps.containsKey(key); + return getter.get().containsKey(key); } @Override public Object get(Object key) { - ensureInitialized(); - return orderedProps.get(key); + return getter.get().get(key); } @Override public synchronized String toString() { - ensureInitialized(); - return orderedProps.toString(); + return getter.get().toString(); } @Override public Set keySet() { - ensureInitialized(); - return orderedProps.keySet(); + return new OrderedProperties(getter.get()).keySet(); } @Override public Collection values() { - ensureInitialized(); - return orderedProps.values(); + return new OrderedProperties(getter.get()).values(); } @Override public Set> entrySet() { - ensureInitialized(); - return orderedProps.entrySet(); + return new OrderedProperties(getter.get()).entrySet(); } @Override public synchronized boolean equals(Object o) { - ensureInitialized(); if (o instanceof WrapperProperties wrapperProperties) { - wrapperProperties.ensureInitialized(); - return orderedProps.equals(wrapperProperties.orderedProps); + o = wrapperProperties.getter.get(); } - return orderedProps.equals(o); + return getter.get().equals(o); } @Override public synchronized int hashCode() { - ensureInitialized(); - return orderedProps.hashCode(); + return getter.get().hashCode(); } @Override public Object getOrDefault(Object key, Object defaultValue) { - ensureInitialized(); - return orderedProps.getOrDefault(key, defaultValue); + if (key instanceof String str && defaultValue instanceof String def) { + return getter.get().getOrDefault(key, def); + } else { + return defaultValue; + } } @Override public synchronized void forEach(BiConsumer action) { - ensureInitialized(); - orderedProps.forEach(action); + getter.get().forEach(action); } interface WriteOp { @@ -208,16 +182,20 @@ interface WriteOpVoid { } private T writeOperation(WriteOp runner) { - ensureInitialized(); - T ret = runner.perform(orderedProps); - setter.accept(orderedProps); + OrderedProperties props = new OrderedProperties(getter.get()); + T ret = runner.perform(props); + if (!props.equals(getter.get())) { + setter.accept(props); + } return ret; } private void writeOperationVoid(WriteOpVoid runner) { - ensureInitialized(); - runner.perform(orderedProps); - setter.accept(orderedProps); + OrderedProperties props = new OrderedProperties(getter.get()); + runner.perform(props); + if (!props.equals(getter.get())) { + setter.accept(props); + } } @Override @@ -324,22 +302,19 @@ public synchronized void load(InputStream inStream) throws IOException { @Override public void save(OutputStream out, String comments) { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); props.save(out, comments); } @Override public void store(Writer writer, String comments) throws IOException { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); props.store(writer, comments); } @Override public void store(OutputStream out, String comments) throws IOException { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); props.store(out, comments); } @@ -350,28 +325,32 @@ public synchronized void loadFromXML(InputStream in) throws IOException, Invalid @Override public void storeToXML(OutputStream os, String comment) throws IOException { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); props.storeToXML(os, comment); } @Override public void storeToXML(OutputStream os, String comment, String encoding) throws IOException { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); props.storeToXML(os, comment, encoding); } private Object writeReplace() throws java.io.ObjectStreamException { - Properties props = new Properties(); - props.putAll(getter.get()); + OrderedProperties props = new OrderedProperties(getter.get()); return props; } private class OrderedProperties extends Properties { private final List keyOrder = new CopyOnWriteArrayList<>(); + public OrderedProperties() { + } + + public OrderedProperties(Map map) { + putAll(map); + } + @Override public synchronized void putAll(Map t) { t.forEach(this::put); From 2a68134f458ed4d20888ea4cd7e67088d71ae216 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 5 Jun 2025 15:55:20 +0200 Subject: [PATCH 2/2] Fix after review --- src/mdo/java/WrapperProperties.java | 32 ++++++++++------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/mdo/java/WrapperProperties.java b/src/mdo/java/WrapperProperties.java index 84907cfc9467..f285ec0666f2 100644 --- a/src/mdo/java/WrapperProperties.java +++ b/src/mdo/java/WrapperProperties.java @@ -191,11 +191,10 @@ private T writeOperation(WriteOp runner) { } private void writeOperationVoid(WriteOpVoid runner) { - OrderedProperties props = new OrderedProperties(getter.get()); - runner.perform(props); - if (!props.equals(getter.get())) { - setter.accept(props); - } + writeOperation(properties -> { + runner.perform(properties); + return null; + }); } @Override @@ -302,20 +301,17 @@ public synchronized void load(InputStream inStream) throws IOException { @Override public void save(OutputStream out, String comments) { - OrderedProperties props = new OrderedProperties(getter.get()); - props.save(out, comments); + new OrderedProperties(getter.get()).save(out, comments); } @Override public void store(Writer writer, String comments) throws IOException { - OrderedProperties props = new OrderedProperties(getter.get()); - props.store(writer, comments); + new OrderedProperties(getter.get()).store(writer, comments); } @Override public void store(OutputStream out, String comments) throws IOException { - OrderedProperties props = new OrderedProperties(getter.get()); - props.store(out, comments); + new OrderedProperties(getter.get()).store(out, comments); } @Override @@ -325,29 +321,23 @@ public synchronized void loadFromXML(InputStream in) throws IOException, Invalid @Override public void storeToXML(OutputStream os, String comment) throws IOException { - OrderedProperties props = new OrderedProperties(getter.get()); - props.storeToXML(os, comment); + new OrderedProperties(getter.get()).storeToXML(os, comment); } @Override public void storeToXML(OutputStream os, String comment, String encoding) throws IOException { - OrderedProperties props = new OrderedProperties(getter.get()); - props.storeToXML(os, comment, encoding); + new OrderedProperties(getter.get()).storeToXML(os, comment, encoding); } private Object writeReplace() throws java.io.ObjectStreamException { - OrderedProperties props = new OrderedProperties(getter.get()); - return props; + return new OrderedProperties(getter.get()); } private class OrderedProperties extends Properties { private final List keyOrder = new CopyOnWriteArrayList<>(); - public OrderedProperties() { - } - - public OrderedProperties(Map map) { + OrderedProperties(Map map) { putAll(map); }