001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.actions;
011:
012: import java.util.ArrayList;
013: import java.util.Collection;
014: import java.util.HashMap;
015: import java.util.List;
016:
017: import org.eclipse.core.runtime.CoreException;
018: import org.eclipse.core.runtime.IPath;
019:
020: import org.eclipse.core.resources.IContainer;
021: import org.eclipse.core.resources.IFile;
022: import org.eclipse.core.resources.IFolder;
023: import org.eclipse.core.resources.IResource;
024: import org.eclipse.core.resources.IStorage;
025:
026: import org.eclipse.jface.dialogs.MessageDialog;
027: import org.eclipse.jface.viewers.ISelectionProvider;
028: import org.eclipse.jface.viewers.IStructuredSelection;
029: import org.eclipse.jface.viewers.StructuredSelection;
030:
031: import org.eclipse.jface.text.ITextSelection;
032:
033: import org.eclipse.ui.IWorkbenchSite;
034: import org.eclipse.ui.IWorkingSet;
035:
036: import org.eclipse.jdt.core.ICompilationUnit;
037: import org.eclipse.jdt.core.IField;
038: import org.eclipse.jdt.core.IImportDeclaration;
039: import org.eclipse.jdt.core.IInitializer;
040: import org.eclipse.jdt.core.IJavaElement;
041: import org.eclipse.jdt.core.IJavaProject;
042: import org.eclipse.jdt.core.IMethod;
043: import org.eclipse.jdt.core.IPackageFragment;
044: import org.eclipse.jdt.core.IPackageFragmentRoot;
045: import org.eclipse.jdt.core.IType;
046: import org.eclipse.jdt.core.JavaCore;
047: import org.eclipse.jdt.core.JavaModelException;
048: import org.eclipse.jdt.core.dom.Modifier;
049:
050: import org.eclipse.jdt.internal.corext.refactoring.nls.NLSHintHelper;
051: import org.eclipse.jdt.internal.corext.refactoring.nls.NLSRefactoring;
052:
053: import org.eclipse.jdt.ui.IWorkingCopyManager;
054: import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
055:
056: import org.eclipse.jdt.internal.ui.JavaPlugin;
057: import org.eclipse.jdt.internal.ui.browsing.LogicalPackage;
058: import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
059: import org.eclipse.jdt.internal.ui.refactoring.nls.search.SearchBrokenNLSKeysUtil;
060: import org.eclipse.jdt.internal.ui.workingsets.JavaWorkingSetUpdater;
061:
062: public class FindBrokenNLSKeysAction extends SelectionDispatchAction {
063:
064: private static class SearchPatternData {
065:
066: private final IType fAccessorType;
067: private final IFile fPropertyFile;
068:
069: public SearchPatternData(IType accessorType, IFile propertyFile) {
070: fAccessorType = accessorType;
071: fPropertyFile = propertyFile;
072: }
073:
074: public IFile getPropertyFile() {
075: return fPropertyFile;
076: }
077:
078: public IType getWrapperClass() {
079: return fAccessorType;
080: }
081:
082: }
083:
084: //TODO: Add to API: IJavaEditorActionDefinitionIds
085: public static final String FIND_BROKEN_NLS_KEYS_ACTION_ID = "org.eclipse.jdt.ui.edit.text.java.find.broken.nls.keys"; //$NON-NLS-1$
086:
087: //TODO: Add to API: JdtActionConstants
088: public static final String ACTION_HANDLER_ID = "org.eclipse.jdt.ui.actions.FindNLSProblems"; //$NON-NLS-1$
089:
090: private static final String JAVA_LANG_STRING = "QString;"; //$NON-NLS-1$
091:
092: private JavaEditor fEditor;
093:
094: public FindBrokenNLSKeysAction(IWorkbenchSite site) {
095: super (site);
096: setText(ActionMessages.FindNLSProblemsAction_Name);
097: setToolTipText(ActionMessages.FindNLSProblemsAction_ToolTip);
098: setDescription(ActionMessages.FindNLSProblemsAction_Description);
099: }
100:
101: /**
102: * Note: This constructor is for internal use only. Clients should not call this constructor.
103: * @param editor the Java editor
104: */
105: public FindBrokenNLSKeysAction(JavaEditor editor) {
106: this (editor.getEditorSite());
107: fEditor = editor;
108: setEnabled(getCompilationUnit(editor) != null);
109: }
110:
111: /* (non-Javadoc)
112: * Method declared on SelectionDispatchAction.
113: */
114: public void run(ITextSelection selection) {
115: ISelectionProvider selectionProvider = fEditor
116: .getSelectionProvider();
117: if (selectionProvider == null)
118: return;
119:
120: run(new StructuredSelection(selectionProvider.getSelection()));
121: }
122:
123: /* (non-Javadoc)
124: * Method declared on SelectionDispatchAction.
125: */
126: public void run(IStructuredSelection selection) {
127: if (selection.size() == 1) {
128: Object firstElement = selection.getFirstElement();
129: if (firstElement instanceof IJavaElement) {
130: IJavaElement javaElement = (IJavaElement) firstElement;
131: if (!ActionUtil.isProcessable(getShell(), javaElement)) {
132: return;
133: }
134: }
135: }
136:
137: SearchPatternData[] data = getNLSFiles(selection);
138: if (data == null || data.length == 0) {
139: MessageDialog
140: .openInformation(
141: getShell(),
142: ActionMessages.FindNLSProblemsAction_ErrorDialogTitle,
143: ActionMessages.FindNLSProblemsAction_NoPropertieFilesFoundErrorDescription);
144: return;
145: }
146:
147: String scope = "workspace"; //$NON-NLS-1$
148: if (selection.size() == 1) {
149: Object firstElement = selection.getFirstElement();
150: if (firstElement instanceof IJavaElement) {
151: scope = ((IJavaElement) firstElement).getElementName();
152: } else if (firstElement instanceof IFile) {
153: scope = ((IFile) firstElement).getName();
154: } else if (firstElement instanceof IFolder) {
155: scope = ((IFolder) firstElement).getName();
156: }
157: }
158: run(data, scope);
159: }
160:
161: private void run(SearchPatternData[] data, String scope) {
162: List wrappers = new ArrayList();
163: List properties = new ArrayList();
164: for (int i = 0; i < data.length; i++) {
165: SearchPatternData current = data[i];
166: if (current.getWrapperClass() != null
167: || current.getPropertyFile() != null) {
168: wrappers.add(current.getWrapperClass());
169: properties.add(current.getPropertyFile());
170: }
171: }
172: IType[] accessorClasses = (IType[]) wrappers
173: .toArray(new IType[wrappers.size()]);
174: IFile[] propertieFiles = (IFile[]) properties
175: .toArray(new IFile[properties.size()]);
176: SearchBrokenNLSKeysUtil.search(scope, accessorClasses,
177: propertieFiles);
178: }
179:
180: /* (non-Javadoc)
181: * Method declared on SelectionDispatchAction.
182: */
183: public void selectionChanged(ITextSelection selection) {
184: ISelectionProvider selectionProvider = fEditor
185: .getSelectionProvider();
186: if (selectionProvider == null) {
187: setEnabled(false);
188: } else {
189: selectionChanged(new StructuredSelection(selectionProvider
190: .getSelection()));
191: }
192: }
193:
194: /* (non-Javadoc)
195: * Method declared on SelectionDispatchAction.
196: */
197: public void selectionChanged(IStructuredSelection selection) {
198: setEnabled(canEnable(selection));
199: }
200:
201: private SearchPatternData[] getNLSFiles(
202: IStructuredSelection selection) {
203: Object[] selectedElements = selection.toArray();
204: HashMap result = new HashMap();
205:
206: collectNLSFilesFromResources(selectedElements, result);
207: collectNLSFilesFromJavaElements(selectedElements, result);
208:
209: Collection values = result.values();
210: return (SearchPatternData[]) values
211: .toArray(new SearchPatternData[values.size()]);
212: }
213:
214: private boolean canEnable(IStructuredSelection selection) {
215: Object[] selected = selection.toArray();
216: for (int i = 0; i < selected.length; i++) {
217: try {
218: if (selected[i] instanceof IJavaElement) {
219: IJavaElement elem = (IJavaElement) selected[i];
220: if (elem.exists()) {
221: switch (elem.getElementType()) {
222: case IJavaElement.TYPE:
223: if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) {
224: return true;
225: }
226: return false;
227: case IJavaElement.COMPILATION_UNIT:
228: return true;
229: case IJavaElement.IMPORT_CONTAINER:
230: return false;
231: case IJavaElement.PACKAGE_FRAGMENT:
232: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
233: IPackageFragmentRoot root = (IPackageFragmentRoot) elem
234: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
235: return (root.getKind() == IPackageFragmentRoot.K_SOURCE);
236: case IJavaElement.JAVA_PROJECT:
237: return true;
238: }
239: }
240: } else if (selected[i] instanceof LogicalPackage) {
241: return true;
242: } else if (selected[i] instanceof IFile) {
243: IFile file = (IFile) selected[i];
244: if ("properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$
245: return true;
246: } else if (selected[i] instanceof IWorkingSet) {
247: IWorkingSet workingSet = (IWorkingSet) selected[i];
248: return JavaWorkingSetUpdater.ID.equals(workingSet
249: .getId());
250: }
251: } catch (JavaModelException e) {
252: if (!e.isDoesNotExist()) {
253: JavaPlugin.log(e);
254: }
255: }
256: }
257: return false;
258: }
259:
260: private void collectNLSFilesFromResources(Object[] objects,
261: HashMap result) {
262: try {
263: for (int i = 0; i < objects.length; i++) {
264: Object object = objects[i];
265:
266: IResource resource = null;
267: if (object instanceof IWorkingSet) {
268: IWorkingSet workingSet = (IWorkingSet) object;
269: collectNLSFilesFromResources(workingSet
270: .getElements(), result);
271: } else if (object instanceof IJavaElement) {
272: resource = ((IJavaElement) object)
273: .getCorrespondingResource();
274: } else if (object instanceof IResource) {
275: resource = (IResource) object;
276: } else if (object instanceof LogicalPackage) {
277: LogicalPackage logicalPackage = (LogicalPackage) object;
278: resource = logicalPackage.getJavaProject()
279: .getProject();
280: }
281:
282: if (resource instanceof IContainer) {
283: collectNLSFilesFromResources(
284: ((IContainer) resource).members(), result);
285: } else if (resource instanceof IFile) {
286: SearchPatternData data = tryIfPropertyFileSelected((IFile) resource);
287: if (data != null
288: && !result.containsKey(data.fAccessorType)) {
289: result.put(data.fAccessorType, data);
290: }
291: }
292: }
293: } catch (JavaModelException e) {
294: if (!e.isDoesNotExist()) {
295: JavaPlugin.log(e);
296: }
297: } catch (CoreException e) {
298: JavaPlugin.log(e);
299: }
300: }
301:
302: private void collectNLSFilesFromJavaElements(Object[] objects,
303: HashMap result) {
304: try {
305: for (int i = 0; i < objects.length; i++) {
306: if (objects[i] instanceof IJavaElement) {
307: IJavaElement elem = (IJavaElement) objects[i];
308: if (elem.exists()) {
309: switch (elem.getElementType()) {
310: case IJavaElement.TYPE:
311: if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) {
312: ICompilationUnit unit = (ICompilationUnit) elem
313: .getParent();
314: if (!result
315: .containsKey(unit.getTypes()[0])) {
316: SearchPatternData data = tryIfPropertyCuSelected(unit);
317: if (data != null)
318: result.put(data.fAccessorType,
319: data);
320: }
321: }
322: break;
323: case IJavaElement.COMPILATION_UNIT:
324: ICompilationUnit unit = (ICompilationUnit) elem;
325: if (!result.containsKey(unit.getTypes()[0])) {
326: SearchPatternData data = tryIfPropertyCuSelected(unit);
327: if (data != null)
328: result
329: .put(data.fAccessorType,
330: data);
331: }
332: break;
333: case IJavaElement.PACKAGE_FRAGMENT:
334: IPackageFragment fragment = (IPackageFragment) elem;
335: if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE)
336: collectNLSFilesFromJavaElements(
337: fragment.getChildren(), result);
338: break;
339: case IJavaElement.PACKAGE_FRAGMENT_ROOT: {
340: IPackageFragmentRoot root = (IPackageFragmentRoot) elem;
341: if (root.getKind() == IPackageFragmentRoot.K_SOURCE)
342: collectNLSFilesFromJavaElements(root
343: .getChildren(), result);
344: break;
345: }
346: case IJavaElement.JAVA_PROJECT: {
347: IJavaProject javaProject = (IJavaProject) elem;
348: IPackageFragmentRoot[] allPackageFragmentRoots = javaProject
349: .getAllPackageFragmentRoots();
350: for (int j = 0; j < allPackageFragmentRoots.length; j++) {
351: IPackageFragmentRoot root = allPackageFragmentRoots[j];
352: if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
353: if (javaProject.equals(root
354: .getJavaProject())) {
355: collectNLSFilesFromJavaElements(
356: new Object[] { root },
357: result);
358: }
359: }
360: }
361: break;
362: }
363: }
364: }
365: } else if (objects[i] instanceof LogicalPackage) {
366: LogicalPackage logicalPackage = (LogicalPackage) objects[i];
367: collectNLSFilesFromJavaElements(
368: new Object[] { logicalPackage
369: .getJavaProject() }, result);
370: } else if (objects[i] instanceof IWorkingSet) {
371: IWorkingSet workingSet = (IWorkingSet) objects[i];
372: collectNLSFilesFromJavaElements(workingSet
373: .getElements(), result);
374: }
375: }
376: } catch (JavaModelException e) {
377: if (!e.isDoesNotExist()) {
378: JavaPlugin.log(e);
379: }
380: }
381: }
382:
383: private SearchPatternData tryIfPropertyCuSelected(
384: ICompilationUnit compilationUnit) throws JavaModelException {
385: if (compilationUnit == null)
386: return null;
387:
388: if (!ActionUtil.isOnBuildPath(compilationUnit))
389: return null;
390:
391: IType[] types = compilationUnit.getTypes();
392: if (types.length > 1)
393: return null;
394:
395: if (!isPotentialNLSAccessor(compilationUnit))
396: return null;
397:
398: IStorage bundle = NLSHintHelper
399: .getResourceBundle(compilationUnit);
400: if (!(bundle instanceof IFile))
401: return null;
402:
403: return new SearchPatternData(types[0], (IFile) bundle);
404: }
405:
406: /*
407: * Be conservative, for every unit this returns true an AST will to be created!
408: */
409: private static boolean isPotentialNLSAccessor(ICompilationUnit unit)
410: throws JavaModelException {
411: IType type = unit.getTypes()[0];
412: if (!type.exists())
413: return false;
414:
415: IField bundleNameField = getBundleNameField(type.getFields());
416: if (bundleNameField == null)
417: return false;
418:
419: if (importsOSGIUtil(unit)) { //new school
420: IInitializer[] initializers = type.getInitializers();
421: for (int i = 0; i < initializers.length; i++) {
422: if (Modifier.isStatic(initializers[0].getFlags()))
423: return true;
424: }
425: } else { //old school
426: IMethod[] methods = type.getMethods();
427: for (int i = 0; i < methods.length; i++) {
428: IMethod method = methods[i];
429: if (isValueAccessor(method))
430: return true;
431: }
432: }
433:
434: return false;
435: }
436:
437: private static boolean importsOSGIUtil(ICompilationUnit unit)
438: throws JavaModelException {
439: IImportDeclaration[] imports = unit.getImports();
440: for (int i = 0; i < imports.length; i++) {
441: if (imports[i].getElementName().startsWith(
442: "org.eclipse.osgi.util.")) //$NON-NLS-1$
443: return true;
444: }
445:
446: return false;
447: }
448:
449: private static boolean isValueAccessor(IMethod method)
450: throws JavaModelException {
451: if (!"getString".equals(method.getElementName())) //$NON-NLS-1$
452: return false;
453:
454: int flags = method.getFlags();
455: if (!Modifier.isStatic(flags) || !Modifier.isPublic(flags))
456: return false;
457:
458: String returnType = method.getReturnType();
459: if (!JAVA_LANG_STRING.equals(returnType))
460: return false;
461:
462: String[] parameters = method.getParameterTypes();
463: if (parameters.length != 1
464: || !JAVA_LANG_STRING.equals(parameters[0]))
465: return false;
466:
467: return true;
468: }
469:
470: private static IField getBundleNameField(IField[] fields) {
471: for (int i = 0; i < fields.length; i++) {
472: if ("BUNDLE_NAME".equals(fields[i].getElementName())) //$NON-NLS-1$
473: return fields[i];
474: }
475:
476: return null;
477: }
478:
479: private SearchPatternData tryIfPropertyFileSelected(IFile file)
480: throws JavaModelException {
481: if (!"properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$
482: return null;
483:
484: IPath propertyFullPath = file.getFullPath();
485: // Try to find a corresponding CU
486: String[] javaExtensions = JavaCore.getJavaLikeExtensions();
487: for (int i = 0; i < javaExtensions.length; i++) {
488: String extension = javaExtensions[i];
489: IPath cuPath = propertyFullPath.removeFileExtension()
490: .addFileExtension(extension);
491: IFile cuFile = (IFile) JavaPlugin.getWorkspace().getRoot()
492: .findMember(cuPath);
493:
494: if (cuFile == null) { //try with uppercase first char
495: String filename = cuPath.removeFileExtension()
496: .lastSegment();
497: if (filename != null && filename.length() > 0) {
498: filename = Character
499: .toUpperCase(filename.charAt(0))
500: + filename.substring(1);
501: IPath dirPath = propertyFullPath
502: .removeLastSegments(1)
503: .addTrailingSeparator();
504: cuPath = dirPath.append(filename).addFileExtension(
505: extension);
506: cuFile = (IFile) JavaPlugin.getWorkspace()
507: .getRoot().findMember(cuPath);
508: }
509: }
510:
511: if (cuFile != null && cuFile.exists()) {
512: IJavaElement element = JavaCore.create(cuFile);
513: if (element != null
514: && element.exists()
515: && element.getElementType() == IJavaElement.COMPILATION_UNIT
516: && ActionUtil.isOnBuildPath(element)) {
517: ICompilationUnit compilationUnit = (ICompilationUnit) element;
518: IType type = compilationUnit.findPrimaryType();
519: if (type != null) {
520: String resourceBundleName = NLSHintHelper
521: .getResourceBundleName(compilationUnit);
522: if (resourceBundleName != null) {
523: String resourceName = resourceBundleName
524: + NLSRefactoring.PROPERTY_FILE_EXT;
525: String name = file.getName();
526: if (resourceName.endsWith(name)) {
527: return new SearchPatternData(type, file);
528: }
529: }
530: }
531: }
532: }
533: }
534:
535: return null;
536: }
537:
538: private static ICompilationUnit getCompilationUnit(JavaEditor editor) {
539: IWorkingCopyManager manager = JavaPlugin.getDefault()
540: .getWorkingCopyManager();
541: ICompilationUnit cu = manager.getWorkingCopy(editor
542: .getEditorInput());
543: return cu;
544: }
545:
546: }
|