blob: 71deb7b2c80e74e23f26956878751f2c482c2cec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.actions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IInitializer;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.internal.corext.refactoring.nls.NLSHintHelper;
import org.eclipse.jdt.internal.corext.refactoring.nls.NLSRefactoring;
import org.eclipse.jdt.ui.IWorkingCopyManager;
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.browsing.LogicalPackage;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.refactoring.nls.search.SearchBrokenNLSKeysUtil;
import org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs;
public class FindBrokenNLSKeysAction extends SelectionDispatchAction {
private static class SearchPatternData {
private final IType fAccessorType;
private final IFile fPropertyFile;
public SearchPatternData(IType accessorType, IFile propertyFile) {
fAccessorType= accessorType;
fPropertyFile= propertyFile;
}
public IFile getPropertyFile() {
return fPropertyFile;
}
public IType getWrapperClass() {
return fAccessorType;
}
}
//TODO: Add to API: IJavaEditorActionDefinitionIds
public static final String FIND_BROKEN_NLS_KEYS_ACTION_ID= "org.eclipse.jdt.ui.edit.text.java.find.broken.nls.keys"; //$NON-NLS-1$
//TODO: Add to API: JdtActionConstants
public static final String ACTION_HANDLER_ID= "org.eclipse.jdt.ui.actions.FindNLSProblems"; //$NON-NLS-1$
private static final String JAVA_LANG_STRING= "QString;"; //$NON-NLS-1$
private JavaEditor fEditor;
public FindBrokenNLSKeysAction(IWorkbenchSite site) {
super(site);
setText(ActionMessages.FindNLSProblemsAction_Name);
setToolTipText(ActionMessages.FindNLSProblemsAction_ToolTip);
setDescription(ActionMessages.FindNLSProblemsAction_Description);
}
/**
* Note: This constructor is for internal use only. Clients should not call this constructor.
* @param editor the Java editor
*/
public FindBrokenNLSKeysAction(JavaEditor editor) {
this(editor.getEditorSite());
fEditor= editor;
setEnabled(getCompilationUnit(editor) != null);
}
/* (non-Javadoc)
* Method declared on SelectionDispatchAction.
*/
@Override
public void run(ITextSelection selection) {
ISelectionProvider selectionProvider= fEditor.getSelectionProvider();
if (selectionProvider == null)
return;
run(new StructuredSelection(selectionProvider.getSelection()));
}
/* (non-Javadoc)
* Method declared on SelectionDispatchAction.
*/
@Override
public void run(IStructuredSelection selection) {
if (selection.size() == 1) {
Object firstElement= selection.getFirstElement();
if (firstElement instanceof IJavaElement) {
IJavaElement javaElement= (IJavaElement) firstElement;
if (!ActionUtil.isProcessable(getShell(), javaElement)) {
return;
}
}
}
SearchPatternData[] data= getNLSFiles(selection);
if (data == null || data.length == 0) {
MessageDialog.openInformation(getShell(), ActionMessages.FindNLSProblemsAction_ErrorDialogTitle, ActionMessages.FindNLSProblemsAction_NoPropertieFilesFoundErrorDescription);
return;
}
String scope= "workspace"; //$NON-NLS-1$
if (selection.size() == 1) {
Object firstElement= selection.getFirstElement();
if (firstElement instanceof IJavaElement) {
scope= ((IJavaElement)firstElement).getElementName();
} else if (firstElement instanceof IFile) {
scope= ((IFile)firstElement).getName();
} else if (firstElement instanceof IFolder) {
scope= ((IFolder)firstElement).getName();
}
}
run(data, scope);
}
private void run(SearchPatternData[] data, String scope) {
List<IType> wrappers= new ArrayList<IType>();
List<IFile> properties= new ArrayList<IFile>();
for (int i= 0; i < data.length; i++) {
SearchPatternData current= data[i];
if (current.getWrapperClass() != null || current.getPropertyFile() != null) {
wrappers.add(current.getWrapperClass());
properties.add(current.getPropertyFile());
}
}
IType[] accessorClasses= wrappers.toArray(new IType[wrappers.size()]);
IFile[] propertieFiles= properties.toArray(new IFile[properties.size()]);
SearchBrokenNLSKeysUtil.search(scope, accessorClasses, propertieFiles);
}
/* (non-Javadoc)
* Method declared on SelectionDispatchAction.
*/
@Override
public void selectionChanged(ITextSelection selection) {
ISelectionProvider selectionProvider= fEditor.getSelectionProvider();
if (selectionProvider == null) {
setEnabled(false);
} else {
selectionChanged(new StructuredSelection(selectionProvider.getSelection()));
}
}
/* (non-Javadoc)
* Method declared on SelectionDispatchAction.
*/
@Override
public void selectionChanged(IStructuredSelection selection) {
setEnabled(canEnable(selection));
}
private SearchPatternData[] getNLSFiles(IStructuredSelection selection) {
Object[] selectedElements= selection.toArray();
HashMap<IType, SearchPatternData> result= new HashMap<IType, SearchPatternData>();
collectNLSFilesFromResources(selectedElements, result);
collectNLSFilesFromJavaElements(selectedElements, result);
Collection<SearchPatternData> values= result.values();
return values.toArray(new SearchPatternData[values.size()]);
}
private boolean canEnable(IStructuredSelection selection) {
Object[] selected= selection.toArray();
for (int i= 0; i < selected.length; i++) {
try {
if (selected[i] instanceof IJavaElement) {
IJavaElement elem= (IJavaElement) selected[i];
if (elem.exists()) {
switch (elem.getElementType()) {
case IJavaElement.TYPE:
if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) {
return true;
}
return false;
case IJavaElement.COMPILATION_UNIT:
return true;
case IJavaElement.IMPORT_CONTAINER:
return false;
case IJavaElement.PACKAGE_FRAGMENT:
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
IPackageFragmentRoot root= (IPackageFragmentRoot) elem.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
return (root.getKind() == IPackageFragmentRoot.K_SOURCE);
case IJavaElement.JAVA_PROJECT:
return true;
}
}
} else if (selected[i] instanceof LogicalPackage) {
return true;
} else if (selected[i] instanceof IFile) {
IFile file= (IFile)selected[i];
if ("properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$
return true;
} else if (selected[i] instanceof IWorkingSet) {
IWorkingSet workingSet= (IWorkingSet) selected[i];
return IWorkingSetIDs.JAVA.equals(workingSet.getId());
}
} catch (JavaModelException e) {
if (!e.isDoesNotExist()) {
JavaPlugin.log(e);
}
}
}
return false;
}
private void collectNLSFilesFromResources(Object[] objects, HashMap<IType, SearchPatternData> result) {
try {
for (int i= 0; i < objects.length; i++) {
Object object= objects[i];
IResource resource= null;
if (object instanceof IWorkingSet) {
IWorkingSet workingSet= (IWorkingSet) object;
collectNLSFilesFromResources(workingSet.getElements(), result);
} else if (object instanceof IJavaElement) {
resource= ((IJavaElement) object).getCorrespondingResource();
} else if (object instanceof IResource) {
resource= (IResource) object;
} else if (object instanceof LogicalPackage) {
LogicalPackage logicalPackage= (LogicalPackage)object;
resource= logicalPackage.getJavaProject().getProject();
}
if (resource instanceof IContainer) {
collectNLSFilesFromResources(((IContainer)resource).members(), result);
} else if (resource instanceof IFile) {
SearchPatternData data= tryIfPropertyFileSelected((IFile) resource);
if (data != null && !result.containsKey(data.fAccessorType)) {
result.put(data.fAccessorType, data);
}
}
}
} catch (JavaModelException e) {
if (!e.isDoesNotExist()) {
JavaPlugin.log(e);
}
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
private void collectNLSFilesFromJavaElements(Object[] objects, HashMap<IType, SearchPatternData> result) {
try {
for (int i= 0; i < objects.length; i++) {
if (objects[i] instanceof IJavaElement) {
IJavaElement elem= (IJavaElement) objects[i];
if (elem.exists()) {
switch (elem.getElementType()) {
case IJavaElement.TYPE:
if (elem.getParent().getElementType() == IJavaElement.COMPILATION_UNIT) {
ICompilationUnit unit= (ICompilationUnit)elem.getParent();
IType[] types= unit.getTypes();
if (types.length > 0 && !result.containsKey(types[0])) {
SearchPatternData data= tryIfPropertyCuSelected(unit);
if (data != null)
result.put(data.fAccessorType, data);
}
}
break;
case IJavaElement.COMPILATION_UNIT:
ICompilationUnit unit= (ICompilationUnit)elem;
IType[] types= unit.getTypes();
if (types.length > 0 && !result.containsKey(types[0])) {
SearchPatternData data= tryIfPropertyCuSelected(unit);
if (data != null)
result.put(data.fAccessorType, data);
}
break;
case IJavaElement.PACKAGE_FRAGMENT:
IPackageFragment fragment= (IPackageFragment)elem;
if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE)
collectNLSFilesFromJavaElements(fragment.getChildren(), result);
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
{
IPackageFragmentRoot root= (IPackageFragmentRoot) elem;
if (root.getKind() == IPackageFragmentRoot.K_SOURCE)
collectNLSFilesFromJavaElements(root.getChildren(), result);
break;
}
case IJavaElement.JAVA_PROJECT:
{
IJavaProject javaProject= (IJavaProject)elem;
IPackageFragmentRoot[] allPackageFragmentRoots= javaProject.getAllPackageFragmentRoots();
for (int j= 0; j < allPackageFragmentRoots.length; j++) {
IPackageFragmentRoot root= allPackageFragmentRoots[j];
if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
if (javaProject.equals(root.getJavaProject())) {
collectNLSFilesFromJavaElements(new Object[] {root}, result);
}
}
}
break;
}
}
}
} else if (objects[i] instanceof LogicalPackage) {
LogicalPackage logicalPackage= (LogicalPackage)objects[i];
collectNLSFilesFromJavaElements(new Object[] {logicalPackage.getJavaProject()}, result);
} else if (objects[i] instanceof IWorkingSet) {
IWorkingSet workingSet= (IWorkingSet) objects[i];
collectNLSFilesFromJavaElements(workingSet.getElements(), result);
}
}
} catch (JavaModelException e) {
if (!e.isDoesNotExist()) {
JavaPlugin.log(e);
}
}
}
private SearchPatternData tryIfPropertyCuSelected(ICompilationUnit compilationUnit) throws JavaModelException {
if (compilationUnit == null)
return null;
if (!ActionUtil.isOnBuildPath(compilationUnit))
return null;
IType[] types= compilationUnit.getTypes();
if (types.length != 1)
return null;
if (!isPotentialNLSAccessor(compilationUnit))
return null;
IStorage bundle= NLSHintHelper.getResourceBundle(compilationUnit);
if (!(bundle instanceof IFile))
return null;
return new SearchPatternData(types[0], (IFile)bundle);
}
/*
* Be conservative, for every unit this returns true an AST will to be created!
*/
private static boolean isPotentialNLSAccessor(ICompilationUnit unit) throws JavaModelException {
IType type= unit.getTypes()[0];
if (!type.exists())
return false;
IField bundleNameField= getBundleNameField(type.getFields());
if (bundleNameField == null)
return false;
if (importsOSGIUtil(unit)) { //new school
IInitializer[] initializers= type.getInitializers();
for (int i= 0; i < initializers.length; i++) {
if (Modifier.isStatic(initializers[0].getFlags()))
return true;
}
} else { //old school
IMethod[] methods= type.getMethods();
for (int i= 0; i < methods.length; i++) {
IMethod method= methods[i];
if (isValueAccessor(method))
return true;
}
}
return false;
}
private static boolean importsOSGIUtil(ICompilationUnit unit) throws JavaModelException {
IImportDeclaration[] imports= unit.getImports();
for (int i= 0; i < imports.length; i++) {
if (imports[i].getElementName().startsWith("org.eclipse.osgi.util.")) //$NON-NLS-1$
return true;
}
return false;
}
private static boolean isValueAccessor(IMethod method) throws JavaModelException {
if (!"getString".equals(method.getElementName())) //$NON-NLS-1$
return false;
int flags= method.getFlags();
if (!Modifier.isStatic(flags) || !Modifier.isPublic(flags))
return false;
String returnType= method.getReturnType();
if (!JAVA_LANG_STRING.equals(returnType))
return false;
String[] parameters= method.getParameterTypes();
if (parameters.length != 1 || !JAVA_LANG_STRING.equals(parameters[0]))
return false;
return true;
}
private static IField getBundleNameField(IField[] fields) {
for (int i= 0; i < fields.length; i++) {
if ("BUNDLE_NAME".equals(fields[i].getElementName())) //$NON-NLS-1$
return fields[i];
}
return null;
}
private SearchPatternData tryIfPropertyFileSelected(IFile file) {
if (!"properties".equalsIgnoreCase(file.getFileExtension())) //$NON-NLS-1$
return null;
IPath propertyFullPath= file.getFullPath();
// Try to find a corresponding CU
String[] javaExtensions= JavaCore.getJavaLikeExtensions();
for (int i= 0; i < javaExtensions.length; i++) {
String extension= javaExtensions[i];
IPath cuPath= propertyFullPath.removeFileExtension().addFileExtension(extension);
IFile cuFile= (IFile)JavaPlugin.getWorkspace().getRoot().findMember(cuPath);
if (cuFile == null) { //try with uppercase first char
String filename= cuPath.removeFileExtension().lastSegment();
if (filename != null && filename.length() > 0) {
filename= Character.toUpperCase(filename.charAt(0)) + filename.substring(1);
IPath dirPath= propertyFullPath.removeLastSegments(1).addTrailingSeparator();
cuPath= dirPath.append(filename).addFileExtension(extension);
cuFile= (IFile)JavaPlugin.getWorkspace().getRoot().findMember(cuPath);
}
}
if (cuFile != null && cuFile.exists()) {
IJavaElement element= JavaCore.create(cuFile);
if (element != null && element.exists() && element.getElementType() == IJavaElement.COMPILATION_UNIT && ActionUtil.isOnBuildPath(element)) {
ICompilationUnit compilationUnit= (ICompilationUnit)element;
IType type= compilationUnit.findPrimaryType();
if (type != null) {
String resourceBundleName= NLSHintHelper.getResourceBundleName(compilationUnit);
if (resourceBundleName != null) {
String resourceName= resourceBundleName + NLSRefactoring.PROPERTY_FILE_EXT;
String name= file.getName();
if (resourceName.endsWith(name)) {
return new SearchPatternData(type, file);
}
}
}
}
}
}
return null;
}
private static ICompilationUnit getCompilationUnit(JavaEditor editor) {
IWorkingCopyManager manager= JavaPlugin.getDefault().getWorkingCopyManager();
ICompilationUnit cu= manager.getWorkingCopy(editor.getEditorInput());
return cu;
}
}