Bug 247012 - [nls tooling] Find Broken Externalized Strings could handle constants for message keys
Summary: [nls tooling] Find Broken Externalized Strings could handle constants for mes...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Text (show other bugs)
Version: 3.4   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: 3.6 M4   Edit
Assignee: Deepak Azad CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-09-11 09:17 EDT by John Vasta CLA
Modified: 2009-12-09 06:05 EST (History)
5 users (show)

See Also:


Attachments
patch (2.95 KB, patch)
2009-10-30 07:25 EDT, Deepak Azad CLA
daniel_megert: review-
Details | Diff
reworked patch (3.73 KB, patch)
2009-11-03 02:18 EST, Deepak Azad CLA
no flags Details | Diff
fix + testcase (8.75 KB, patch)
2009-11-03 06:12 EST, Deepak Azad CLA
daniel_megert: review-
Details | Diff
fix + testcases (10.23 KB, patch)
2009-11-08 08:23 EST, Deepak Azad CLA
daniel_megert: review-
Details | Diff
fix + tests (10.85 KB, patch)
2009-11-10 22:48 EST, Deepak Azad CLA
daniel_megert: iplog+
daniel_megert: review+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description John Vasta CLA 2008-09-11 09:17:28 EDT
If I externalize strings using non-Eclipse-style, then change the literal key strings in the code to constants, the Find Broken Externalized Strings function reports such keys as undefined in the code, and unused in the properties file. For example, with this class,

public class Main {
	private static final String MAIN_INDIRECT = "Main.indirect";

	public static void main(String[] args) {
		System.out.println(Messages.getString("Main.direct")); //$NON-NLS-1$
		System.out.println(Messages.getString(MAIN_INDIRECT)); //$NON-NLS-1$
	}
}

the "Main.indirect" key is reported as undefined and unused. We have a lot of externalized strings, can't use Eclipse-style externalization, and like to have the keys defined as constants for improved readability and maintainability. It's unfortunate to lose the ability to check for broken string externalization.
Comment 1 Simon Archer CLA 2008-09-11 09:31:44 EDT
+1 for this enhancement. Having to pick between improved readability and a tool that checks for NLS breakages is unfortunate.
Comment 2 Jerome Lanneluc CLA 2008-09-12 07:03:57 EDT
"Find Broken Externalized Strings" is provided by JDT/UI.
Moving to JDT/UI
Comment 3 Dani Megert CLA 2008-09-12 08:16:20 EDT
The Eclipse externalization method does exactly that i.e. uses constants AND it is much better in terms of memory usage. For details see:
http://dev.eclipse.org/viewcvs/index.cgi/platform-core-home/documents/3.1/message_bundles.html?view=co

Why don't you use that one?
Comment 4 John Vasta CLA 2008-09-12 08:50:14 EDT
We have a server component that must serve multiple clients that are potentially running in different locales. We want the server to generate messages for a particular client using the client's locale, so it can't just preload all the messages using the server's locale.
Comment 5 Dani Megert CLA 2008-09-12 09:47:52 EDT
OK, I see.
Are those constants in the same file or would we have to check other files?
Comment 6 John Vasta CLA 2008-09-12 09:58:51 EDT
Requiring the constants to be in the same file would be fine. (We typically create them using the Extract Constant refactoring.)
Comment 7 Dani Megert CLA 2008-09-12 10:05:00 EDT
Will see what I can do.
3.5 time permitting.
Comment 8 Dani Megert CLA 2009-04-24 09:49:47 EDT
Sorry not time left for this.
Comment 9 Deepak Azad CLA 2009-10-30 07:25:18 EDT
Created attachment 150912 [details]
patch

I have added checks for string constants defined in the same class. (Extract constant refactoring creates the constants in the same class.)
Comment 10 Dani Megert CLA 2009-11-02 09:35:36 EST
My first try of the patch causes exceptions:

!ENTRY org.eclipse.core.jobs 4 2 2009-11-02 15:36:45.683
!MESSAGE An internal error occurred during: "Search for Broken NLS Keys".
!STACK 0
org.eclipse.core.runtime.AssertionFailedException: assertion failed: 
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:110)
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:96)
	at org.eclipse.jface.text.Position.setLength(Position.java:185)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.findKey(NLSSearchResultRequestor.java:248)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.acceptSearchMatch(NLSSearchResultRequestor.java:122)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.report(MatchLocator.java:1760)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:513)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:342)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2163)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2638)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2365)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.process(MatchLocator.java:1627)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1037)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1078)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1210)
	at org.eclipse.jdt.internal.core.search.JavaSearchParticipant.locateMatches(JavaSearchParticipant.java:94)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.findMatches(BasicSearchEngine.java:231)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.search(BasicSearchEngine.java:515)
	at org.eclipse.jdt.core.search.SearchEngine.search(SearchEngine.java:582)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchQuery.run(NLSSearchQuery.java:90)
	at org.eclipse.search2.internal.ui.InternalSearchUI$InternalSearchJob.run(InternalSearchUI.java:91)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)

!ENTRY org.eclipse.core.jobs 4 2 2009-11-02 15:36:45.683
!MESSAGE An internal error occurred during: "Search for Broken NLS Keys".
!STACK 0
org.eclipse.core.runtime.AssertionFailedException: assertion failed: 
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:110)
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:96)
	at org.eclipse.jface.text.Position.setLength(Position.java:185)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.findKey(NLSSearchResultRequestor.java:248)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.acceptSearchMatch(NLSSearchResultRequestor.java:122)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.report(MatchLocator.java:1760)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:513)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:342)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2163)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2638)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2365)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.process(MatchLocator.java:1627)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1037)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1078)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1210)
	at org.eclipse.jdt.internal.core.search.JavaSearchParticipant.locateMatches(JavaSearchParticipant.java:94)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.findMatches(BasicSearchEngine.java:231)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.search(BasicSearchEngine.java:515)
	at org.eclipse.jdt.core.search.SearchEngine.search(SearchEngine.java:582)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchQuery.run(NLSSearchQuery.java:90)
	at org.eclipse.search2.internal.ui.InternalSearchUI$InternalSearchJob.run(InternalSearchUI.java:91)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
Comment 11 Dani Megert CLA 2009-11-02 09:37:54 EST
And with the example from comment 0 I get NPEs:


!ENTRY org.eclipse.core.jobs 4 2 2009-11-02 15:41:02.240
!MESSAGE An internal error occurred during: "Search for Broken NLS Keys".
!STACK 0
java.lang.NullPointerException
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.findKey(NLSSearchResultRequestor.java:258)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.acceptSearchMatch(NLSSearchResultRequestor.java:122)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.report(MatchLocator.java:1760)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:513)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:342)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2163)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2638)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2365)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.process(MatchLocator.java:1627)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1037)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1078)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1195)
	at org.eclipse.jdt.internal.core.search.JavaSearchParticipant.locateMatches(JavaSearchParticipant.java:94)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.findMatches(BasicSearchEngine.java:231)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.search(BasicSearchEngine.java:515)
	at org.eclipse.jdt.core.search.SearchEngine.search(SearchEngine.java:582)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchQuery.run(NLSSearchQuery.java:90)
	at org.eclipse.search2.internal.ui.InternalSearchUI$InternalSearchJob.run(InternalSearchUI.java:91)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)

!ENTRY org.eclipse.core.jobs 4 2 2009-11-02 15:41:02.240
!MESSAGE An internal error occurred during: "Search for Broken NLS Keys".
!STACK 0
java.lang.NullPointerException
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.findKey(NLSSearchResultRequestor.java:258)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchResultRequestor.acceptSearchMatch(NLSSearchResultRequestor.java:122)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.report(MatchLocator.java:1760)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:513)
	at org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator.matchReportReference(TypeReferenceLocator.java:342)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2163)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2638)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.reportMatching(MatchLocator.java:2365)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.process(MatchLocator.java:1627)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1037)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1078)
	at org.eclipse.jdt.internal.core.search.matching.MatchLocator.locateMatches(MatchLocator.java:1195)
	at org.eclipse.jdt.internal.core.search.JavaSearchParticipant.locateMatches(JavaSearchParticipant.java:94)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.findMatches(BasicSearchEngine.java:231)
	at org.eclipse.jdt.internal.core.search.BasicSearchEngine.search(BasicSearchEngine.java:515)
	at org.eclipse.jdt.core.search.SearchEngine.search(SearchEngine.java:582)
	at org.eclipse.jdt.internal.ui.refactoring.nls.search.NLSSearchQuery.run(NLSSearchQuery.java:90)
	at org.eclipse.search2.internal.ui.InternalSearchUI$InternalSearchJob.run(InternalSearchUI.java:91)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
Comment 12 Dani Megert CLA 2009-11-02 09:38:30 EST
Please also make sure to update the copyright date.
Comment 13 Deepak Azad CLA 2009-11-03 02:18:39 EST
Created attachment 151156 [details]
reworked patch

Assertion Failed Exception (first set of exceptions) occurred when the length of identifier name was 1. 
 eg private static final String X ="X";
I have fixed this problem

The null pointer exception (second set) was unreproducible.
Comment 14 Dani Megert CLA 2009-11-03 02:26:16 EST
Deepak, please also add some test cases for this new scenario (in org.eclipse.jdt.ui.tests - org.eclipse.jdt.ui.tests.search).
Comment 15 Deepak Azad CLA 2009-11-03 06:12:48 EST
Created attachment 151172 [details]
fix + testcase

I was able to reproduce null pointer exception from comment 11. The attached patch includes the fix for this. 

The patch also includes test cases for the fix.
Comment 16 Dani Megert CLA 2009-11-04 04:29:01 EST
The patch is not good as it can lead to ClassCastExceptions in case there's a 1-arg method that takes something else than a String constant, e.g. something like:

    public static final int MAIN_INDIRECT2 = 1;
    public static void main(String[] args) {
        System.out.println(Messages.getString(MAIN_INDIRECT2)); 
    }
    public class Messages {
	public static String getString(int i) {
		return "";
	}
    }

Other minor issues with the patch:
- I hate code duplication ;-)
  I suggest we extend the first if (!...) with the other case, then assign the
  common values and return found argument string for the existing case. Note that
  the same string can serve as the identifier in the ongoing code.
- the additional Javadoc comment is not correct: the field does not need to be
  private
- testBug247012_2 also passes without the fix - is this intentional?
Comment 17 Deepak Azad CLA 2009-11-08 08:23:07 EST
Created attachment 151642 [details]
fix + testcases

(In reply to comment #16)
> The patch is not good as it can lead to ClassCastExceptions in case there's a
> 1-arg method that takes something else than a String constant, 
Added a check before casting to String

> Other minor issues with the patch:
> - I hate code duplication ;-)
Improved the patch :)

> - testBug247012_2 also passes without the fix - is this intentional?
Yes, this is intentional. testBug247012_2 and testBug247012_3 are to test for the case when the constant is not declared as something like 'static final String MAIN_INDIRECT = "Main.indirect";'. testBug247012_2 catches the null pointer exception from comment 11 and testBug247012_3 catches the classCastException mentioned above.
Comment 18 Dani Megert CLA 2009-11-10 06:06:39 EST
The patch fixes the CCE but introduces a new bug: the returned key string is wrong in the normal case i.e. it includes the '"' at the end. In addition I see no value to loop over all fields once we've found a match i.e. simply return on first match.
==>
- remove local var 'key'
- return result from if and else statements
- to improve readability I would also rename 'keyStart/End with tokenStart/End

Tests are OK.
Comment 19 Deepak Azad CLA 2009-11-10 22:48:10 EST
Created attachment 151909 [details]
fix + tests
Comment 20 Dani Megert CLA 2009-11-11 06:02:16 EST
Comment on attachment 151909 [details]
fix + tests

That's how I like it!
Comment 21 Dani Megert CLA 2009-11-11 06:03:33 EST
Deepak, please attach a patch that adds some additional tests which cover the bug made in the previous patch.
Comment 22 Raksha Vasisht CLA 2009-12-08 03:16:26 EST
Verified for 3.6 M4 with I20091207-1800.
Comment 23 Dani Megert CLA 2009-12-09 06:04:40 EST
>Deepak, please attach a patch that adds some additional tests which cover the
>bug made in the previous patch.
This is still missing. I filed bug to track this.
Comment 24 Dani Megert CLA 2009-12-09 06:05:37 EST
>I filed bug to track this.
Bug 297320.