-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Fix location bundle with fast access #3327
Fix location bundle with fast access #3327
Conversation
…ed, that all accesses to it are only lookups in hashmaps Made all access of Location-messages a fast hash table lookup. Fixed one message in JabRefFrame that used an escaped key instead of the unescaped version.
* Fix NPE in MainTable * Fix build
public static String lang(String key, String... params) { | ||
if (localizedMessages == null) { | ||
// I'm logging this because it should never happen | ||
LOGGER.error("Messages are not initialized in " + Localization.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yozu can omit the class part when writing a log message, as the Logger is already instantiated for the current class and therefore knows its name.
*/ | ||
private static Hashtable<String, String> createLookupTable(ResourceBundle baseBundle){ | ||
final ArrayList<String> baseKeys = Collections.list(baseBundle.getKeys()); | ||
Hashtable<String, String> lookup = new Hashtable<>(baseKeys.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HashTable is deprecated, better use HashMap.
And with java 8 streams and the Collection methods you an even make this code a lot nicer:
https://www.mkyong.com/java8/java-8-convert-list-to-map/
protected static String translate(ResourceBundle resBundle, String idForErrorMessage, String key, String... params) { | ||
Objects.requireNonNull(resBundle); | ||
private static String lookup(LocalizationBundle bundle, String idForErrorMessage, String key, String... params) { | ||
if (key == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better use the Objects.requireNonNull here. It will automatically throw an NPE
private static class LocalizationBundle extends ResourceBundle { | ||
|
||
// I'm using a Hashtable since it allows to get the keys directly as an Enumeration | ||
private Hashtable<String, String> lookup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, use as HashMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all thanks for the analysis and for providing a fix!
I added some minor code wise suggestions, but in general looks good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added a few minor comments on top of those of @Siedlerchr, but overall the code looks good.
I have also played around with a running JabRef, changing the localization etc., in this branch and did not notice errors.
I think your refactoring of the localization classes is fine, as well as moving the initialization into the preferences. So, when the few minor things are fixed, this PR is good to go.
ResourceBundle menuTitles = ResourceBundle.getBundle(MENU_RESOURCE_PREFIX, locale, new EncodingControl(StandardCharsets.UTF_8)); | ||
// Just for the case something really stupid happened. | ||
if (messages == null || menuTitles == null) { | ||
LOGGER.error("Could not load language translation files in " + Localization.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same remark regarding the class files as by @Siedlerchr above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// Just for the case something really stupid happened. | ||
if (messages == null || menuTitles == null) { | ||
LOGGER.error("Could not load language translation files in " + Localization.class); | ||
throw new NullPointerException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think throwing an IllegalStateException
would be better here. Just for the more descriptive exception type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used
Objects.requireNonNull(messages, "Could not load " + RESOURCE_PREFIX + " resource.");
Objects.requireNonNull(menuTitles, "Could not load " + MENU_RESOURCE_PREFIX + " resource.");
which throws NPE, but gives a description.
if (menuTitles == null) { | ||
setLanguage("en"); | ||
public final Object handleGetObject(String key) { | ||
if (key == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, rather use Objects.requireNonNull
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
General code cleanup
I fixed the issues. Is there a nicer way to create a |
Unfortunately there is no simpler solution for the Map thing. |
Replacement of last null test with Objects.requireNonNull
Since the checkstyle problems are fixed, I'll merge this now. Thanks a lot for your contribution! |
* upstream/master: Initializing EntryEditor Tabs on focus (#3331) Fix #3292: annotations are now automatically refreshed (#3325) Change integrity message for names depending on database mode (#3330) Fix location bundle with fast access (#3327) Small code cleanup Fix "path not found" exception Small fix in drag and drop handler of linked files (#3328) # Conflicts: # src/main/java/org/jabref/gui/DefaultInjector.java
* upstream/master: (31 commits) Source tab entry duplication (#3360) Use CITE_COMMENT not only for external latex editors but also for cop… (#3351) Updating with new translations (#3348) Upgrade error-prone (#3350) Jabref_pt_BR partially updated (#3349) Used late initialization for context menus (#3340) Fix NPE when calling with bib file as cmd argument (#3343) update mockito-core from 2.10.0 -> 2.11.0 (#3338) Remove underscore in Localized messages (#3336) Localisation: French: new entries translated (#3337) Refactoring: Lazy init of all editor tabs (#3333) Initializing EntryEditor Tabs on focus (#3331) Fix #3292: annotations are now automatically refreshed (#3325) Change integrity message for names depending on database mode (#3330) Fix location bundle with fast access (#3327) Small code cleanup Fix "path not found" exception Small fix in drag and drop handler of linked files (#3328) Fix NPE in MainTable (#3318) Increase relative size of abstract field in editor (#3320) ...
This PR addresses the issue I mentioned under Localization memory. To provide a reasonable implementation and not only a bugfix I had to change the layout. These things bothered me the most:
LocalizationBundle
andLocalization
were calling each other. We neededLocalization
to create the keys in the bundle and on the other hand, theLocalizationBundle
was only used in theLocalization
classLocalization
seemed like a quick fix to provide messages as bundle to Java FXTo unify the implementation, I based the complete
Localization
framework on two instances (menus and messages) ofLocalizationBundle
and made this an internal class as it should not be used without it. The bundles are (re)created whensetLanguage
is called and contain the unescaped keys and messages for the specified language as a fast hash table.This enables two things: (1) We can pass the
LocalizationBundle
instance directly to Java FX withgetMessages
and since we only pass the reference to the hash table, it will be instantaneous. (2) Every call tolang
ormenuTitle
is a simple lookup in the hash table and a replacement of possible parameters in the string.The public API for
Localization
, meaninglang
,menuTitle
,getMessages
andsetLanguage
did not change and works as expected. During profiling/debugging I noticed, however, that there is a flaw in the JabRef startup. The language is set after the initialization of the preferences. At least one preference setting uses a localized string in a static field. Since when uninitialized, theLocalization
reverts back to "en", these strings will never be in the user language (see this issue). Furthermore, due to this access, theLocalization
framework will always be initialized several times. Once in the init of the preferences and once further down inmain
.This can only be fixed by pushing
setLanguage
to the initialization of the preferences which I did. One additional minor thing was that one message key was used escaped and had to be unescaped. Other than that, this implementation worked out of the box and passed all existing localization tests.I commented my code heavily and would be glad if someone would review it. Don't forget that I'm looking only about 5 days on the code base and it is very likely I have overlooked something.