Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

package com.mirth.connect.donkey.model.message;

import java.util.HashMap;
import java.util.TreeMap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since a TreeMap is sorted and a HashMap is not, I'm guessing there could be considerable changes compared to a prior export the first time an export is done with the new code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, then subsequent exports will benefit.

Copy link
Member

@tonygermano tonygermano Jun 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a purpose for having MapContent as part of this PR? I think it only shows up in a message export, and not part of the server configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll revert it and verify.

import java.util.Map;

public class MapContent extends Content {
private Object content = new HashMap<String, Object>();
private Object content = new TreeMap<String, Object>();
private transient boolean persisted = false;

public MapContent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

package com.mirth.connect.donkey.util;

import java.util.HashMap;
import java.util.TreeMap;
import java.util.Map;
import java.util.Map.Entry;

Expand Down Expand Up @@ -61,7 +61,7 @@ public static String serializeMap(Serializer serializer, Map<String, Object> map
try {
return serializer.serialize(map);
} catch (Exception e) {
Map<String, Object> newMap = new HashMap<String, Object>();
Map<String, Object> newMap = new TreeMap<String, Object>();

for (Entry<String, Object> entry : map.entrySet()) {
Comment on lines 62 to 66
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes aim to make map serialization deterministic by using TreeMap, but existing MapUtil tests don’t assert ordering. To prevent regressions (and to validate the PR’s main goal), add a unit test that serializes a map with out-of-order keys and verifies the resulting XML entry order is consistent/sorted for the TreeMap-backed paths (both the fallback re-serialization path and deserializeMapWithInvalidValues).

Copilot uses AI. Check for mistakes.
Object value = entry.getValue();
Expand Down Expand Up @@ -108,7 +108,7 @@ public static Map<String, Object> deserializeMapWithInvalidValues(Serializer ser
* If an exception occurs while deserializing, we build up a new map manually, attempting to
* deserialize each entry and replacing entries that fail with their string representations.
*/
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new TreeMap<String, Object>();

for (DonkeyElement entry : mapElement.getChildElements()) {
if (!entry.getNodeName().equalsIgnoreCase("entry")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Set;

Expand All @@ -25,7 +25,7 @@ public CodeTemplateContextSet(ContextType... contextTypes) {
}

public CodeTemplateContextSet(Collection<ContextType> contextTypes) {
delegate = new HashSet<ContextType>(contextTypes);
delegate = new TreeSet<ContextType>(contextTypes);
}

public CodeTemplateContextSet addContext(ContextType... contextTypes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -46,8 +46,8 @@ public class CodeTemplateLibrary implements Serializable, Migratable, Purgable,

public CodeTemplateLibrary() {
id = UUID.randomUUID().toString();
enabledChannelIds = new HashSet<String>();
disabledChannelIds = new HashSet<String>();
enabledChannelIds = new TreeSet<String>();
disabledChannelIds = new TreeSet<String>();
codeTemplates = new ArrayList<CodeTemplate>();
}
Comment on lines 47 to 52
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor now initializes enabled/disabledChannelIds as TreeSet for deterministic ordering, but the setters still accept and assign arbitrary Set implementations. There are call sites that pass HashSet (e.g., client CodeTemplatePanel, server ChannelServletTest), which will reintroduce non-deterministic export order and undermine the purpose of this change. Consider defensively copying in the setters (e.g., wrap in a new TreeSet) so CodeTemplateLibrary always holds a sorted set regardless of how it’s populated (including after deserialization or UI updates).

Copilot uses AI. Check for mistakes.

Expand All @@ -58,8 +58,8 @@ public CodeTemplateLibrary(CodeTemplateLibrary library) {
lastModified = library.getLastModified();
description = library.getDescription();
includeNewChannels = library.isIncludeNewChannels();
enabledChannelIds = new HashSet<String>(library.getEnabledChannelIds());
disabledChannelIds = new HashSet<String>(library.getDisabledChannelIds());
enabledChannelIds = new TreeSet<String>(library.getEnabledChannelIds());
disabledChannelIds = new TreeSet<String>(library.getDisabledChannelIds());
codeTemplates = new ArrayList<CodeTemplate>();
if (CollectionUtils.isNotEmpty(library.getCodeTemplates())) {
for (CodeTemplate codeTemplate : library.getCodeTemplates()) {
Expand Down
Loading