From e481a730b2f2773a49c7bbc6bffb94c86b424475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 22 Feb 2024 07:01:08 +0100 Subject: [PATCH] Fix ModuleCapability/ModuleRequirement hashcode/equals according to API The API of Capability/Requirement currently defines the contract to be: This Capability/Requirement is equal to another Capability/Requirement if they have the same namespace, directives and attributes and are declared by the same resource. But Equinox currently implements hashCode/equals in terms of object identity. --- .../osgi/container/ModuleCapability.java | 25 +++++++++++++++++++ .../osgi/container/ModuleDatabase.java | 3 ++- .../osgi/container/ModuleRequirement.java | 25 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleCapability.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleCapability.java index 7cbb36009a7..f19a4f66576 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleCapability.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleCapability.java @@ -16,8 +16,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.osgi.framework.namespace.NativeNamespace; import org.osgi.framework.wiring.BundleCapability; +import org.osgi.resource.Capability; /** * An implementation of {@link BundleCapability}. @@ -30,6 +32,7 @@ public final class ModuleCapability implements BundleCapability { private final Map attributes; private final Map transientAttrs; private final ModuleRevision revision; + private transient int hashCode; ModuleCapability(String namespace, Map directives, Map attributes, ModuleRevision revision) { @@ -93,4 +96,26 @@ public ModuleRevision getResource() { public String toString() { return namespace + ModuleContainer.toString(attributes, false) + ModuleContainer.toString(directives, true); } + + @Override + public int hashCode() { + if (hashCode != 0) { + return hashCode; + } + return hashCode = Objects.hash(namespace, directives, attributes, revision); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Capability) { + Capability other = (Capability) obj; + return Objects.equals(revision, other.getResource()) && Objects.equals(namespace, other.getNamespace()) + && Objects.equals(directives, other.getDirectives()) + && Objects.equals(attributes, other.getAttributes()); + } + return false; + } } diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java index 1835b91f00f..cca3f6bfdb1 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java @@ -25,6 +25,7 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -1084,7 +1085,7 @@ public static void store(ModuleDatabase moduleDatabase, DataOutputStream out, bo } // Now persist all the Strings - Map objectTable = new HashMap<>(); + Map objectTable = new IdentityHashMap<>(); allStrings.remove(null); out.writeInt(allStrings.size()); for (String string : allStrings) { diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRequirement.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRequirement.java index 37151ef8f55..a69d71ad889 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRequirement.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRequirement.java @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.eclipse.osgi.internal.container.Capabilities; import org.eclipse.osgi.internal.framework.FilterImpl; import org.osgi.framework.InvalidSyntaxException; @@ -24,6 +25,7 @@ import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRequirement; import org.osgi.resource.Namespace; +import org.osgi.resource.Requirement; /** * An implementation of {@link BundleRequirement}. This requirement implements @@ -38,6 +40,7 @@ public class ModuleRequirement implements BundleRequirement { private final Map directives; private final Map attributes; private final ModuleRevision revision; + private transient int hashCode; ModuleRequirement(String namespace, Map directives, Map attributes, ModuleRevision revision) { @@ -153,4 +156,26 @@ ModuleRequirement getOriginal() { } } + @Override + public int hashCode() { + if (hashCode != 0) { + return hashCode; + } + return hashCode = Objects.hash(namespace, directives, attributes, revision); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Requirement) { + Requirement other = (Requirement) obj; + return Objects.equals(revision, other.getResource()) && Objects.equals(namespace, other.getNamespace()) + && Objects.equals(directives, other.getDirectives()) + && Objects.equals(attributes, other.getAttributes()); + } + return false; + } + }