Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MavenTargetLocation equals comparison never returns true #1649

Closed
wants to merge 1 commit into from

Conversation

haubi
Copy link

@haubi haubi commented Jan 17, 2024

In the TargetPlatformPreferencePage, upon the Apply button, some target definition containing some MavenTargetLocation always is rewritten to it's .target file, when MavenTargetDependency lacks an impl of equals(), causing MavenTargetLocation.roots to never be equal.

@laeubi
Copy link
Member

laeubi commented Jan 19, 2024

@haubi you need to increment the version for this change.

One thing I wonder is if we should move the equals check if the location to MavenTargetLocation do you see any issue with that? The reason is that a MavenTargetDependency can be "bound" to a target location and therefore it makes it quite hard to have a true equals method in the sense of its definition in the java language.

@haubi
Copy link
Author

haubi commented Jan 24, 2024

Agreed, having the equals check in MavenTargetLocation does make sense.

Meanwhile, it turns out that MavenTargetLocation.equals ignores lots of other attributes right now - which becomes relevant when MavenTargetDependency.equals eventually returns true.
But then, adding explicit comparison for each and every relevant nested attribute is a pita, so I prefer to compare along serialize() instead.

@haubi haubi force-pushed the maventargetdependency-equals branch from 4eb7a21 to 2955180 Compare January 24, 2024 15:18
@haubi haubi changed the title implement MavenTargetDependency equals comparison MavenTargetLocation equals comparison never returns true Jan 24, 2024
@haubi haubi force-pushed the maventargetdependency-equals branch 2 times, most recently from c53df64 to 6d1ca3b Compare January 24, 2024 15:37
@haubi haubi force-pushed the maventargetdependency-equals branch from 6d1ca3b to a816490 Compare January 25, 2024 07:58
@HannesWell HannesWell force-pushed the maventargetdependency-equals branch from a816490 to 0fa9047 Compare January 27, 2024 17:38
Copy link

github-actions bot commented Jan 27, 2024

Test Results

  214 files  ±0    214 suites  ±0   16m 53s ⏱️ +16s
  664 tests ±0    654 ✅ ±0  10 💤 ±0  0 ❌ ±0 
1 328 runs  ±0  1 306 ✅ ±0  22 💤 ±0  0 ❌ ±0 

Results for commit 26bbd40. ± Comparison against base commit c3fa2ed.

♻️ This comment has been updated with latest results.

Copy link
Contributor

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

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

In general one could consider to cache the result of serialize() in order to speed-up equals(), but I assume that method is not called that often for a MavenTargetLocation so it is probably not relevant, is it?

@HannesWell HannesWell force-pushed the maventargetdependency-equals branch from 0fa9047 to 9c6c79a Compare February 2, 2024 22:43
Copy link
Contributor

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

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

After thinking about this again, we should cache the result of serialize(), either in the serialize() method itself, computing the value lazy or in the constructor where we could consider to even use the original string from which the target was parsed (if so).

&& Objects.equals(dependencyScopes, other.dependencyScopes)
&& Objects.equals(failedArtifacts, other.failedArtifacts);
// check each and every relevant attribute, including nested ones
&& serialize().equals(other.serialize());
Copy link
Contributor

Choose a reason for hiding this comment

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

If equals is changed, hashCode() should be adjusted in the same way.

@HannesWell HannesWell force-pushed the maventargetdependency-equals branch from 9c6c79a to 26bbd40 Compare February 4, 2024 16:16
In the TargetPlatformPreferencePage, upon the Apply button, some target
definition containing some MavenTargetLocation always was rewritten to
it's .target file. This was because MavenTargetLocation.equals never
returned true for multiple reasons, starting with MavenTargetDependency
lacking an impl of equals().
Using serialize(), we can be sure to perform the equals check on any
relevant attribute, including deeply nested ones.

Beyond that, also use serialize() to implement hashCode().
@haubi haubi force-pushed the maventargetdependency-equals branch from 26bbd40 to e8ced44 Compare February 5, 2024 09:46
@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

@haubi what I'm wondering and as you have already analyzed that, can you probably point out where exactly the equals check fails in the PDE code?

I wondering if instead of changing m2e, it might generally be better to change the code there to always check the serialize() for every location in an equals check (not only m2e ones).

@haubi
Copy link
Author

haubi commented Feb 5, 2024

Using serialize() for hashCode() is fine with me.

While I understand the motivation behind caching the serialize() value, I believe we are unable to catch each and every situation where (deeply nested) attributes can be modified, which would require the cached value to be flushed.
Alternatively, we could think of refactoring to some real immutable object, but would that make sense?

@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

Maven Target locations should be immutable already, do you see any mutated state here?

@haubi
Copy link
Author

haubi commented Feb 5, 2024

@haubi what I'm wondering and as you have already analyzed that, can you probably point out where exactly the equals check fails in the PDE code?

@laeubi Well, here's my journey:

  • List<MavenTargetDependency> roots: fails because of missing MavenTargetDependency.equals - this was my first try
  • Then I figured that there are quite some other relevant members that are ignored by equals, so I started to use Objects.equals on them, but then figured that:
  • List<MavenTargetRepository> extraRepositories: fails because of missing MavenTargetRepository.equals - ok, would be easy
  • IFeature featureTemplate: not sure how to guarantee proper IFeature.equals impls
  • Map<String, BNDInstructions> instructionsMap: fails because of missing BNDInstructions.equals, but playing around with the editor I managed to end up with comparing the empty map against a map with empty values, and this was when I headed over to use serialize.

I wondering if instead of changing m2e, it might generally be better to change the code there to always check the serialize() for every location in an equals check (not only m2e ones).

Not sure where you mean by "there", but indeed I have thought on something similar too:
Compare the serialized values right in TargetPlatformPreferencePage.performOk (see also the backtrace in eclipse-pde/eclipse.pde#1059) instead of using ((TargetDefinition) original).isContentEqual(def), which seems strange anyway .

@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

Not sure where you mean by "there"

You mentions that you faced "In the TargetPlatformPreferencePage, upon the Apply button, some target definition containing some MavenTargetLocation always is rewritten to it's .target file"

So I would think about that lets say there is even other location types, maybe this check should be enhanced "there" (in the TargetPlatformPreferencePage or related code) to always compare a normalized serialized version of a target location, for example I would assume the following does not matter:

  1. Order of locations
  2. Order of attributes
  3. Whitespaces
  4. Line endings

so just roughly speaking, parse the XML of previous state and compare it with the parsed XML on new state to decide if something has to change and if not keep the file as is.

So given a location always produces the same XML equivalent (not necessary text representation) it should never touch the file.

@haubi
Copy link
Author

haubi commented Feb 5, 2024

Maven Target locations should be immutable already, do you see any mutated state here?

Well, maybe I'm misinterpreting, but this is what makes me believe it should be mutable:

  • setExcluded modifying a relevant member
  • getDependencyScopes returning a potentially mutable Collection
  • getRoots returning a mutable List
  • getFeatureTemplate not sure if IFeature is immutable
  • String label is not final (ok, could be without warnings/errors)

@haubi
Copy link
Author

haubi commented Feb 5, 2024

So I would think about that lets say there is even other location types, maybe this check should be enhanced "there" (in the TargetPlatformPreferencePage or related code) to always compare a normalized serialized version of a target location,

Well, TargetPlatformPreferencePage uses at least two variants of comparisons using equals:

  1. ((TargetDefinition) fPrevious).isContentEquivalent, not comparing the name
    https://github.com/eclipse-pde/eclipse.pde/blob/89bf2f0a1a25f6dd4278accd6f38415ec7c328ad/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/TargetPlatformPreferencePage.java#L886
  2. ((TargetDefinition) original).isContentEqual(def), comparing the name as well
    https://github.com/eclipse-pde/eclipse.pde/blob/89bf2f0a1a25f6dd4278accd6f38415ec7c328ad/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/preferences/TargetPlatformPreferencePage.java#L939

Feels like not using Arrays.equals would make TargetDefinition.isContentEquivalent and TargetDefinition.isContentEqual less readable, and "implementing proper equals is difficult" feels like some poor argument, no?

@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

Yes I think org.eclipse.pde.internal.core.target.TargetDefinition.isContentEqual(ITargetDefinition) and isContentEquivalent is where we actually need a fix!

As mentioned the order of locations is irrelevant (what is currently checked!) and the locations itself are better compared by their serialized representation instead of their object representation!

@haubi
Copy link
Author

haubi commented Feb 5, 2024

@laeubi Did I understand it correctly in that you want something like:
eclipse-pde/eclipse.pde#1127

@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

@haubi yes, sorry for the confusion here but I think that is much more felxible and usefull than what we can do here at m2e (even though equals/hashcode should be improved of course!

@haubi
Copy link
Author

haubi commented Feb 5, 2024

@laeubi Well, it turns out that not each implementation of ITargetLocation really implements serialize - and indeed, TargetDefinition.serializeBundleContainers does not use serialize for some implementations.

@laeubi
Copy link
Member

laeubi commented Feb 5, 2024

@laeubi Well, it turns out that not each implementation of ITargetLocation really implements serialize - and indeed, TargetDefinition.serializeBundleContainers does not use serialize for some implementations.

Yes this is some left overs from "old implementation, best actually would be to implement serialize always in the container ... thats really a mess.

@HannesWell
Copy link
Contributor

@haubi is this now obsolete since eclipse-pde/eclipse.pde#1127 is submitted?

@haubi haubi closed this Feb 12, 2024
@haubi
Copy link
Author

haubi commented Feb 12, 2024

Sure!

@haubi haubi deleted the maventargetdependency-equals branch February 12, 2024 07:39
@laeubi
Copy link
Member

laeubi commented Feb 12, 2024

@haubi thanks for working on this one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants