Skip to content

Commit 0bbc4ad

Browse files
mehmet-karamaniloveeclipse
authored andcommitted
Synchronize ICompulationUnit stamp with underlined IResource after save
actions JDT Java search has a special handling for modified working copies (see BasicSearchEngine.getWorkingCopies()). This fix synchronizes the modification stamps between the CompilationUnitElementInfo and underlined IResource after "save actions" execution triggered in CompilationUnitDocumentProvider.commitWorkingCopy(), so the saved working copy has correct state and Java search can find the type from the index. See eclipse-jdt/eclipse.jdt.core#3919 for the first part of the change on JDT Core side. Fixes eclipse-jdt/eclipse.jdt.core#3918
1 parent 465d19f commit 0bbc4ad

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/ImportOrganizeTest.java

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@
1414
*******************************************************************************/
1515
package org.eclipse.jdt.ui.tests.core;
1616

17+
import static org.eclipse.jdt.internal.corext.fix.CleanUpConstants.ORGANIZE_IMPORTS;
18+
import static org.eclipse.jdt.internal.corext.fix.CleanUpPreferenceUtilCore.SAVE_PARTICIPANT_KEY_PREFIX;
19+
import static org.eclipse.jdt.internal.ui.javaeditor.saveparticipant.SaveParticipantPreferenceConfigurationConstants.EDITOR_SAVE_PARTICIPANT_PREFIX;
20+
import static org.eclipse.jdt.internal.ui.javaeditor.saveparticipant.SaveParticipantPreferenceConfigurationConstants.POSTSAVELISTENER_ID;
1721
import static org.junit.Assert.assertEquals;
1822
import static org.junit.Assert.assertNotNull;
1923
import static org.junit.Assert.assertTrue;
2024

2125
import java.io.File;
26+
import java.lang.reflect.Field;
27+
import java.util.ArrayList;
2228
import java.util.Hashtable;
29+
import java.util.List;
2330

2431
import org.junit.After;
2532
import org.junit.Before;
@@ -30,30 +37,46 @@
3037
import org.eclipse.jdt.testplugin.JavaTestPlugin;
3138
import org.eclipse.jdt.testplugin.TestOptions;
3239

40+
import org.eclipse.core.runtime.NullProgressMonitor;
3341
import org.eclipse.core.runtime.Path;
3442
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
43+
import org.eclipse.core.runtime.preferences.InstanceScope;
3544

3645
import org.eclipse.core.resources.ProjectScope;
3746

3847
import org.eclipse.jface.preference.IPreferenceStore;
3948

49+
import org.eclipse.ui.IEditorPart;
50+
4051
import org.eclipse.jdt.core.IClasspathAttribute;
4152
import org.eclipse.jdt.core.ICompilationUnit;
4253
import org.eclipse.jdt.core.IImportDeclaration;
4354
import org.eclipse.jdt.core.IJavaProject;
4455
import org.eclipse.jdt.core.IPackageFragment;
4556
import org.eclipse.jdt.core.IPackageFragmentRoot;
4657
import org.eclipse.jdt.core.JavaCore;
58+
import org.eclipse.jdt.core.JavaModelException;
4759
import org.eclipse.jdt.core.Signature;
4860
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
4961
import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation;
5062
import org.eclipse.jdt.core.manipulation.OrganizeImportsOperation.IChooseImportQuery;
63+
import org.eclipse.jdt.core.search.IJavaSearchConstants;
64+
import org.eclipse.jdt.core.search.IJavaSearchScope;
65+
import org.eclipse.jdt.core.search.MethodNameMatch;
66+
import org.eclipse.jdt.core.search.MethodNameMatchRequestor;
67+
import org.eclipse.jdt.core.search.SearchEngine;
68+
import org.eclipse.jdt.core.search.SearchPattern;
5169
import org.eclipse.jdt.core.search.TypeNameMatch;
5270

71+
import org.eclipse.jdt.internal.core.CompilationUnit;
72+
import org.eclipse.jdt.internal.core.CompilationUnitElementInfo;
73+
5374
import org.eclipse.jdt.ui.JavaUI;
5475
import org.eclipse.jdt.ui.PreferenceConstants;
5576
import org.eclipse.jdt.ui.tests.core.rules.ProjectTestSetup;
5677

78+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
79+
5780
public class ImportOrganizeTest extends CoreTests {
5881

5982
private IJavaProject fJProject1;
@@ -3880,6 +3903,158 @@ public class C1 {
38803903
""";
38813904
assertEqualString(cu1.getSource(), str2);
38823905
}
3906+
3907+
@Test
3908+
public void testBug3918() throws Exception {
3909+
IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(JavaUI.ID_PLUGIN);
3910+
String postSaveActionsKey = EDITOR_SAVE_PARTICIPANT_PREFIX + POSTSAVELISTENER_ID;
3911+
String organizeImportsKey = SAVE_PARTICIPANT_KEY_PREFIX + ORGANIZE_IMPORTS;
3912+
3913+
final boolean orgImportsOrigState = prefs.getBoolean(organizeImportsKey, false);
3914+
final boolean postSaveActionsOrigState = prefs.getBoolean(postSaveActionsKey, false);
3915+
3916+
try {
3917+
prefs.putBoolean(postSaveActionsKey, true);
3918+
prefs.putBoolean(organizeImportsKey, true);
3919+
3920+
IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
3921+
3922+
IPackageFragment pack0= sourceFolder.createPackageFragment("p", false, null);
3923+
String original= """
3924+
package p;
3925+
3926+
import java.util.function.IntPredicate;
3927+
import java.util.Properties;
3928+
3929+
class UnusedStaticImport {
3930+
boolean value = match(Character::isUpperCase, 'A');
3931+
3932+
public static boolean match(IntPredicate matcher, int codePoint) {
3933+
return matcher.test(codePoint);
3934+
}
3935+
}
3936+
""";
3937+
CompilationUnit icu= (CompilationUnit) pack0.createCompilationUnit("Unusedmport.java", original, false, null);
3938+
IEditorPart editor= EditorUtility.openInEditor(icu);
3939+
3940+
long timeStamp= getElementInfoTimestamp(icu);
3941+
assertEquals("ICU modification stamp differs from resource stamp", icu.getResource().getModificationStamp(), timeStamp);
3942+
3943+
// On save "organize imports" participant modifies CU and the CU timestamp should change.
3944+
// Check that CU stamp is still in-sync with the underlined IFie stamp
3945+
editor.doSave(new NullProgressMonitor());
3946+
3947+
String fixed= """
3948+
package p;
3949+
3950+
import java.util.function.IntPredicate;
3951+
3952+
class UnusedStaticImport {
3953+
boolean value = match(Character::isUpperCase, 'A');
3954+
3955+
public static boolean match(IntPredicate matcher, int codePoint) {
3956+
return matcher.test(codePoint);
3957+
}
3958+
}
3959+
""";
3960+
assertEqualString(fixed, icu.getSource());
3961+
3962+
timeStamp= getElementInfoTimestamp(icu);
3963+
assertEquals("ICU modification stamp differs from resource stamp", icu.getResource().getModificationStamp(), timeStamp);
3964+
} finally {
3965+
prefs.putBoolean(postSaveActionsKey, postSaveActionsOrigState);
3966+
prefs.putBoolean(organizeImportsKey, orgImportsOrigState);
3967+
}
3968+
}
3969+
3970+
@Test
3971+
public void testBug3918_Search() throws Exception {
3972+
String postSaveActionsKey = EDITOR_SAVE_PARTICIPANT_PREFIX + POSTSAVELISTENER_ID;
3973+
String organizeImportsKey= SAVE_PARTICIPANT_KEY_PREFIX + ORGANIZE_IMPORTS;
3974+
IEclipsePreferences jdtUiPreferences = InstanceScope.INSTANCE.getNode(JavaUI.ID_PLUGIN);
3975+
final boolean orgImportsOrigState= jdtUiPreferences.getBoolean(organizeImportsKey, false);
3976+
final boolean postSaveActionsOrigState = jdtUiPreferences.getBoolean(postSaveActionsKey, false);
3977+
3978+
try {
3979+
jdtUiPreferences.putBoolean(postSaveActionsKey, true);
3980+
jdtUiPreferences.putBoolean(organizeImportsKey, true);
3981+
3982+
IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
3983+
3984+
IPackageFragment pack0= sourceFolder.createPackageFragment("p", false, null);
3985+
String original= """
3986+
package p;
3987+
3988+
import java.util.function.IntPredicate;
3989+
import java.util.Properties;
3990+
3991+
class UnusedStaticImport {
3992+
boolean value = match(Character::isUpperCase, 'A');
3993+
3994+
public static boolean match(IntPredicate matcher, int codePoint) {
3995+
return matcher.test(codePoint);
3996+
}
3997+
}
3998+
""";
3999+
ICompilationUnit icu= pack0.createCompilationUnit("Unusedmport.java", original, false, null);
4000+
IEditorPart editor= EditorUtility.openInEditor(icu);
4001+
class Collector extends MethodNameMatchRequestor {
4002+
List<MethodNameMatch> matches = new ArrayList<>();
4003+
@Override
4004+
public void acceptMethodNameMatch(MethodNameMatch match) {
4005+
this.matches.add(match);
4006+
}
4007+
}
4008+
IJavaSearchScope scope = SearchEngine.createHierarchyScope(icu.getType("UnusedStaticImport"));
4009+
Collector collector = new Collector();
4010+
new SearchEngine().searchAllMethodNames(
4011+
null, SearchPattern.R_PREFIX_MATCH, //package
4012+
null, SearchPattern.R_PREFIX_MATCH, // declaring Qualification
4013+
null, SearchPattern.R_PREFIX_MATCH, // declaring SimpleType
4014+
"match".toCharArray(), SearchPattern.R_PREFIX_MATCH,
4015+
scope, collector,
4016+
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null);
4017+
4018+
assertEquals("Should find one method, but found: " + collector.matches, 1, collector.matches.size());
4019+
MethodNameMatch actual= collector.matches.get(0);
4020+
assertEquals("Unexpected method found", "match", actual.getMethod().getElementName());
4021+
4022+
// On save "organize imports" participant modifies CU so the CU stamp should be properly updated.
4023+
// That has impact on search results: we don't see matches if CU stamp is out of sync with IFile stamp.
4024+
editor.doSave(new NullProgressMonitor());
4025+
4026+
collector = new Collector();
4027+
new SearchEngine().searchAllMethodNames(
4028+
null, SearchPattern.R_PREFIX_MATCH, //package
4029+
null, SearchPattern.R_PREFIX_MATCH, // declaring Qualification
4030+
null, SearchPattern.R_PREFIX_MATCH, // declaring SimpleType
4031+
"match".toCharArray(), SearchPattern.R_PREFIX_MATCH,
4032+
scope, collector,
4033+
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null);
4034+
4035+
assertEquals("Should find one method, but found: " + collector.matches, 1, collector.matches.size());
4036+
actual= collector.matches.get(0);
4037+
assertEquals("Unexpected method found", "match", actual.getMethod().getElementName());
4038+
4039+
} finally {
4040+
jdtUiPreferences.putBoolean(postSaveActionsKey, postSaveActionsOrigState);
4041+
jdtUiPreferences.putBoolean(organizeImportsKey, orgImportsOrigState);
4042+
}
4043+
}
4044+
4045+
private long getElementInfoTimestamp(CompilationUnit icu) throws NoSuchFieldException, IllegalAccessException, JavaModelException {
4046+
CompilationUnitElementInfo elementInfo= (CompilationUnitElementInfo) icu.getElementInfo();
4047+
4048+
Field timestampField= CompilationUnitElementInfo.class.getDeclaredField("timestamp");
4049+
long timeStamp;
4050+
try {
4051+
timestampField.setAccessible(true);
4052+
timeStamp= (long) timestampField.get(elementInfo);
4053+
} finally {
4054+
timestampField.setAccessible(false);
4055+
}
4056+
return timeStamp;
4057+
}
38834058

38844059
protected OrganizeImportsOperation createOperation(ICompilationUnit cu, String[] order, int threshold, boolean ignoreLowerCaseNames, boolean save, boolean allowSyntaxErrors, IChooseImportQuery chooseImportQuery) {
38854060
setOrganizeImportSettings(order, threshold, threshold, cu.getJavaProject());

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/CompilationUnitDocumentProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,9 @@ public void run() {
16171617
if (buffer.hasUnsavedChanges())
16181618
buffer.save(getSubProgressMonitor(monitor, 1), true);
16191619

1620+
if (stamp != unit.getResource().getModificationStamp()) {
1621+
unit.updateTimeStamp();
1622+
}
16201623
} catch (CoreException ex) {
16211624
handleException(ex);
16221625
} finally {

0 commit comments

Comments
 (0)