Skip to content
This repository has been archived by the owner on Oct 16, 2024. It is now read-only.

Add BiMap to the special support for collections and maps. #419

Merged
merged 1 commit into from
Apr 4, 2020
Merged

Add BiMap to the special support for collections and maps. #419

merged 1 commit into from
Apr 4, 2020

Conversation

thespags
Copy link
Contributor

Follows the behavoir of Maps under https://github.com/inferred/FreeBuilder#collections-and-maps.

Addresses #418

@thespags
Copy link
Contributor Author

thespags commented Feb 24, 2020

Hi @alicederyn here's a rough draft of adding BiMaps. I do have some questions inlined for you. Mostly I went the lazy approach of using the existing implementation for Map and moving it over to BiMap.

addPut(code);
addPutAll(code);
addRemoveKey(code);
addRemoveValue(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.

This may have been misguided but since BiMap's are 1-1, you may want to make modifications based on the value and not the key.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems reasonable. You could do .mutateX(m -> m.inverse().remove(value)) so it really comes down to convenience.

@thespags thespags requested a review from alicederyn February 24, 2020 07:06
Copy link
Collaborator

@alicederyn alicederyn left a comment

Choose a reason for hiding this comment

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

Answers and some points I noted in a quick skim. Please make sure you have the full API tested — I noted some examples but I wasn't exhaustive.

addPut(code);
addPutAll(code);
addRemoveKey(code);
addRemoveValue(code);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems reasonable. You could do .mutateX(m -> m.inverse().remove(value)) so it really comes down to convenience.

.addLine(
"public %s %s(%s key) {",
datatype.getBuilder(),
"removeKey" + property.getCapitalizedName(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

"removeKeyFrom" + ... seems more readable — also, should live in BuilderMethods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The "from" reads much better, but doesn't the method currently live in BuilderMethods?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You'll need to add a new static method, removeKeyFromMethod, to BuilderMethods.java, so that you're not writing out the formula "removeKeyFrom" + x everywhere.

@thespags
Copy link
Contributor Author

thespags commented Mar 1, 2020

I'd suggest passing around forcePut instead of put, and doing the IllegalArgumentException-throwing check of put explicitly in the put method. You'll also need tests.

If I understand it correctly, that means defining a forcePut method on the builder. As the passed around put is simply the implementation defined (overridden or not) for the builder.
And adding

      checkArgument(!containsValue(value), "value already present: %s", value);

to the put in the checked bimap.

Is that correct?

@alicederyn
Copy link
Collaborator

Yes, that seems correct: generate a forcePutX (name creation logic should live in BuilderMethods.forcePutMethod) method, and use it as the canonical put method.

Note that this also means that the generated putX needs to be rewritten to delegate to forcePutX, as the point of this circumlocution is to support https://github.com/inferred/FreeBuilder#defaults-and-constraints — the user overrides a single method, adds custom constraint checking, and knows for sure that all mutations will ultimately go via that method. Change testOverridingAdd to override the forcePut method not the put method to verify all this. (Or, even better, look at e.g. ListPropertyTest, where I've explicitly tested validation. Mea culpa on testOverridingAdd being a bit terrible :( )

@thespags
Copy link
Contributor Author

thespags commented Mar 8, 2020

Change testOverridingAdd to override the forcePut method not the put method to verify all this. (Or, even better, look at e.g. ListPropertyTest, where I've explicitly tested validation. Mea culpa on testOverridingAdd being a bit terrible :( )

I've updated to testOverridingAdd to forcePut. I'm a bit confused why entryRemainsUsableAfterCallingSetValue doesn't work. I'll admit I'm having a bit of a time trying to tie generated test code through generated prod code to the guava methods themselves to figure out what's going on.

Any more feedback for either entryRemainsUsableAfterCallingSetValue or the overall review? Thanks in advance.

@alicederyn
Copy link
Collaborator

If you dig into the logic of setValue in HashBiMap's entry implementations, it should become clear why entryRemainsUsableAfterCallingSetValue is not working. You'll need to rewrite CheckedBiMap's Entry implementation. Cleanest fix, given how BiMap works, is likely just to copy key + value from the underlying entry on construction, rather than keeping a link to the entry.

@thespags
Copy link
Contributor Author

Cleanest fix, given how BiMap works, is likely just to copy key + value from the underlying entry on construction, rather than keeping a link to the entry.

Hi @alicederyn . I've updated to copy key and value from the underlying entry. As well as updating equals/hashcode to use those values.

@thespags
Copy link
Contributor Author

@alicederyn sorry for the delay. I finally fixed the checkstyle issues. I wasn't sure how to suppress my comment line with a url, and checkstyle didn't like nested static method calls. Thank you.

@thespags
Copy link
Contributor Author

Hi @alicederyn I was wondering when you would have a chance to go over my latest changes. Thank you!

@alicederyn
Copy link
Collaborator

Sorry for the delay

@Test
public void callSetValueOnEntryChecksDuplicateValue() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("value already present: ");
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'm not sure for your framework the cleanest way to change a literal "beta" -> beta. I could do values.example(1).replace('\"', '') but I was thinking there was a better way.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You'd have to add that method to ElementFactory. exampleToString?

@thespags
Copy link
Contributor Author

@alicederyn no worries.

.addLine(" }")
.addLine("")
.addLine(" @Override public V setValue(V value) {")
.addLine(" %s.checkArgument(!hasValue.test(value), \"value already present: \" + value);",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use %s like in the put method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Test
public void callSetValueOnEntryChecksDuplicateValue() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("value already present: ");
Copy link
Collaborator

Choose a reason for hiding this comment

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

You'd have to add that method to ElementFactory. exampleToString?

@thespags
Copy link
Contributor Author

You'd have to add that method to ElementFactory. exampleToString?

Done.

@alicederyn
Copy link
Collaborator

Build failed on checkstyle though: https://travis-ci.org/github/inferred/FreeBuilder/builds/669328480

@thespags
Copy link
Contributor Author

Thanks @alicederyn, I've fixed the offending lines and cleaned up my commits. Things are passing now.

@alicederyn alicederyn merged commit bbece66 into inferred:master Apr 4, 2020
@alicederyn
Copy link
Collaborator

Thanks @thespags. I merged a number of follow-up cleanups and bugfixes to this, and it is now released as v2.6.0.

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

Successfully merging this pull request may close these issues.

2 participants