]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2012 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.ui.actions; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.Collection; | |
15 | import java.util.HashMap; | |
16 | import java.util.List; | |
17 | ||
18 | import org.eclipse.core.runtime.CoreException; | |
19 | import org.eclipse.core.runtime.IPath; | |
20 | ||
21 | import org.eclipse.core.resources.IContainer; | |
22 | import org.eclipse.core.resources.IFile; | |
23 | import org.eclipse.core.resources.IFolder; | |
24 | import org.eclipse.core.resources.IResource; | |
25 | import org.eclipse.core.resources.IStorage; | |
26 | ||
27 | import org.eclipse.jface.dialogs.MessageDialog; | |
28 | import org.eclipse.jface.viewers.ISelectionProvider; | |
29 | import org.eclipse.jface.viewers.IStructuredSelection; | |
30 | import org.eclipse.jface.viewers.StructuredSelection; | |
31 | ||
32 | import org.eclipse.jface.text.ITextSelection; | |
33 | ||
34 | import org.eclipse.ui.IWorkbenchSite; | |
35 | import org.eclipse.ui.IWorkingSet; | |
36 | ||
37 | import org.eclipse.jdt.core.ICompilationUnit; | |
38 | import org.eclipse.jdt.core.IField; | |
39 | import org.eclipse.jdt.core.IImportDeclaration; | |
40 | import org.eclipse.jdt.core.IInitializer; | |
41 | import org.eclipse.jdt.core.IJavaElement; | |
42 | import org.eclipse.jdt.core.IJavaProject; | |
43 | import org.eclipse.jdt.core.IMethod; | |
44 | import org.eclipse.jdt.core.IPackageFragment; | |
45 | import org.eclipse.jdt.core.IPackageFragmentRoot; | |
46 | import org.eclipse.jdt.core.IType; | |
47 | import org.eclipse.jdt.core.JavaCore; | |
48 | import org.eclipse.jdt.core.JavaModelException; | |
49 | import org.eclipse.jdt.core.dom.Modifier; | |
50 | ||
51 | import org.eclipse.jdt.internal.corext.refactoring.nls.NLSHintHelper; | |
52 | import org.eclipse.jdt.internal.corext.refactoring.nls.NLSRefactoring; | |
53 | ||
54 | import org.eclipse.jdt.ui.IWorkingCopyManager; | |
55 | import org.eclipse.jdt.ui.actions.SelectionDispatchAction; | |
56 | ||
57 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
58 | import org.eclipse.jdt.internal.ui.browsing.LogicalPackage; | |
59 | import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; | |
60 | import org.eclipse.jdt.internal.ui.refactoring.nls.search.SearchBrokenNLSKeysUtil; | |
61 | import org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs; | |
62 | ||
63 | public class FindBrokenNLSKeysAction extends SelectionDispatchAction { | |
64 | ||
65 | private static class SearchPatternData { | |
66 | ||
67 | private final IType fAccessorType; | |
68 | private final IFile fPropertyFile; | |
69 | ||
70 | public SearchPatternData(IType accessorType, IFile propertyFile) { | |
71 | fAccessorType= accessorType; | |
72 | fPropertyFile= propertyFile; | |
73 | } | |
74 | ||
75 | public IFile getPropertyFile() { | |
76 | return fPropertyFile; | |
77 | } | |
78 | ||
79 | public IType getWrapperClass() { | |
80 | return fAccessorType; | |
81 | } | |
82 | ||
83 | } | |
84 | ||
85 | //TODO: Add to API: IJavaEditorActionDefinitionIds | |
86 | public static final String FIND_BROKEN_NLS_KEYS_ACTION_ID= "org.eclipse.jdt.ui.edit.text.java.find.broken.nls.keys"; //$NON-NLS-1$ | |
87 | ||
88 | //TODO: Add to API: JdtActionConstants | |
89 | public static final String ACTION_HANDLER_ID= "org.eclipse.jdt.ui.actions.FindNLSProblems"; //$NON-NLS-1$ | |
90 | ||
91 | private static final String JAVA_LANG_STRING= "QString;"; //$NON-NLS-1$ | |
92 | ||
93 | private JavaEditor fEditor; | |
94 | ||
95 | public FindBrokenNLSKeysAction(IWorkbenchSite site) { | |
96 | super(site); | |
97 | setText(ActionMessages.FindNLSProblemsAction_Name); | |
98 | setToolTipText(ActionMessages.FindNLSProblemsAction_ToolTip); | |
99 | setDescription(ActionMessages.FindNLSProblemsAction_Description); | |
100 | } | |
101 | ||
102 | /** | |
103 | * Note: This constructor is for internal use only. Clients should not call this constructor. | |
104 | * @param editor the Java editor | |
105 | */ | |
106 | public FindBrokenNLSKeysAction(JavaEditor editor) { | |
107 | this(editor.getEditorSite()); | |
108 | fEditor= editor; | |
109 | setEnabled(getCompilationUnit(editor) != null); | |
110 | } | |
111 | ||
112 | /* (non-Javadoc) | |
113 | * Method declared on SelectionDispatchAction. | |
114 | */ | |
115 | @Override | |
116 | public void run(ITextSelection selection) { | |
117 | ISelectionProvider selectionProvider= fEditor.getSelectionProvider(); | |
118 | if (selectionProvider == null) | |
119 | return; | |
120 | ||
121 | run(new StructuredSelection(selectionProvider.getSelection())); | |
122 | } | |
123 | ||
124 | /* (non-Javadoc) | |
125 | * Method declared on SelectionDispatchAction. | |
126 | */ | |
127 | @Override | |
128 | public void run(IStructuredSelection selection) { | |
129 | if (selection.size() == 1) { | |
130 | Object firstElement= selection.getFirstElement(); | |
131 | if (firstElement instanceof IJavaElement) { | |
132 | IJavaElement javaElement= (IJavaElement) firstElement; | |
133 | if (!ActionUtil.isProcessable(getShell(), javaElement)) { | |
134 | return; | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | SearchPatternData[] data= getNLSFiles(selection); | |
140 | if (data == null || data.length == 0) { | |
141 | MessageDialog.openInformation(getShell(), ActionMessages.FindNLSProblemsAction_ErrorDialogTitle, ActionMessages.FindNLSProblemsAction_NoPropertieFilesFoundErrorDescription); | |
142 | return; | |
143 | } | |
144 | ||
145 | String scope= "workspace"; //$NON-NLS-1$ | |
146 | if (selection.size() == 1) { | |
147 | Object firstElement= selection.getFirstElement(); | |
148 | if (firstElement instanceof IJavaElement) { | |
149 | scope= ((IJavaElement)firstElement).getElementName(); | |
150 | } else if (firstElement instanceof IFile) { | |
151 | scope= ((IFile)firstElement).getName(); | |
152 | } else if (firstElement instanceof IFolder) { | |
153 | scope= ((IFolder)firstElement).getName(); | |
154 | } | |
155 | } | |
156 | run(data, scope); | |
157 | } | |
158 | ||
159 | private void run(SearchPatternData[] data, String scope) { | |
160 | List<IType> wrappers= new ArrayList<IType>(); | |
161 | List<IFile> properties= new ArrayList<IFile>(); | |
162 | for (int i= 0; i < data.length; i++) { | |
163 | SearchPatternData current= data[i]; | |
164 | if (current.getWrapperClass() != null || current.getPropertyFile() != null) { | |
165 | wrappers.add(current.getWrapperClass()); | |
166 | properties.add(current.getPropertyFile()); | |
167 | } | |
168 | } | |
169 | IType[] accessorClasses= wrappers.toArray(new IType[wrappers.size()]); | |
170 | IFile[] propertieFiles= properties.toArray(new IFile[properties.size()]); | |
171 | SearchBrokenNLSKeysUtil.search(scope, accessorClasses, propertieFiles); | |
172 | } | |
173 | ||
174 | /* (non-Javadoc) | |
175 | * Method declared on SelectionDispatchAction. | |
176 | */ | |
177 | @Override | |
178 | public void selectionChanged(ITextSelection selection) { | |
179 | ISelectionProvider selectionProvider= fEditor.getSelectionProvider(); | |
180 | if (selectionProvider == null) { | |
181 | setEnabled(false); | |
182 | } else { | |
183 | selectionChanged(new StructuredSelection(selectionProvider.getSelection())); | |
184 | } | |
185 | } | |
186 | ||
187 | /* (non-Javadoc) | |
188 | * Method declared on SelectionDispatchAction. | |
189 | */ | |
190 | @Override | |
191 | public void selectionChanged(IStructuredSelection selection) { | |
192 | setEnabled(canEnable(selection)); | |
193 | } | |
194 | ||
195 | private SearchPatternData[] getNLSFiles(IStructuredSelection selection) { | |
196 | Object[] selectedElements= selection.toArray(); | |
197 | HashMap<IType, SearchPatternData> result= new HashMap<IType, SearchPatternData>(); | |
198 | ||
199 | collectNLSFilesFromResources(selectedElements, result); | |
200 | collectNLSFilesFromJavaElements(selectedElements, result); | |
201 | ||
202 | Collection<SearchPatternData> values= result.values(); | |
203 | return values.toArray(new SearchPatternData[values.size()]); | |
204 | } | |
205 | ||
206 | private boolean canEnable(IStructuredSelection selection) { | |
207 | Object[] selected= selection.toArray(); | |
208 | for (int i= 0; i < selected.length; i++) { | |
209 | try { | |
210 | if (selected[i] instanceof IJavaElement) { | |
211 | IJavaElement elem= (IJavaElement) selected[i]; | |
212 | if (elem.exists()) { | |
213 | switch (elem.getElementType()) { | |
214 | case IJavaElement.TYPE: | |
215 | if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) { | |
216 | return true; | |
217 | } | |
218 | return false; | |
219 | case IJavaElement.COMPILATION_UNIT: | |
220 | return true; | |
221 | case IJavaElement.IMPORT_CONTAINER: | |
222 | return false; | |
223 | case IJavaElement.PACKAGE_FRAGMENT: | |
224 | case IJavaElement.PACKAGE_FRAGMENT_ROOT: | |
225 | IPackageFragmentRoot root= (IPackageFragmentRoot) elem.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); | |
226 | return (root.getKind() == IPackageFragmentRoot.K_SOURCE); | |
227 | case IJavaElement.JAVA_PROJECT: | |
228 | return true; | |
229 | } | |
230 | } | |
231 | } else if (selected[i] instanceof LogicalPackage) { | |
232 | return true; | |
233 | } else if (selected[i] instanceof IFile) { | |
234 | IFile file= (IFile)selected[i]; | |
235 | if ("properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$ | |
236 | return true; | |
237 | } else if (selected[i] instanceof IWorkingSet) { | |
238 | IWorkingSet workingSet= (IWorkingSet) selected[i]; | |
239 | return IWorkingSetIDs.JAVA.equals(workingSet.getId()); | |
240 | } | |
241 | } catch (JavaModelException e) { | |
242 | if (!e.isDoesNotExist()) { | |
243 | JavaPlugin.log(e); | |
244 | } | |
245 | } | |
246 | } | |
247 | return false; | |
248 | } | |
249 | ||
250 | private void collectNLSFilesFromResources(Object[] objects, HashMap<IType, SearchPatternData> result) { | |
251 | try { | |
252 | for (int i= 0; i < objects.length; i++) { | |
253 | Object object= objects[i]; | |
254 | ||
255 | IResource resource= null; | |
256 | if (object instanceof IWorkingSet) { | |
257 | IWorkingSet workingSet= (IWorkingSet) object; | |
258 | collectNLSFilesFromResources(workingSet.getElements(), result); | |
259 | } else if (object instanceof IJavaElement) { | |
260 | resource= ((IJavaElement) object).getCorrespondingResource(); | |
261 | } else if (object instanceof IResource) { | |
262 | resource= (IResource) object; | |
263 | } else if (object instanceof LogicalPackage) { | |
264 | LogicalPackage logicalPackage= (LogicalPackage)object; | |
265 | resource= logicalPackage.getJavaProject().getProject(); | |
266 | } | |
267 | ||
268 | if (resource instanceof IContainer) { | |
269 | collectNLSFilesFromResources(((IContainer)resource).members(), result); | |
270 | } else if (resource instanceof IFile) { | |
271 | SearchPatternData data= tryIfPropertyFileSelected((IFile) resource); | |
272 | if (data != null && !result.containsKey(data.fAccessorType)) { | |
273 | result.put(data.fAccessorType, data); | |
274 | } | |
275 | } | |
276 | } | |
277 | } catch (JavaModelException e) { | |
278 | if (!e.isDoesNotExist()) { | |
279 | JavaPlugin.log(e); | |
280 | } | |
281 | } catch (CoreException e) { | |
282 | JavaPlugin.log(e); | |
283 | } | |
284 | } | |
285 | ||
286 | private void collectNLSFilesFromJavaElements(Object[] objects, HashMap<IType, SearchPatternData> result) { | |
287 | try { | |
288 | for (int i= 0; i < objects.length; i++) { | |
289 | if (objects[i] instanceof IJavaElement) { | |
290 | IJavaElement elem= (IJavaElement) objects[i]; | |
291 | if (elem.exists()) { | |
292 | switch (elem.getElementType()) { | |
293 | case IJavaElement.TYPE: | |
294 | if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) { | |
295 | ICompilationUnit unit= (ICompilationUnit)elem.getParent(); | |
296 | IType[] types= unit.getTypes(); | |
297 | if (types.length > 0 && !result.containsKey(types[0])) { | |
298 | SearchPatternData data= tryIfPropertyCuSelected(unit); | |
299 | if (data != null) | |
300 | result.put(data.fAccessorType, data); | |
301 | } | |
302 | } | |
303 | break; | |
304 | case IJavaElement.COMPILATION_UNIT: | |
305 | ICompilationUnit unit= (ICompilationUnit)elem; | |
306 | IType[] types= unit.getTypes(); | |
307 | if (types.length > 0 && !result.containsKey(types[0])) { | |
308 | SearchPatternData data= tryIfPropertyCuSelected(unit); | |
309 | if (data != null) | |
310 | result.put(data.fAccessorType, data); | |
311 | } | |
312 | break; | |
313 | case IJavaElement.PACKAGE_FRAGMENT: | |
314 | IPackageFragment fragment= (IPackageFragment)elem; | |
315 | if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) | |
316 | collectNLSFilesFromJavaElements(fragment.getChildren(), result); | |
317 | break; | |
318 | case IJavaElement.PACKAGE_FRAGMENT_ROOT: | |
319 | { | |
320 | IPackageFragmentRoot root= (IPackageFragmentRoot) elem; | |
321 | if (root.getKind() == IPackageFragmentRoot.K_SOURCE) | |
322 | collectNLSFilesFromJavaElements(root.getChildren(), result); | |
323 | break; | |
324 | } | |
325 | case IJavaElement.JAVA_PROJECT: | |
326 | { | |
327 | IJavaProject javaProject= (IJavaProject)elem; | |
328 | IPackageFragmentRoot[] allPackageFragmentRoots= javaProject.getAllPackageFragmentRoots(); | |
329 | for (int j= 0; j < allPackageFragmentRoots.length; j++) { | |
330 | IPackageFragmentRoot root= allPackageFragmentRoots[j]; | |
331 | if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { | |
332 | if (javaProject.equals(root.getJavaProject())) { | |
333 | collectNLSFilesFromJavaElements(new Object[] {root}, result); | |
334 | } | |
335 | } | |
336 | } | |
337 | break; | |
338 | } | |
339 | } | |
340 | } | |
341 | } else if (objects[i] instanceof LogicalPackage) { | |
342 | LogicalPackage logicalPackage= (LogicalPackage)objects[i]; | |
343 | collectNLSFilesFromJavaElements(new Object[] {logicalPackage.getJavaProject()}, result); | |
344 | } else if (objects[i] instanceof IWorkingSet) { | |
345 | IWorkingSet workingSet= (IWorkingSet) objects[i]; | |
346 | collectNLSFilesFromJavaElements(workingSet.getElements(), result); | |
347 | } | |
348 | } | |
349 | } catch (JavaModelException e) { | |
350 | if (!e.isDoesNotExist()) { | |
351 | JavaPlugin.log(e); | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | private SearchPatternData tryIfPropertyCuSelected(ICompilationUnit compilationUnit) throws JavaModelException { | |
357 | IStorage bundle= getResourceBundle(compilationUnit); | |
358 | if (!(bundle instanceof IFile)) | |
359 | return null; | |
360 | ||
361 | return new SearchPatternData(compilationUnit.getTypes()[0], (IFile) bundle); | |
362 | } | |
363 | ||
364 | public static IStorage getResourceBundle(ICompilationUnit compilationUnit) throws JavaModelException { | |
365 | if (compilationUnit == null) | |
366 | return null; | |
367 | ||
368 | if (!ActionUtil.isOnBuildPath(compilationUnit)) | |
369 | return null; | |
370 | ||
371 | IType[] types= compilationUnit.getTypes(); | |
372 | if (types.length != 1) | |
373 | return null; | |
374 | ||
375 | if (!isPotentialNLSAccessor(compilationUnit)) | |
376 | return null; | |
377 | ||
378 | return NLSHintHelper.getResourceBundle(compilationUnit); | |
379 | } | |
380 | ||
381 | /* | |
382 | * Be conservative, for every unit this returns true an AST will to be created! | |
383 | */ | |
384 | private static boolean isPotentialNLSAccessor(ICompilationUnit unit) throws JavaModelException { | |
385 | IType type= unit.getTypes()[0]; | |
386 | if (!type.exists()) | |
387 | return false; | |
388 | ||
389 | IField bundleNameField= getBundleNameField(type.getFields()); | |
390 | if (bundleNameField == null) | |
391 | return false; | |
392 | ||
393 | if (importsOSGIUtil(unit)) { //new school | |
394 | IInitializer[] initializers= type.getInitializers(); | |
395 | for (int i= 0; i < initializers.length; i++) { | |
396 | if (Modifier.isStatic(initializers[0].getFlags())) | |
397 | return true; | |
398 | } | |
399 | } else { //old school | |
400 | IMethod[] methods= type.getMethods(); | |
401 | for (int i= 0; i < methods.length; i++) { | |
402 | IMethod method= methods[i]; | |
403 | if (isValueAccessor(method)) | |
404 | return true; | |
405 | } | |
406 | } | |
407 | ||
408 | return false; | |
409 | } | |
410 | ||
411 | private static boolean importsOSGIUtil(ICompilationUnit unit) throws JavaModelException { | |
412 | IImportDeclaration[] imports= unit.getImports(); | |
413 | for (int i= 0; i < imports.length; i++) { | |
414 | if (imports[i].getElementName().startsWith("org.eclipse.osgi.util.")) //$NON-NLS-1$ | |
415 | return true; | |
416 | } | |
417 | ||
418 | return false; | |
419 | } | |
420 | ||
421 | private static boolean isValueAccessor(IMethod method) throws JavaModelException { | |
422 | if (!"getString".equals(method.getElementName())) //$NON-NLS-1$ | |
423 | return false; | |
424 | ||
425 | int flags= method.getFlags(); | |
426 | if (!Modifier.isStatic(flags) || !Modifier.isPublic(flags)) | |
427 | return false; | |
428 | ||
429 | String returnType= method.getReturnType(); | |
430 | if (!JAVA_LANG_STRING.equals(returnType)) | |
431 | return false; | |
432 | ||
433 | String[] parameters= method.getParameterTypes(); | |
434 | if (parameters.length != 1 || !JAVA_LANG_STRING.equals(parameters[0])) | |
435 | return false; | |
436 | ||
437 | return true; | |
438 | } | |
439 | ||
440 | private static IField getBundleNameField(IField[] fields) { | |
441 | for (int i= 0; i < fields.length; i++) { | |
442 | if ("BUNDLE_NAME".equals(fields[i].getElementName())) //$NON-NLS-1$ | |
443 | return fields[i]; | |
444 | } | |
445 | ||
446 | return null; | |
447 | } | |
448 | ||
449 | private SearchPatternData tryIfPropertyFileSelected(IFile file) { | |
450 | IType accessorType= getAccessorType(file); | |
451 | return (accessorType != null) ? new SearchPatternData(accessorType, file) : null; | |
452 | } | |
453 | ||
454 | public static IType getAccessorType(IFile file) { | |
455 | if (!"properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$ | |
456 | return null; | |
457 | ||
458 | IPath propertyFullPath= file.getFullPath(); | |
459 | // Try to find a corresponding CU | |
460 | String[] javaExtensions= JavaCore.getJavaLikeExtensions(); | |
461 | for (int i= 0; i < javaExtensions.length; i++) { | |
462 | String extension= javaExtensions[i]; | |
463 | IPath cuPath= propertyFullPath.removeFileExtension().addFileExtension(extension); | |
464 | IFile cuFile= (IFile)JavaPlugin.getWorkspace().getRoot().findMember(cuPath); | |
465 | ||
466 | if (cuFile == null) { //try with uppercase first char | |
467 | String filename= cuPath.removeFileExtension().lastSegment(); | |
468 | if (filename != null && filename.length() > 0) { | |
469 | filename= Character.toUpperCase(filename.charAt(0)) + filename.substring(1); | |
470 | IPath dirPath= propertyFullPath.removeLastSegments(1).addTrailingSeparator(); | |
471 | cuPath= dirPath.append(filename).addFileExtension(extension); | |
472 | cuFile= (IFile)JavaPlugin.getWorkspace().getRoot().findMember(cuPath); | |
473 | } | |
474 | } | |
475 | ||
476 | if (cuFile != null && cuFile.exists()) { | |
477 | IJavaElement element= JavaCore.create(cuFile); | |
478 | if (element != null && element.exists() && element.getElementType() == IJavaElement.COMPILATION_UNIT && ActionUtil.isOnBuildPath(element)) { | |
479 | ICompilationUnit compilationUnit= (ICompilationUnit)element; | |
480 | IType type= compilationUnit.findPrimaryType(); | |
481 | if (type != null) { | |
482 | String resourceBundleName= NLSHintHelper.getResourceBundleName(compilationUnit); | |
483 | if (resourceBundleName != null) { | |
484 | String resourceName= resourceBundleName + NLSRefactoring.PROPERTY_FILE_EXT; | |
485 | String name= file.getName(); | |
486 | if (resourceName.endsWith(name)) { | |
487 | return type; | |
488 | } | |
489 | } | |
490 | } | |
491 | } | |
492 | } | |
493 | } | |
494 | ||
495 | return null; | |
496 | } | |
497 | ||
498 | private static ICompilationUnit getCompilationUnit(JavaEditor editor) { | |
499 | IWorkingCopyManager manager= JavaPlugin.getDefault().getWorkingCopyManager(); | |
500 | ICompilationUnit cu= manager.getWorkingCopy(editor.getEditorInput()); | |
501 | return cu; | |
502 | } | |
503 | ||
504 | } |